18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/ipv6/fib6_rules.c IPv6 Routing Policy Rules 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C)2003-2006 Helsinki University of Technology 68c2ecf20Sopenharmony_ci * Copyright (C)2003-2006 USAGI/WIDE Project 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Authors 98c2ecf20Sopenharmony_ci * Thomas Graf <tgraf@suug.ch> 108c2ecf20Sopenharmony_ci * Ville Nuorvala <vnuorval@tcs.hut.fi> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/notifier.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include <linux/indirect_call_wrapper.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <net/fib_rules.h> 198c2ecf20Sopenharmony_ci#include <net/ipv6.h> 208c2ecf20Sopenharmony_ci#include <net/addrconf.h> 218c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 228c2ecf20Sopenharmony_ci#include <net/netlink.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct fib6_rule { 258c2ecf20Sopenharmony_ci struct fib_rule common; 268c2ecf20Sopenharmony_ci struct rt6key src; 278c2ecf20Sopenharmony_ci struct rt6key dst; 288c2ecf20Sopenharmony_ci u8 tclass; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic bool fib6_rule_matchall(const struct fib_rule *rule) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct fib6_rule *r = container_of(rule, struct fib6_rule, common); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (r->dst.plen || r->src.plen || r->tclass) 368c2ecf20Sopenharmony_ci return false; 378c2ecf20Sopenharmony_ci return fib_rule_matchall(rule); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cibool fib6_rule_default(const struct fib_rule *rule) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if (!fib6_rule_matchall(rule) || rule->action != FR_ACT_TO_TBL || 438c2ecf20Sopenharmony_ci rule->l3mdev) 448c2ecf20Sopenharmony_ci return false; 458c2ecf20Sopenharmony_ci if (rule->table != RT6_TABLE_LOCAL && rule->table != RT6_TABLE_MAIN) 468c2ecf20Sopenharmony_ci return false; 478c2ecf20Sopenharmony_ci return true; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_rule_default); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciint fib6_rules_dump(struct net *net, struct notifier_block *nb, 528c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return fib_rules_dump(net, nb, AF_INET6, extack); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciunsigned int fib6_rules_seq_read(struct net *net) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return fib_rules_seq_read(net, AF_INET6); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* called with rcu lock held; no reference taken on fib6_info */ 638c2ecf20Sopenharmony_ciint fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, 648c2ecf20Sopenharmony_ci struct fib6_result *res, int flags) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci int err; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (net->ipv6.fib6_has_custom_rules) { 698c2ecf20Sopenharmony_ci struct fib_lookup_arg arg = { 708c2ecf20Sopenharmony_ci .lookup_ptr = fib6_table_lookup, 718c2ecf20Sopenharmony_ci .lookup_data = &oif, 728c2ecf20Sopenharmony_ci .result = res, 738c2ecf20Sopenharmony_ci .flags = FIB_LOOKUP_NOREF, 748c2ecf20Sopenharmony_ci }; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci l3mdev_update_flow(net, flowi6_to_flowi(fl6)); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci err = fib_rules_lookup(net->ipv6.fib6_rules_ops, 798c2ecf20Sopenharmony_ci flowi6_to_flowi(fl6), flags, &arg); 808c2ecf20Sopenharmony_ci } else { 818c2ecf20Sopenharmony_ci err = fib6_table_lookup(net, net->ipv6.fib6_local_tbl, oif, 828c2ecf20Sopenharmony_ci fl6, res, flags); 838c2ecf20Sopenharmony_ci if (err || res->f6i == net->ipv6.fib6_null_entry) 848c2ecf20Sopenharmony_ci err = fib6_table_lookup(net, net->ipv6.fib6_main_tbl, 858c2ecf20Sopenharmony_ci oif, fl6, res, flags); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return err; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, 928c2ecf20Sopenharmony_ci const struct sk_buff *skb, 938c2ecf20Sopenharmony_ci int flags, pol_lookup_t lookup) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci if (net->ipv6.fib6_has_custom_rules) { 968c2ecf20Sopenharmony_ci struct fib6_result res = {}; 978c2ecf20Sopenharmony_ci struct fib_lookup_arg arg = { 988c2ecf20Sopenharmony_ci .lookup_ptr = lookup, 998c2ecf20Sopenharmony_ci .lookup_data = skb, 1008c2ecf20Sopenharmony_ci .result = &res, 1018c2ecf20Sopenharmony_ci .flags = FIB_LOOKUP_NOREF, 1028c2ecf20Sopenharmony_ci }; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* update flow if oif or iif point to device enslaved to l3mdev */ 1058c2ecf20Sopenharmony_ci l3mdev_update_flow(net, flowi6_to_flowi(fl6)); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci fib_rules_lookup(net->ipv6.fib6_rules_ops, 1088c2ecf20Sopenharmony_ci flowi6_to_flowi(fl6), flags, &arg); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (res.rt6) 1118c2ecf20Sopenharmony_ci return &res.rt6->dst; 1128c2ecf20Sopenharmony_ci } else { 1138c2ecf20Sopenharmony_ci struct rt6_info *rt; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci rt = pol_lookup_func(lookup, 1168c2ecf20Sopenharmony_ci net, net->ipv6.fib6_local_tbl, fl6, skb, flags); 1178c2ecf20Sopenharmony_ci if (rt != net->ipv6.ip6_null_entry && rt->dst.error != -EAGAIN) 1188c2ecf20Sopenharmony_ci return &rt->dst; 1198c2ecf20Sopenharmony_ci ip6_rt_put_flags(rt, flags); 1208c2ecf20Sopenharmony_ci rt = pol_lookup_func(lookup, 1218c2ecf20Sopenharmony_ci net, net->ipv6.fib6_main_tbl, fl6, skb, flags); 1228c2ecf20Sopenharmony_ci if (rt->dst.error != -EAGAIN) 1238c2ecf20Sopenharmony_ci return &rt->dst; 1248c2ecf20Sopenharmony_ci ip6_rt_put_flags(rt, flags); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!(flags & RT6_LOOKUP_F_DST_NOREF)) 1288c2ecf20Sopenharmony_ci dst_hold(&net->ipv6.ip6_null_entry->dst); 1298c2ecf20Sopenharmony_ci return &net->ipv6.ip6_null_entry->dst; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int fib6_rule_saddr(struct net *net, struct fib_rule *rule, int flags, 1338c2ecf20Sopenharmony_ci struct flowi6 *flp6, const struct net_device *dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct fib6_rule *r = (struct fib6_rule *)rule; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* If we need to find a source address for this traffic, 1388c2ecf20Sopenharmony_ci * we check the result if it meets requirement of the rule. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci if ((rule->flags & FIB_RULE_FIND_SADDR) && 1418c2ecf20Sopenharmony_ci r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) { 1428c2ecf20Sopenharmony_ci struct in6_addr saddr; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (ipv6_dev_get_saddr(net, dev, &flp6->daddr, 1458c2ecf20Sopenharmony_ci rt6_flags2srcprefs(flags), &saddr)) 1468c2ecf20Sopenharmony_ci return -EAGAIN; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (!ipv6_prefix_equal(&saddr, &r->src.addr, r->src.plen)) 1498c2ecf20Sopenharmony_ci return -EAGAIN; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci flp6->saddr = saddr; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp, 1588c2ecf20Sopenharmony_ci int flags, struct fib_lookup_arg *arg) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct fib6_result *res = arg->result; 1618c2ecf20Sopenharmony_ci struct flowi6 *flp6 = &flp->u.ip6; 1628c2ecf20Sopenharmony_ci struct net *net = rule->fr_net; 1638c2ecf20Sopenharmony_ci struct fib6_table *table; 1648c2ecf20Sopenharmony_ci int err, *oif; 1658c2ecf20Sopenharmony_ci u32 tb_id; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci switch (rule->action) { 1688c2ecf20Sopenharmony_ci case FR_ACT_TO_TBL: 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case FR_ACT_UNREACHABLE: 1718c2ecf20Sopenharmony_ci return -ENETUNREACH; 1728c2ecf20Sopenharmony_ci case FR_ACT_PROHIBIT: 1738c2ecf20Sopenharmony_ci return -EACCES; 1748c2ecf20Sopenharmony_ci case FR_ACT_BLACKHOLE: 1758c2ecf20Sopenharmony_ci default: 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci tb_id = fib_rule_get_table(rule, arg); 1808c2ecf20Sopenharmony_ci table = fib6_get_table(net, tb_id); 1818c2ecf20Sopenharmony_ci if (!table) 1828c2ecf20Sopenharmony_ci return -EAGAIN; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci oif = (int *)arg->lookup_data; 1858c2ecf20Sopenharmony_ci err = fib6_table_lookup(net, table, *oif, flp6, res, flags); 1868c2ecf20Sopenharmony_ci if (!err && res->f6i != net->ipv6.fib6_null_entry) 1878c2ecf20Sopenharmony_ci err = fib6_rule_saddr(net, rule, flags, flp6, 1888c2ecf20Sopenharmony_ci res->nh->fib_nh_dev); 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci err = -EAGAIN; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return err; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp, 1968c2ecf20Sopenharmony_ci int flags, struct fib_lookup_arg *arg) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct fib6_result *res = arg->result; 1998c2ecf20Sopenharmony_ci struct flowi6 *flp6 = &flp->u.ip6; 2008c2ecf20Sopenharmony_ci struct rt6_info *rt = NULL; 2018c2ecf20Sopenharmony_ci struct fib6_table *table; 2028c2ecf20Sopenharmony_ci struct net *net = rule->fr_net; 2038c2ecf20Sopenharmony_ci pol_lookup_t lookup = arg->lookup_ptr; 2048c2ecf20Sopenharmony_ci int err = 0; 2058c2ecf20Sopenharmony_ci u32 tb_id; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci switch (rule->action) { 2088c2ecf20Sopenharmony_ci case FR_ACT_TO_TBL: 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci case FR_ACT_UNREACHABLE: 2118c2ecf20Sopenharmony_ci err = -ENETUNREACH; 2128c2ecf20Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 2138c2ecf20Sopenharmony_ci goto discard_pkt; 2148c2ecf20Sopenharmony_ci default: 2158c2ecf20Sopenharmony_ci case FR_ACT_BLACKHOLE: 2168c2ecf20Sopenharmony_ci err = -EINVAL; 2178c2ecf20Sopenharmony_ci rt = net->ipv6.ip6_blk_hole_entry; 2188c2ecf20Sopenharmony_ci goto discard_pkt; 2198c2ecf20Sopenharmony_ci case FR_ACT_PROHIBIT: 2208c2ecf20Sopenharmony_ci err = -EACCES; 2218c2ecf20Sopenharmony_ci rt = net->ipv6.ip6_prohibit_entry; 2228c2ecf20Sopenharmony_ci goto discard_pkt; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci tb_id = fib_rule_get_table(rule, arg); 2268c2ecf20Sopenharmony_ci table = fib6_get_table(net, tb_id); 2278c2ecf20Sopenharmony_ci if (!table) { 2288c2ecf20Sopenharmony_ci err = -EAGAIN; 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci rt = pol_lookup_func(lookup, 2338c2ecf20Sopenharmony_ci net, table, flp6, arg->lookup_data, flags); 2348c2ecf20Sopenharmony_ci if (rt != net->ipv6.ip6_null_entry) { 2358c2ecf20Sopenharmony_ci struct inet6_dev *idev = ip6_dst_idev(&rt->dst); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!idev) 2388c2ecf20Sopenharmony_ci goto again; 2398c2ecf20Sopenharmony_ci err = fib6_rule_saddr(net, rule, flags, flp6, 2408c2ecf20Sopenharmony_ci idev->dev); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (err == -EAGAIN) 2438c2ecf20Sopenharmony_ci goto again; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci err = rt->dst.error; 2468c2ecf20Sopenharmony_ci if (err != -EAGAIN) 2478c2ecf20Sopenharmony_ci goto out; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ciagain: 2508c2ecf20Sopenharmony_ci ip6_rt_put_flags(rt, flags); 2518c2ecf20Sopenharmony_ci err = -EAGAIN; 2528c2ecf20Sopenharmony_ci rt = NULL; 2538c2ecf20Sopenharmony_ci goto out; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cidiscard_pkt: 2568c2ecf20Sopenharmony_ci if (!(flags & RT6_LOOKUP_F_DST_NOREF)) 2578c2ecf20Sopenharmony_ci dst_hold(&rt->dst); 2588c2ecf20Sopenharmony_ciout: 2598c2ecf20Sopenharmony_ci res->rt6 = rt; 2608c2ecf20Sopenharmony_ci return err; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE int fib6_rule_action(struct fib_rule *rule, 2648c2ecf20Sopenharmony_ci struct flowi *flp, int flags, 2658c2ecf20Sopenharmony_ci struct fib_lookup_arg *arg) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci if (arg->lookup_ptr == fib6_table_lookup) 2688c2ecf20Sopenharmony_ci return fib6_rule_action_alt(rule, flp, flags, arg); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return __fib6_rule_action(rule, flp, flags, arg); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE bool fib6_rule_suppress(struct fib_rule *rule, 2748c2ecf20Sopenharmony_ci int flags, 2758c2ecf20Sopenharmony_ci struct fib_lookup_arg *arg) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct fib6_result *res = arg->result; 2788c2ecf20Sopenharmony_ci struct rt6_info *rt = res->rt6; 2798c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!rt) 2828c2ecf20Sopenharmony_ci return false; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (rt->rt6i_idev) 2858c2ecf20Sopenharmony_ci dev = rt->rt6i_idev->dev; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* do not accept result if the route does 2888c2ecf20Sopenharmony_ci * not meet the required prefix length 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci if (rt->rt6i_dst.plen <= rule->suppress_prefixlen) 2918c2ecf20Sopenharmony_ci goto suppress_route; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* do not accept result if the route uses a device 2948c2ecf20Sopenharmony_ci * belonging to a forbidden interface group 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_ci if (rule->suppress_ifgroup != -1 && dev && dev->group == rule->suppress_ifgroup) 2978c2ecf20Sopenharmony_ci goto suppress_route; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return false; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cisuppress_route: 3028c2ecf20Sopenharmony_ci ip6_rt_put_flags(rt, flags); 3038c2ecf20Sopenharmony_ci return true; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE int fib6_rule_match(struct fib_rule *rule, 3078c2ecf20Sopenharmony_ci struct flowi *fl, int flags) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct fib6_rule *r = (struct fib6_rule *) rule; 3108c2ecf20Sopenharmony_ci struct flowi6 *fl6 = &fl->u.ip6; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (r->dst.plen && 3138c2ecf20Sopenharmony_ci !ipv6_prefix_equal(&fl6->daddr, &r->dst.addr, r->dst.plen)) 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * If FIB_RULE_FIND_SADDR is set and we do not have a 3188c2ecf20Sopenharmony_ci * source address for the traffic, we defer check for 3198c2ecf20Sopenharmony_ci * source address. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci if (r->src.plen) { 3228c2ecf20Sopenharmony_ci if (flags & RT6_LOOKUP_F_HAS_SADDR) { 3238c2ecf20Sopenharmony_ci if (!ipv6_prefix_equal(&fl6->saddr, &r->src.addr, 3248c2ecf20Sopenharmony_ci r->src.plen)) 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci } else if (!(r->common.flags & FIB_RULE_FIND_SADDR)) 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel)) 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (rule->ip_proto && (rule->ip_proto != fl6->flowi6_proto)) 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (fib_rule_port_range_set(&rule->sport_range) && 3378c2ecf20Sopenharmony_ci !fib_rule_port_inrange(&rule->sport_range, fl6->fl6_sport)) 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (fib_rule_port_range_set(&rule->dport_range) && 3418c2ecf20Sopenharmony_ci !fib_rule_port_inrange(&rule->dport_range, fl6->fl6_dport)) 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 1; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic const struct nla_policy fib6_rule_policy[FRA_MAX+1] = { 3488c2ecf20Sopenharmony_ci FRA_GENERIC_POLICY, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 3528c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh, 3538c2ecf20Sopenharmony_ci struct nlattr **tb, 3548c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci int err = -EINVAL; 3578c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 3588c2ecf20Sopenharmony_ci struct fib6_rule *rule6 = (struct fib6_rule *) rule; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (rule->action == FR_ACT_TO_TBL && !rule->l3mdev) { 3618c2ecf20Sopenharmony_ci if (rule->table == RT6_TABLE_UNSPEC) { 3628c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid table"); 3638c2ecf20Sopenharmony_ci goto errout; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (fib6_new_table(net, rule->table) == NULL) { 3678c2ecf20Sopenharmony_ci err = -ENOBUFS; 3688c2ecf20Sopenharmony_ci goto errout; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (frh->src_len) 3738c2ecf20Sopenharmony_ci rule6->src.addr = nla_get_in6_addr(tb[FRA_SRC]); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (frh->dst_len) 3768c2ecf20Sopenharmony_ci rule6->dst.addr = nla_get_in6_addr(tb[FRA_DST]); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci rule6->src.plen = frh->src_len; 3798c2ecf20Sopenharmony_ci rule6->dst.plen = frh->dst_len; 3808c2ecf20Sopenharmony_ci rule6->tclass = frh->tos; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (fib_rule_requires_fldissect(rule)) 3838c2ecf20Sopenharmony_ci net->ipv6.fib6_rules_require_fldissect++; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci net->ipv6.fib6_has_custom_rules = true; 3868c2ecf20Sopenharmony_ci err = 0; 3878c2ecf20Sopenharmony_cierrout: 3888c2ecf20Sopenharmony_ci return err; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int fib6_rule_delete(struct fib_rule *rule) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct net *net = rule->fr_net; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (net->ipv6.fib6_rules_require_fldissect && 3968c2ecf20Sopenharmony_ci fib_rule_requires_fldissect(rule)) 3978c2ecf20Sopenharmony_ci net->ipv6.fib6_rules_require_fldissect--; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 4038c2ecf20Sopenharmony_ci struct nlattr **tb) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct fib6_rule *rule6 = (struct fib6_rule *) rule; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (frh->src_len && (rule6->src.plen != frh->src_len)) 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (frh->dst_len && (rule6->dst.plen != frh->dst_len)) 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (frh->tos && (rule6->tclass != frh->tos)) 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (frh->src_len && 4178c2ecf20Sopenharmony_ci nla_memcmp(tb[FRA_SRC], &rule6->src.addr, sizeof(struct in6_addr))) 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (frh->dst_len && 4218c2ecf20Sopenharmony_ci nla_memcmp(tb[FRA_DST], &rule6->dst.addr, sizeof(struct in6_addr))) 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 1; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int fib6_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 4288c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct fib6_rule *rule6 = (struct fib6_rule *) rule; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci frh->dst_len = rule6->dst.plen; 4338c2ecf20Sopenharmony_ci frh->src_len = rule6->src.plen; 4348c2ecf20Sopenharmony_ci frh->tos = rule6->tclass; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if ((rule6->dst.plen && 4378c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, FRA_DST, &rule6->dst.addr)) || 4388c2ecf20Sopenharmony_ci (rule6->src.plen && 4398c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, FRA_SRC, &rule6->src.addr))) 4408c2ecf20Sopenharmony_ci goto nla_put_failure; 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cinla_put_failure: 4448c2ecf20Sopenharmony_ci return -ENOBUFS; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic size_t fib6_rule_nlmsg_payload(struct fib_rule *rule) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci return nla_total_size(16) /* dst */ 4508c2ecf20Sopenharmony_ci + nla_total_size(16); /* src */ 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic const struct fib_rules_ops __net_initconst fib6_rules_ops_template = { 4548c2ecf20Sopenharmony_ci .family = AF_INET6, 4558c2ecf20Sopenharmony_ci .rule_size = sizeof(struct fib6_rule), 4568c2ecf20Sopenharmony_ci .addr_size = sizeof(struct in6_addr), 4578c2ecf20Sopenharmony_ci .action = fib6_rule_action, 4588c2ecf20Sopenharmony_ci .match = fib6_rule_match, 4598c2ecf20Sopenharmony_ci .suppress = fib6_rule_suppress, 4608c2ecf20Sopenharmony_ci .configure = fib6_rule_configure, 4618c2ecf20Sopenharmony_ci .delete = fib6_rule_delete, 4628c2ecf20Sopenharmony_ci .compare = fib6_rule_compare, 4638c2ecf20Sopenharmony_ci .fill = fib6_rule_fill, 4648c2ecf20Sopenharmony_ci .nlmsg_payload = fib6_rule_nlmsg_payload, 4658c2ecf20Sopenharmony_ci .nlgroup = RTNLGRP_IPV6_RULE, 4668c2ecf20Sopenharmony_ci .policy = fib6_rule_policy, 4678c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4688c2ecf20Sopenharmony_ci .fro_net = &init_net, 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int __net_init fib6_rules_net_init(struct net *net) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 4748c2ecf20Sopenharmony_ci int err = -ENOMEM; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci ops = fib_rules_register(&fib6_rules_ops_template, net); 4778c2ecf20Sopenharmony_ci if (IS_ERR(ops)) 4788c2ecf20Sopenharmony_ci return PTR_ERR(ops); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci err = fib_default_rule_add(ops, 0, RT6_TABLE_LOCAL, 0); 4818c2ecf20Sopenharmony_ci if (err) 4828c2ecf20Sopenharmony_ci goto out_fib6_rules_ops; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci err = fib_default_rule_add(ops, 0x7FFE, RT6_TABLE_MAIN, 0); 4858c2ecf20Sopenharmony_ci if (err) 4868c2ecf20Sopenharmony_ci goto out_fib6_rules_ops; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci net->ipv6.fib6_rules_ops = ops; 4898c2ecf20Sopenharmony_ci net->ipv6.fib6_rules_require_fldissect = 0; 4908c2ecf20Sopenharmony_ciout: 4918c2ecf20Sopenharmony_ci return err; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciout_fib6_rules_ops: 4948c2ecf20Sopenharmony_ci fib_rules_unregister(ops); 4958c2ecf20Sopenharmony_ci goto out; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void __net_exit fib6_rules_net_exit(struct net *net) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci rtnl_lock(); 5018c2ecf20Sopenharmony_ci fib_rules_unregister(net->ipv6.fib6_rules_ops); 5028c2ecf20Sopenharmony_ci rtnl_unlock(); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic struct pernet_operations fib6_rules_net_ops = { 5068c2ecf20Sopenharmony_ci .init = fib6_rules_net_init, 5078c2ecf20Sopenharmony_ci .exit = fib6_rules_net_exit, 5088c2ecf20Sopenharmony_ci}; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ciint __init fib6_rules_init(void) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci return register_pernet_subsys(&fib6_rules_net_ops); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_civoid fib6_rules_cleanup(void) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci unregister_pernet_subsys(&fib6_rules_net_ops); 5198c2ecf20Sopenharmony_ci} 520