linux/net/ipv6/fib6_rules.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * net/ipv6/fib6_rules.c        IPv6 Routing Policy Rules
   4 *
   5 * Copyright (C)2003-2006 Helsinki University of Technology
   6 * Copyright (C)2003-2006 USAGI/WIDE Project
   7 *
   8 * Authors
   9 *      Thomas Graf             <tgraf@suug.ch>
  10 *      Ville Nuorvala          <vnuorval@tcs.hut.fi>
  11 */
  12
  13#include <linux/netdevice.h>
  14#include <linux/notifier.h>
  15#include <linux/export.h>
  16#include <linux/indirect_call_wrapper.h>
  17
  18#include <net/fib_rules.h>
  19#include <net/inet_dscp.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        dscp_t                  dscp;
  30};
  31
  32static bool fib6_rule_matchall(const struct fib_rule *rule)
  33{
  34        struct fib6_rule *r = container_of(rule, struct fib6_rule, common);
  35
  36        if (r->dst.plen || r->src.plen || r->dscp)
  37                return false;
  38        return fib_rule_matchall(rule);
  39}
  40
  41bool fib6_rule_default(const struct fib_rule *rule)
  42{
  43        if (!fib6_rule_matchall(rule) || rule->action != FR_ACT_TO_TBL ||
  44            rule->l3mdev)
  45                return false;
  46        if (rule->table != RT6_TABLE_LOCAL && rule->table != RT6_TABLE_MAIN)
  47                return false;
  48        return true;
  49}
  50EXPORT_SYMBOL_GPL(fib6_rule_default);
  51
  52int fib6_rules_dump(struct net *net, struct notifier_block *nb,
  53                    struct netlink_ext_ack *extack)
  54{
  55        return fib_rules_dump(net, nb, AF_INET6, extack);
  56}
  57
  58unsigned int fib6_rules_seq_read(struct net *net)
  59{
  60        return fib_rules_seq_read(net, AF_INET6);
  61}
  62
  63/* called with rcu lock held; no reference taken on fib6_info */
  64int fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
  65                struct fib6_result *res, int flags)
  66{
  67        int err;
  68
  69        if (net->ipv6.fib6_has_custom_rules) {
  70                struct fib_lookup_arg arg = {
  71                        .lookup_ptr = fib6_table_lookup,
  72                        .lookup_data = &oif,
  73                        .result = res,
  74                        .flags = FIB_LOOKUP_NOREF,
  75                };
  76
  77                l3mdev_update_flow(net, flowi6_to_flowi(fl6));
  78
  79                err = fib_rules_lookup(net->ipv6.fib6_rules_ops,
  80                                       flowi6_to_flowi(fl6), flags, &arg);
  81        } else {
  82                err = fib6_table_lookup(net, net->ipv6.fib6_local_tbl, oif,
  83                                        fl6, res, flags);
  84                if (err || res->f6i == net->ipv6.fib6_null_entry)
  85                        err = fib6_table_lookup(net, net->ipv6.fib6_main_tbl,
  86                                                oif, fl6, res, flags);
  87        }
  88
  89        return err;
  90}
  91
  92struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
  93                                   const struct sk_buff *skb,
  94                                   int flags, pol_lookup_t lookup)
  95{
  96        if (net->ipv6.fib6_has_custom_rules) {
  97                struct fib6_result res = {};
  98                struct fib_lookup_arg arg = {
  99                        .lookup_ptr = lookup,
 100                        .lookup_data = skb,
 101                        .result = &res,
 102                        .flags = FIB_LOOKUP_NOREF,
 103                };
 104
 105                /* update flow if oif or iif point to device enslaved to l3mdev */
 106                l3mdev_update_flow(net, flowi6_to_flowi(fl6));
 107
 108                fib_rules_lookup(net->ipv6.fib6_rules_ops,
 109                                 flowi6_to_flowi(fl6), flags, &arg);
 110
 111                if (res.rt6)
 112                        return &res.rt6->dst;
 113        } else {
 114                struct rt6_info *rt;
 115
 116                rt = pol_lookup_func(lookup,
 117                             net, net->ipv6.fib6_local_tbl, fl6, skb, flags);
 118                if (rt != net->ipv6.ip6_null_entry && rt->dst.error != -EAGAIN)
 119                        return &rt->dst;
 120                ip6_rt_put_flags(rt, flags);
 121                rt = pol_lookup_func(lookup,
 122                             net, net->ipv6.fib6_main_tbl, fl6, skb, flags);
 123                if (rt->dst.error != -EAGAIN)
 124                        return &rt->dst;
 125                ip6_rt_put_flags(rt, flags);
 126        }
 127
 128        if (!(flags & RT6_LOOKUP_F_DST_NOREF))
 129                dst_hold(&net->ipv6.ip6_null_entry->dst);
 130        return &net->ipv6.ip6_null_entry->dst;
 131}
 132
 133static int fib6_rule_saddr(struct net *net, struct fib_rule *rule, int flags,
 134                           struct flowi6 *flp6, const struct net_device *dev)
 135{
 136        struct fib6_rule *r = (struct fib6_rule *)rule;
 137
 138        /* If we need to find a source address for this traffic,
 139         * we check the result if it meets requirement of the rule.
 140         */
 141        if ((rule->flags & FIB_RULE_FIND_SADDR) &&
 142            r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) {
 143                struct in6_addr saddr;
 144
 145                if (ipv6_dev_get_saddr(net, dev, &flp6->daddr,
 146                                       rt6_flags2srcprefs(flags), &saddr))
 147                        return -EAGAIN;
 148
 149                if (!ipv6_prefix_equal(&saddr, &r->src.addr, r->src.plen))
 150                        return -EAGAIN;
 151
 152                flp6->saddr = saddr;
 153        }
 154
 155        return 0;
 156}
 157
 158static int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp,
 159                                int flags, struct fib_lookup_arg *arg)
 160{
 161        struct fib6_result *res = arg->result;
 162        struct flowi6 *flp6 = &flp->u.ip6;
 163        struct net *net = rule->fr_net;
 164        struct fib6_table *table;
 165        int err, *oif;
 166        u32 tb_id;
 167
 168        switch (rule->action) {
 169        case FR_ACT_TO_TBL:
 170                break;
 171        case FR_ACT_UNREACHABLE:
 172                return -ENETUNREACH;
 173        case FR_ACT_PROHIBIT:
 174                return -EACCES;
 175        case FR_ACT_BLACKHOLE:
 176        default:
 177                return -EINVAL;
 178        }
 179
 180        tb_id = fib_rule_get_table(rule, arg);
 181        table = fib6_get_table(net, tb_id);
 182        if (!table)
 183                return -EAGAIN;
 184
 185        oif = (int *)arg->lookup_data;
 186        err = fib6_table_lookup(net, table, *oif, flp6, res, flags);
 187        if (!err && res->f6i != net->ipv6.fib6_null_entry)
 188                err = fib6_rule_saddr(net, rule, flags, flp6,
 189                                      res->nh->fib_nh_dev);
 190        else
 191                err = -EAGAIN;
 192
 193        return err;
 194}
 195
 196static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
 197                              int flags, struct fib_lookup_arg *arg)
 198{
 199        struct fib6_result *res = arg->result;
 200        struct flowi6 *flp6 = &flp->u.ip6;
 201        struct rt6_info *rt = NULL;
 202        struct fib6_table *table;
 203        struct net *net = rule->fr_net;
 204        pol_lookup_t lookup = arg->lookup_ptr;
 205        int err = 0;
 206        u32 tb_id;
 207
 208        switch (rule->action) {
 209        case FR_ACT_TO_TBL:
 210                break;
 211        case FR_ACT_UNREACHABLE:
 212                err = -ENETUNREACH;
 213                rt = net->ipv6.ip6_null_entry;
 214                goto discard_pkt;
 215        default:
 216        case FR_ACT_BLACKHOLE:
 217                err = -EINVAL;
 218                rt = net->ipv6.ip6_blk_hole_entry;
 219                goto discard_pkt;
 220        case FR_ACT_PROHIBIT:
 221                err = -EACCES;
 222                rt = net->ipv6.ip6_prohibit_entry;
 223                goto discard_pkt;
 224        }
 225
 226        tb_id = fib_rule_get_table(rule, arg);
 227        table = fib6_get_table(net, tb_id);
 228        if (!table) {
 229                err = -EAGAIN;
 230                goto out;
 231        }
 232
 233        rt = pol_lookup_func(lookup,
 234                             net, table, flp6, arg->lookup_data, flags);
 235        if (rt != net->ipv6.ip6_null_entry) {
 236                err = fib6_rule_saddr(net, rule, flags, flp6,
 237                                      ip6_dst_idev(&rt->dst)->dev);
 238
 239                if (err == -EAGAIN)
 240                        goto again;
 241
 242                err = rt->dst.error;
 243                if (err != -EAGAIN)
 244                        goto out;
 245        }
 246again:
 247        ip6_rt_put_flags(rt, flags);
 248        err = -EAGAIN;
 249        rt = NULL;
 250        goto out;
 251
 252discard_pkt:
 253        if (!(flags & RT6_LOOKUP_F_DST_NOREF))
 254                dst_hold(&rt->dst);
 255out:
 256        res->rt6 = rt;
 257        return err;
 258}
 259
 260INDIRECT_CALLABLE_SCOPE int fib6_rule_action(struct fib_rule *rule,
 261                                             struct flowi *flp, int flags,
 262                                             struct fib_lookup_arg *arg)
 263{
 264        if (arg->lookup_ptr == fib6_table_lookup)
 265                return fib6_rule_action_alt(rule, flp, flags, arg);
 266
 267        return __fib6_rule_action(rule, flp, flags, arg);
 268}
 269
 270INDIRECT_CALLABLE_SCOPE bool fib6_rule_suppress(struct fib_rule *rule,
 271                                                int flags,
 272                                                struct fib_lookup_arg *arg)
 273{
 274        struct fib6_result *res = arg->result;
 275        struct rt6_info *rt = res->rt6;
 276        struct net_device *dev = NULL;
 277
 278        if (!rt)
 279                return false;
 280
 281        if (rt->rt6i_idev)
 282                dev = rt->rt6i_idev->dev;
 283
 284        /* do not accept result if the route does
 285         * not meet the required prefix length
 286         */
 287        if (rt->rt6i_dst.plen <= rule->suppress_prefixlen)
 288                goto suppress_route;
 289
 290        /* do not accept result if the route uses a device
 291         * belonging to a forbidden interface group
 292         */
 293        if (rule->suppress_ifgroup != -1 && dev && dev->group == rule->suppress_ifgroup)
 294                goto suppress_route;
 295
 296        return false;
 297
 298suppress_route:
 299        ip6_rt_put_flags(rt, flags);
 300        return true;
 301}
 302
 303INDIRECT_CALLABLE_SCOPE int fib6_rule_match(struct fib_rule *rule,
 304                                            struct flowi *fl, int flags)
 305{
 306        struct fib6_rule *r = (struct fib6_rule *) rule;
 307        struct flowi6 *fl6 = &fl->u.ip6;
 308
 309        if (r->dst.plen &&
 310            !ipv6_prefix_equal(&fl6->daddr, &r->dst.addr, r->dst.plen))
 311                return 0;
 312
 313        /*
 314         * If FIB_RULE_FIND_SADDR is set and we do not have a
 315         * source address for the traffic, we defer check for
 316         * source address.
 317         */
 318        if (r->src.plen) {
 319                if (flags & RT6_LOOKUP_F_HAS_SADDR) {
 320                        if (!ipv6_prefix_equal(&fl6->saddr, &r->src.addr,
 321                                               r->src.plen))
 322                                return 0;
 323                } else if (!(r->common.flags & FIB_RULE_FIND_SADDR))
 324                        return 0;
 325        }
 326
 327        if (r->dscp && r->dscp != ip6_dscp(fl6->flowlabel))
 328                return 0;
 329
 330        if (rule->ip_proto && (rule->ip_proto != fl6->flowi6_proto))
 331                return 0;
 332
 333        if (fib_rule_port_range_set(&rule->sport_range) &&
 334            !fib_rule_port_inrange(&rule->sport_range, fl6->fl6_sport))
 335                return 0;
 336
 337        if (fib_rule_port_range_set(&rule->dport_range) &&
 338            !fib_rule_port_inrange(&rule->dport_range, fl6->fl6_dport))
 339                return 0;
 340
 341        return 1;
 342}
 343
 344static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 345                               struct fib_rule_hdr *frh,
 346                               struct nlattr **tb,
 347                               struct netlink_ext_ack *extack)
 348{
 349        int err = -EINVAL;
 350        struct net *net = sock_net(skb->sk);
 351        struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 352
 353        if (!inet_validate_dscp(frh->tos)) {
 354                NL_SET_ERR_MSG(extack,
 355                               "Invalid dsfield (tos): ECN bits must be 0");
 356                goto errout;
 357        }
 358        rule6->dscp = inet_dsfield_to_dscp(frh->tos);
 359
 360        if (rule->action == FR_ACT_TO_TBL && !rule->l3mdev) {
 361                if (rule->table == RT6_TABLE_UNSPEC) {
 362                        NL_SET_ERR_MSG(extack, "Invalid table");
 363                        goto errout;
 364                }
 365
 366                if (fib6_new_table(net, rule->table) == NULL) {
 367                        err = -ENOBUFS;
 368                        goto errout;
 369                }
 370        }
 371
 372        if (frh->src_len)
 373                rule6->src.addr = nla_get_in6_addr(tb[FRA_SRC]);
 374
 375        if (frh->dst_len)
 376                rule6->dst.addr = nla_get_in6_addr(tb[FRA_DST]);
 377
 378        rule6->src.plen = frh->src_len;
 379        rule6->dst.plen = frh->dst_len;
 380
 381        if (fib_rule_requires_fldissect(rule))
 382                net->ipv6.fib6_rules_require_fldissect++;
 383
 384        net->ipv6.fib6_has_custom_rules = true;
 385        err = 0;
 386errout:
 387        return err;
 388}
 389
 390static int fib6_rule_delete(struct fib_rule *rule)
 391{
 392        struct net *net = rule->fr_net;
 393
 394        if (net->ipv6.fib6_rules_require_fldissect &&
 395            fib_rule_requires_fldissect(rule))
 396                net->ipv6.fib6_rules_require_fldissect--;
 397
 398        return 0;
 399}
 400
 401static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
 402                             struct nlattr **tb)
 403{
 404        struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 405
 406        if (frh->src_len && (rule6->src.plen != frh->src_len))
 407                return 0;
 408
 409        if (frh->dst_len && (rule6->dst.plen != frh->dst_len))
 410                return 0;
 411
 412        if (frh->tos && inet_dscp_to_dsfield(rule6->dscp) != frh->tos)
 413                return 0;
 414
 415        if (frh->src_len &&
 416            nla_memcmp(tb[FRA_SRC], &rule6->src.addr, sizeof(struct in6_addr)))
 417                return 0;
 418
 419        if (frh->dst_len &&
 420            nla_memcmp(tb[FRA_DST], &rule6->dst.addr, sizeof(struct in6_addr)))
 421                return 0;
 422
 423        return 1;
 424}
 425
 426static int fib6_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
 427                          struct fib_rule_hdr *frh)
 428{
 429        struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 430
 431        frh->dst_len = rule6->dst.plen;
 432        frh->src_len = rule6->src.plen;
 433        frh->tos = inet_dscp_to_dsfield(rule6->dscp);
 434
 435        if ((rule6->dst.plen &&
 436             nla_put_in6_addr(skb, FRA_DST, &rule6->dst.addr)) ||
 437            (rule6->src.plen &&
 438             nla_put_in6_addr(skb, FRA_SRC, &rule6->src.addr)))
 439                goto nla_put_failure;
 440        return 0;
 441
 442nla_put_failure:
 443        return -ENOBUFS;
 444}
 445
 446static size_t fib6_rule_nlmsg_payload(struct fib_rule *rule)
 447{
 448        return nla_total_size(16) /* dst */
 449               + nla_total_size(16); /* src */
 450}
 451
 452static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = {
 453        .family                 = AF_INET6,
 454        .rule_size              = sizeof(struct fib6_rule),
 455        .addr_size              = sizeof(struct in6_addr),
 456        .action                 = fib6_rule_action,
 457        .match                  = fib6_rule_match,
 458        .suppress               = fib6_rule_suppress,
 459        .configure              = fib6_rule_configure,
 460        .delete                 = fib6_rule_delete,
 461        .compare                = fib6_rule_compare,
 462        .fill                   = fib6_rule_fill,
 463        .nlmsg_payload          = fib6_rule_nlmsg_payload,
 464        .nlgroup                = RTNLGRP_IPV6_RULE,
 465        .owner                  = THIS_MODULE,
 466        .fro_net                = &init_net,
 467};
 468
 469static int __net_init fib6_rules_net_init(struct net *net)
 470{
 471        struct fib_rules_ops *ops;
 472        int err;
 473
 474        ops = fib_rules_register(&fib6_rules_ops_template, net);
 475        if (IS_ERR(ops))
 476                return PTR_ERR(ops);
 477
 478        err = fib_default_rule_add(ops, 0, RT6_TABLE_LOCAL, 0);
 479        if (err)
 480                goto out_fib6_rules_ops;
 481
 482        err = fib_default_rule_add(ops, 0x7FFE, RT6_TABLE_MAIN, 0);
 483        if (err)
 484                goto out_fib6_rules_ops;
 485
 486        net->ipv6.fib6_rules_ops = ops;
 487        net->ipv6.fib6_rules_require_fldissect = 0;
 488out:
 489        return err;
 490
 491out_fib6_rules_ops:
 492        fib_rules_unregister(ops);
 493        goto out;
 494}
 495
 496static void __net_exit fib6_rules_net_exit_batch(struct list_head *net_list)
 497{
 498        struct net *net;
 499
 500        rtnl_lock();
 501        list_for_each_entry(net, net_list, exit_list) {
 502                fib_rules_unregister(net->ipv6.fib6_rules_ops);
 503                cond_resched();
 504        }
 505        rtnl_unlock();
 506}
 507
 508static struct pernet_operations fib6_rules_net_ops = {
 509        .init = fib6_rules_net_init,
 510        .exit_batch = fib6_rules_net_exit_batch,
 511};
 512
 513int __init fib6_rules_init(void)
 514{
 515        return register_pernet_subsys(&fib6_rules_net_ops);
 516}
 517
 518
 519void fib6_rules_cleanup(void)
 520{
 521        unregister_pernet_subsys(&fib6_rules_net_ops);
 522}
 523