linux/net/ipv6/fib6_rules.c
<<
>>
Prefs
   1/*
   2 * net/ipv6/fib6_rules.c        IPv6 Routing Policy Rules
   3 *
   4 * Copyright (C)2003-2006 Helsinki University of Technology
   5 * Copyright (C)2003-2006 USAGI/WIDE Project
   6 *
   7 *      This program is free software; you can redistribute it and/or
   8 *      modify it under the terms of the GNU General Public License as
   9 *      published by the Free Software Foundation, version 2.
  10 *
  11 * Authors
  12 *      Thomas Graf             <tgraf@suug.ch>
  13 *      Ville Nuorvala          <vnuorval@tcs.hut.fi>
  14 */
  15
  16#include <linux/netdevice.h>
  17#include <linux/export.h>
  18
  19#include <net/fib_rules.h>
  20#include <net/ipv6.h>
  21#include <net/addrconf.h>
  22#include <net/ip6_route.h>
  23#include <net/netlink.h>
  24
  25struct fib6_rule {
  26        struct fib_rule         common;
  27        struct rt6key           src;
  28        struct rt6key           dst;
  29        u8                      tclass;
  30};
  31
  32struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
  33                                   int flags, pol_lookup_t lookup)
  34{
  35        struct rt6_info *rt;
  36        struct fib_lookup_arg arg = {
  37                .lookup_ptr = lookup,
  38                .flags = FIB_LOOKUP_NOREF,
  39        };
  40
  41        /* update flow if oif or iif point to device enslaved to l3mdev */
  42        l3mdev_update_flow(net, flowi6_to_flowi(fl6));
  43
  44        fib_rules_lookup(net->ipv6.fib6_rules_ops,
  45                         flowi6_to_flowi(fl6), flags, &arg);
  46
  47        rt = arg.result;
  48
  49        if (!rt) {
  50                dst_hold(&net->ipv6.ip6_null_entry->dst);
  51                return &net->ipv6.ip6_null_entry->dst;
  52        }
  53
  54        if (rt->rt6i_flags & RTF_REJECT &&
  55            rt->dst.error == -EAGAIN) {
  56                ip6_rt_put(rt);
  57                rt = net->ipv6.ip6_null_entry;
  58                dst_hold(&rt->dst);
  59        }
  60
  61        return &rt->dst;
  62}
  63
  64static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
  65                            int flags, struct fib_lookup_arg *arg)
  66{
  67        struct flowi6 *flp6 = &flp->u.ip6;
  68        struct rt6_info *rt = NULL;
  69        struct fib6_table *table;
  70        struct net *net = rule->fr_net;
  71        pol_lookup_t lookup = arg->lookup_ptr;
  72        int err = 0;
  73        u32 tb_id;
  74
  75        switch (rule->action) {
  76        case FR_ACT_TO_TBL:
  77                break;
  78        case FR_ACT_UNREACHABLE:
  79                err = -ENETUNREACH;
  80                rt = net->ipv6.ip6_null_entry;
  81                goto discard_pkt;
  82        default:
  83        case FR_ACT_BLACKHOLE:
  84                err = -EINVAL;
  85                rt = net->ipv6.ip6_blk_hole_entry;
  86                goto discard_pkt;
  87        case FR_ACT_PROHIBIT:
  88                err = -EACCES;
  89                rt = net->ipv6.ip6_prohibit_entry;
  90                goto discard_pkt;
  91        }
  92
  93        tb_id = fib_rule_get_table(rule, arg);
  94        table = fib6_get_table(net, tb_id);
  95        if (!table) {
  96                err = -EAGAIN;
  97                goto out;
  98        }
  99
 100        rt = lookup(net, table, flp6, flags);
 101        if (rt != net->ipv6.ip6_null_entry) {
 102                struct fib6_rule *r = (struct fib6_rule *)rule;
 103
 104                /*
 105                 * If we need to find a source address for this traffic,
 106                 * we check the result if it meets requirement of the rule.
 107                 */
 108                if ((rule->flags & FIB_RULE_FIND_SADDR) &&
 109                    r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) {
 110                        struct in6_addr saddr;
 111
 112                        if (ipv6_dev_get_saddr(net,
 113                                               ip6_dst_idev(&rt->dst)->dev,
 114                                               &flp6->daddr,
 115                                               rt6_flags2srcprefs(flags),
 116                                               &saddr))
 117                                goto again;
 118                        if (!ipv6_prefix_equal(&saddr, &r->src.addr,
 119                                               r->src.plen))
 120                                goto again;
 121                        flp6->saddr = saddr;
 122                }
 123                err = rt->dst.error;
 124                goto out;
 125        }
 126again:
 127        ip6_rt_put(rt);
 128        err = -EAGAIN;
 129        rt = NULL;
 130        goto out;
 131
 132discard_pkt:
 133        dst_hold(&rt->dst);
 134out:
 135        arg->result = rt;
 136        return err;
 137}
 138
 139static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg)
 140{
 141        struct rt6_info *rt = (struct rt6_info *) arg->result;
 142        struct net_device *dev = NULL;
 143
 144        if (rt->rt6i_idev)
 145                dev = rt->rt6i_idev->dev;
 146
 147        /* do not accept result if the route does
 148         * not meet the required prefix length
 149         */
 150        if (rt->rt6i_dst.plen <= rule->suppress_prefixlen)
 151                goto suppress_route;
 152
 153        /* do not accept result if the route uses a device
 154         * belonging to a forbidden interface group
 155         */
 156        if (rule->suppress_ifgroup != -1 && dev && dev->group == rule->suppress_ifgroup)
 157                goto suppress_route;
 158
 159        return false;
 160
 161suppress_route:
 162        ip6_rt_put(rt);
 163        return true;
 164}
 165
 166static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
 167{
 168        struct fib6_rule *r = (struct fib6_rule *) rule;
 169        struct flowi6 *fl6 = &fl->u.ip6;
 170
 171        if (r->dst.plen &&
 172            !ipv6_prefix_equal(&fl6->daddr, &r->dst.addr, r->dst.plen))
 173                return 0;
 174
 175        /*
 176         * If FIB_RULE_FIND_SADDR is set and we do not have a
 177         * source address for the traffic, we defer check for
 178         * source address.
 179         */
 180        if (r->src.plen) {
 181                if (flags & RT6_LOOKUP_F_HAS_SADDR) {
 182                        if (!ipv6_prefix_equal(&fl6->saddr, &r->src.addr,
 183                                               r->src.plen))
 184                                return 0;
 185                } else if (!(r->common.flags & FIB_RULE_FIND_SADDR))
 186                        return 0;
 187        }
 188
 189        if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel))
 190                return 0;
 191
 192        return 1;
 193}
 194
 195static const struct nla_policy fib6_rule_policy[FRA_MAX+1] = {
 196        FRA_GENERIC_POLICY,
 197};
 198
 199static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 200                               struct fib_rule_hdr *frh,
 201                               struct nlattr **tb)
 202{
 203        int err = -EINVAL;
 204        struct net *net = sock_net(skb->sk);
 205        struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 206
 207        if (rule->action == FR_ACT_TO_TBL && !rule->l3mdev) {
 208                if (rule->table == RT6_TABLE_UNSPEC)
 209                        goto errout;
 210
 211                if (fib6_new_table(net, rule->table) == NULL) {
 212                        err = -ENOBUFS;
 213                        goto errout;
 214                }
 215        }
 216
 217        if (frh->src_len)
 218                rule6->src.addr = nla_get_in6_addr(tb[FRA_SRC]);
 219
 220        if (frh->dst_len)
 221                rule6->dst.addr = nla_get_in6_addr(tb[FRA_DST]);
 222
 223        rule6->src.plen = frh->src_len;
 224        rule6->dst.plen = frh->dst_len;
 225        rule6->tclass = frh->tos;
 226
 227        err = 0;
 228errout:
 229        return err;
 230}
 231
 232static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
 233                             struct nlattr **tb)
 234{
 235        struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 236
 237        if (frh->src_len && (rule6->src.plen != frh->src_len))
 238                return 0;
 239
 240        if (frh->dst_len && (rule6->dst.plen != frh->dst_len))
 241                return 0;
 242
 243        if (frh->tos && (rule6->tclass != frh->tos))
 244                return 0;
 245
 246        if (frh->src_len &&
 247            nla_memcmp(tb[FRA_SRC], &rule6->src.addr, sizeof(struct in6_addr)))
 248                return 0;
 249
 250        if (frh->dst_len &&
 251            nla_memcmp(tb[FRA_DST], &rule6->dst.addr, sizeof(struct in6_addr)))
 252                return 0;
 253
 254        return 1;
 255}
 256
 257static int fib6_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
 258                          struct fib_rule_hdr *frh)
 259{
 260        struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 261
 262        frh->dst_len = rule6->dst.plen;
 263        frh->src_len = rule6->src.plen;
 264        frh->tos = rule6->tclass;
 265
 266        if ((rule6->dst.plen &&
 267             nla_put_in6_addr(skb, FRA_DST, &rule6->dst.addr)) ||
 268            (rule6->src.plen &&
 269             nla_put_in6_addr(skb, FRA_SRC, &rule6->src.addr)))
 270                goto nla_put_failure;
 271        return 0;
 272
 273nla_put_failure:
 274        return -ENOBUFS;
 275}
 276
 277static size_t fib6_rule_nlmsg_payload(struct fib_rule *rule)
 278{
 279        return nla_total_size(16) /* dst */
 280               + nla_total_size(16); /* src */
 281}
 282
 283static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = {
 284        .family                 = AF_INET6,
 285        .rule_size              = sizeof(struct fib6_rule),
 286        .addr_size              = sizeof(struct in6_addr),
 287        .action                 = fib6_rule_action,
 288        .match                  = fib6_rule_match,
 289        .suppress               = fib6_rule_suppress,
 290        .configure              = fib6_rule_configure,
 291        .compare                = fib6_rule_compare,
 292        .fill                   = fib6_rule_fill,
 293        .nlmsg_payload          = fib6_rule_nlmsg_payload,
 294        .nlgroup                = RTNLGRP_IPV6_RULE,
 295        .policy                 = fib6_rule_policy,
 296        .owner                  = THIS_MODULE,
 297        .fro_net                = &init_net,
 298};
 299
 300static int __net_init fib6_rules_net_init(struct net *net)
 301{
 302        struct fib_rules_ops *ops;
 303        int err = -ENOMEM;
 304
 305        ops = fib_rules_register(&fib6_rules_ops_template, net);
 306        if (IS_ERR(ops))
 307                return PTR_ERR(ops);
 308
 309        err = fib_default_rule_add(ops, 0, RT6_TABLE_LOCAL, 0);
 310        if (err)
 311                goto out_fib6_rules_ops;
 312
 313        err = fib_default_rule_add(ops, 0x7FFE, RT6_TABLE_MAIN, 0);
 314        if (err)
 315                goto out_fib6_rules_ops;
 316
 317        net->ipv6.fib6_rules_ops = ops;
 318out:
 319        return err;
 320
 321out_fib6_rules_ops:
 322        fib_rules_unregister(ops);
 323        goto out;
 324}
 325
 326static void __net_exit fib6_rules_net_exit(struct net *net)
 327{
 328        rtnl_lock();
 329        fib_rules_unregister(net->ipv6.fib6_rules_ops);
 330        rtnl_unlock();
 331}
 332
 333static struct pernet_operations fib6_rules_net_ops = {
 334        .init = fib6_rules_net_init,
 335        .exit = fib6_rules_net_exit,
 336};
 337
 338int __init fib6_rules_init(void)
 339{
 340        return register_pernet_subsys(&fib6_rules_net_ops);
 341}
 342
 343
 344void fib6_rules_cleanup(void)
 345{
 346        unregister_pernet_subsys(&fib6_rules_net_ops);
 347}
 348