18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/core/fib_rules.c Generic Routing Rules 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Thomas Graf <tgraf@suug.ch> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 148c2ecf20Sopenharmony_ci#include <net/sock.h> 158c2ecf20Sopenharmony_ci#include <net/fib_rules.h> 168c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h> 178c2ecf20Sopenharmony_ci#include <linux/indirect_call_wrapper.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#if defined(CONFIG_IPV6) && defined(CONFIG_IPV6_MULTIPLE_TABLES) 208c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTIPLE_TABLES 218c2ecf20Sopenharmony_ci#define INDIRECT_CALL_MT(f, f2, f1, ...) \ 228c2ecf20Sopenharmony_ci INDIRECT_CALL_INET(f, f2, f1, __VA_ARGS__) 238c2ecf20Sopenharmony_ci#else 248c2ecf20Sopenharmony_ci#define INDIRECT_CALL_MT(f, f2, f1, ...) INDIRECT_CALL_1(f, f2, __VA_ARGS__) 258c2ecf20Sopenharmony_ci#endif 268c2ecf20Sopenharmony_ci#elif defined(CONFIG_IP_MULTIPLE_TABLES) 278c2ecf20Sopenharmony_ci#define INDIRECT_CALL_MT(f, f2, f1, ...) INDIRECT_CALL_1(f, f1, __VA_ARGS__) 288c2ecf20Sopenharmony_ci#else 298c2ecf20Sopenharmony_ci#define INDIRECT_CALL_MT(f, f2, f1, ...) f(__VA_ARGS__) 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct fib_kuid_range fib_kuid_range_unset = { 338c2ecf20Sopenharmony_ci KUIDT_INIT(0), 348c2ecf20Sopenharmony_ci KUIDT_INIT(~0), 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cibool fib_rule_matchall(const struct fib_rule *rule) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci if (rule->iifindex || rule->oifindex || rule->mark || rule->tun_id || 408c2ecf20Sopenharmony_ci rule->flags) 418c2ecf20Sopenharmony_ci return false; 428c2ecf20Sopenharmony_ci if (rule->suppress_ifgroup != -1 || rule->suppress_prefixlen != -1) 438c2ecf20Sopenharmony_ci return false; 448c2ecf20Sopenharmony_ci if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || 458c2ecf20Sopenharmony_ci !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) 468c2ecf20Sopenharmony_ci return false; 478c2ecf20Sopenharmony_ci if (fib_rule_port_range_set(&rule->sport_range)) 488c2ecf20Sopenharmony_ci return false; 498c2ecf20Sopenharmony_ci if (fib_rule_port_range_set(&rule->dport_range)) 508c2ecf20Sopenharmony_ci return false; 518c2ecf20Sopenharmony_ci return true; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_rule_matchall); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciint fib_default_rule_add(struct fib_rules_ops *ops, 568c2ecf20Sopenharmony_ci u32 pref, u32 table, u32 flags) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct fib_rule *r; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci r = kzalloc(ops->rule_size, GFP_KERNEL); 618c2ecf20Sopenharmony_ci if (r == NULL) 628c2ecf20Sopenharmony_ci return -ENOMEM; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci refcount_set(&r->refcnt, 1); 658c2ecf20Sopenharmony_ci r->action = FR_ACT_TO_TBL; 668c2ecf20Sopenharmony_ci r->pref = pref; 678c2ecf20Sopenharmony_ci r->table = table; 688c2ecf20Sopenharmony_ci r->flags = flags; 698c2ecf20Sopenharmony_ci r->proto = RTPROT_KERNEL; 708c2ecf20Sopenharmony_ci r->fr_net = ops->fro_net; 718c2ecf20Sopenharmony_ci r->uid_range = fib_kuid_range_unset; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci r->suppress_prefixlen = -1; 748c2ecf20Sopenharmony_ci r->suppress_ifgroup = -1; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* The lock is not required here, the list in unreacheable 778c2ecf20Sopenharmony_ci * at the moment this function is called */ 788c2ecf20Sopenharmony_ci list_add_tail(&r->list, &ops->rules_list); 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fib_default_rule_add); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic u32 fib_default_rule_pref(struct fib_rules_ops *ops) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct list_head *pos; 868c2ecf20Sopenharmony_ci struct fib_rule *rule; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!list_empty(&ops->rules_list)) { 898c2ecf20Sopenharmony_ci pos = ops->rules_list.next; 908c2ecf20Sopenharmony_ci if (pos->next != &ops->rules_list) { 918c2ecf20Sopenharmony_ci rule = list_entry(pos->next, struct fib_rule, list); 928c2ecf20Sopenharmony_ci if (rule->pref) 938c2ecf20Sopenharmony_ci return rule->pref - 1; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void notify_rule_change(int event, struct fib_rule *rule, 1018c2ecf20Sopenharmony_ci struct fib_rules_ops *ops, struct nlmsghdr *nlh, 1028c2ecf20Sopenharmony_ci u32 pid); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct fib_rules_ops *lookup_rules_ops(struct net *net, int family) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci rcu_read_lock(); 1098c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ops, &net->rules_ops, list) { 1108c2ecf20Sopenharmony_ci if (ops->family == family) { 1118c2ecf20Sopenharmony_ci if (!try_module_get(ops->owner)) 1128c2ecf20Sopenharmony_ci ops = NULL; 1138c2ecf20Sopenharmony_ci rcu_read_unlock(); 1148c2ecf20Sopenharmony_ci return ops; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci rcu_read_unlock(); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return NULL; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void rules_ops_put(struct fib_rules_ops *ops) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci if (ops) 1258c2ecf20Sopenharmony_ci module_put(ops->owner); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void flush_route_cache(struct fib_rules_ops *ops) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (ops->flush_cache) 1318c2ecf20Sopenharmony_ci ops->flush_cache(ops); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int __fib_rules_register(struct fib_rules_ops *ops) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int err = -EEXIST; 1378c2ecf20Sopenharmony_ci struct fib_rules_ops *o; 1388c2ecf20Sopenharmony_ci struct net *net; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci net = ops->fro_net; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (ops->rule_size < sizeof(struct fib_rule)) 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (ops->match == NULL || ops->configure == NULL || 1468c2ecf20Sopenharmony_ci ops->compare == NULL || ops->fill == NULL || 1478c2ecf20Sopenharmony_ci ops->action == NULL) 1488c2ecf20Sopenharmony_ci return -EINVAL; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spin_lock(&net->rules_mod_lock); 1518c2ecf20Sopenharmony_ci list_for_each_entry(o, &net->rules_ops, list) 1528c2ecf20Sopenharmony_ci if (ops->family == o->family) 1538c2ecf20Sopenharmony_ci goto errout; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci list_add_tail_rcu(&ops->list, &net->rules_ops); 1568c2ecf20Sopenharmony_ci err = 0; 1578c2ecf20Sopenharmony_cierrout: 1588c2ecf20Sopenharmony_ci spin_unlock(&net->rules_mod_lock); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return err; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistruct fib_rules_ops * 1648c2ecf20Sopenharmony_cifib_rules_register(const struct fib_rules_ops *tmpl, struct net *net) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 1678c2ecf20Sopenharmony_ci int err; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL); 1708c2ecf20Sopenharmony_ci if (ops == NULL) 1718c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ops->rules_list); 1748c2ecf20Sopenharmony_ci ops->fro_net = net; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci err = __fib_rules_register(ops); 1778c2ecf20Sopenharmony_ci if (err) { 1788c2ecf20Sopenharmony_ci kfree(ops); 1798c2ecf20Sopenharmony_ci ops = ERR_PTR(err); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ops; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_rules_register); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void fib_rules_cleanup_ops(struct fib_rules_ops *ops) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct fib_rule *rule, *tmp; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci list_for_each_entry_safe(rule, tmp, &ops->rules_list, list) { 1918c2ecf20Sopenharmony_ci list_del_rcu(&rule->list); 1928c2ecf20Sopenharmony_ci if (ops->delete) 1938c2ecf20Sopenharmony_ci ops->delete(rule); 1948c2ecf20Sopenharmony_ci fib_rule_put(rule); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_civoid fib_rules_unregister(struct fib_rules_ops *ops) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct net *net = ops->fro_net; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_lock(&net->rules_mod_lock); 2038c2ecf20Sopenharmony_ci list_del_rcu(&ops->list); 2048c2ecf20Sopenharmony_ci spin_unlock(&net->rules_mod_lock); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci fib_rules_cleanup_ops(ops); 2078c2ecf20Sopenharmony_ci kfree_rcu(ops, rcu); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_rules_unregister); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int uid_range_set(struct fib_kuid_range *range) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return uid_valid(range->start) && uid_valid(range->end); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic struct fib_kuid_range nla_get_kuid_range(struct nlattr **tb) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct fib_rule_uid_range *in; 2198c2ecf20Sopenharmony_ci struct fib_kuid_range out; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci in = (struct fib_rule_uid_range *)nla_data(tb[FRA_UID_RANGE]); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci out.start = make_kuid(current_user_ns(), in->start); 2248c2ecf20Sopenharmony_ci out.end = make_kuid(current_user_ns(), in->end); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return out; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct fib_rule_uid_range out = { 2328c2ecf20Sopenharmony_ci from_kuid_munged(current_user_ns(), range->start), 2338c2ecf20Sopenharmony_ci from_kuid_munged(current_user_ns(), range->end) 2348c2ecf20Sopenharmony_ci }; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int nla_get_port_range(struct nlattr *pattr, 2408c2ecf20Sopenharmony_ci struct fib_rule_port_range *port_range) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci const struct fib_rule_port_range *pr = nla_data(pattr); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (!fib_rule_port_range_valid(pr)) 2458c2ecf20Sopenharmony_ci return -EINVAL; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci port_range->start = pr->start; 2488c2ecf20Sopenharmony_ci port_range->end = pr->end; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int nla_put_port_range(struct sk_buff *skb, int attrtype, 2548c2ecf20Sopenharmony_ci struct fib_rule_port_range *range) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci return nla_put(skb, attrtype, sizeof(*range), range); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, 2608c2ecf20Sopenharmony_ci struct flowi *fl, int flags, 2618c2ecf20Sopenharmony_ci struct fib_lookup_arg *arg) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci int ret = 0; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (rule->iifindex && (rule->iifindex != fl->flowi_iif)) 2668c2ecf20Sopenharmony_ci goto out; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (rule->oifindex && (rule->oifindex != fl->flowi_oif)) 2698c2ecf20Sopenharmony_ci goto out; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask) 2728c2ecf20Sopenharmony_ci goto out; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (rule->tun_id && (rule->tun_id != fl->flowi_tun_key.tun_id)) 2758c2ecf20Sopenharmony_ci goto out; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (rule->l3mdev && !l3mdev_fib_rule_match(rule->fr_net, fl, arg)) 2788c2ecf20Sopenharmony_ci goto out; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (uid_lt(fl->flowi_uid, rule->uid_range.start) || 2818c2ecf20Sopenharmony_ci uid_gt(fl->flowi_uid, rule->uid_range.end)) 2828c2ecf20Sopenharmony_ci goto out; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ret = INDIRECT_CALL_MT(ops->match, 2858c2ecf20Sopenharmony_ci fib6_rule_match, 2868c2ecf20Sopenharmony_ci fib4_rule_match, 2878c2ecf20Sopenharmony_ci rule, fl, flags); 2888c2ecf20Sopenharmony_ciout: 2898c2ecf20Sopenharmony_ci return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ciint fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, 2938c2ecf20Sopenharmony_ci int flags, struct fib_lookup_arg *arg) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct fib_rule *rule; 2968c2ecf20Sopenharmony_ci int err; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci rcu_read_lock(); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rule, &ops->rules_list, list) { 3018c2ecf20Sopenharmony_cijumped: 3028c2ecf20Sopenharmony_ci if (!fib_rule_match(rule, ops, fl, flags, arg)) 3038c2ecf20Sopenharmony_ci continue; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (rule->action == FR_ACT_GOTO) { 3068c2ecf20Sopenharmony_ci struct fib_rule *target; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci target = rcu_dereference(rule->ctarget); 3098c2ecf20Sopenharmony_ci if (target == NULL) { 3108c2ecf20Sopenharmony_ci continue; 3118c2ecf20Sopenharmony_ci } else { 3128c2ecf20Sopenharmony_ci rule = target; 3138c2ecf20Sopenharmony_ci goto jumped; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci } else if (rule->action == FR_ACT_NOP) 3168c2ecf20Sopenharmony_ci continue; 3178c2ecf20Sopenharmony_ci else 3188c2ecf20Sopenharmony_ci err = INDIRECT_CALL_MT(ops->action, 3198c2ecf20Sopenharmony_ci fib6_rule_action, 3208c2ecf20Sopenharmony_ci fib4_rule_action, 3218c2ecf20Sopenharmony_ci rule, fl, flags, arg); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!err && ops->suppress && INDIRECT_CALL_MT(ops->suppress, 3248c2ecf20Sopenharmony_ci fib6_rule_suppress, 3258c2ecf20Sopenharmony_ci fib4_rule_suppress, 3268c2ecf20Sopenharmony_ci rule, flags, arg)) 3278c2ecf20Sopenharmony_ci continue; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (err != -EAGAIN) { 3308c2ecf20Sopenharmony_ci if ((arg->flags & FIB_LOOKUP_NOREF) || 3318c2ecf20Sopenharmony_ci likely(refcount_inc_not_zero(&rule->refcnt))) { 3328c2ecf20Sopenharmony_ci arg->rule = rule; 3338c2ecf20Sopenharmony_ci goto out; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci err = -ESRCH; 3408c2ecf20Sopenharmony_ciout: 3418c2ecf20Sopenharmony_ci rcu_read_unlock(); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return err; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_rules_lookup); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int call_fib_rule_notifier(struct notifier_block *nb, 3488c2ecf20Sopenharmony_ci enum fib_event_type event_type, 3498c2ecf20Sopenharmony_ci struct fib_rule *rule, int family, 3508c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct fib_rule_notifier_info info = { 3538c2ecf20Sopenharmony_ci .info.family = family, 3548c2ecf20Sopenharmony_ci .info.extack = extack, 3558c2ecf20Sopenharmony_ci .rule = rule, 3568c2ecf20Sopenharmony_ci }; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return call_fib_notifier(nb, event_type, &info.info); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int call_fib_rule_notifiers(struct net *net, 3628c2ecf20Sopenharmony_ci enum fib_event_type event_type, 3638c2ecf20Sopenharmony_ci struct fib_rule *rule, 3648c2ecf20Sopenharmony_ci struct fib_rules_ops *ops, 3658c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct fib_rule_notifier_info info = { 3688c2ecf20Sopenharmony_ci .info.family = ops->family, 3698c2ecf20Sopenharmony_ci .info.extack = extack, 3708c2ecf20Sopenharmony_ci .rule = rule, 3718c2ecf20Sopenharmony_ci }; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ops->fib_rules_seq++; 3748c2ecf20Sopenharmony_ci return call_fib_notifiers(net, event_type, &info.info); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* Called with rcu_read_lock() */ 3788c2ecf20Sopenharmony_ciint fib_rules_dump(struct net *net, struct notifier_block *nb, int family, 3798c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 3828c2ecf20Sopenharmony_ci struct fib_rule *rule; 3838c2ecf20Sopenharmony_ci int err = 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ops = lookup_rules_ops(net, family); 3868c2ecf20Sopenharmony_ci if (!ops) 3878c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 3888c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rule, &ops->rules_list, list) { 3898c2ecf20Sopenharmony_ci err = call_fib_rule_notifier(nb, FIB_EVENT_RULE_ADD, 3908c2ecf20Sopenharmony_ci rule, family, extack); 3918c2ecf20Sopenharmony_ci if (err) 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci rules_ops_put(ops); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return err; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_rules_dump); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ciunsigned int fib_rules_seq_read(struct net *net, int family) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci unsigned int fib_rules_seq; 4038c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ops = lookup_rules_ops(net, family); 4088c2ecf20Sopenharmony_ci if (!ops) 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci fib_rules_seq = ops->fib_rules_seq; 4118c2ecf20Sopenharmony_ci rules_ops_put(ops); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return fib_rules_seq; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_rules_seq_read); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic struct fib_rule *rule_find(struct fib_rules_ops *ops, 4188c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh, 4198c2ecf20Sopenharmony_ci struct nlattr **tb, 4208c2ecf20Sopenharmony_ci struct fib_rule *rule, 4218c2ecf20Sopenharmony_ci bool user_priority) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct fib_rule *r; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci list_for_each_entry(r, &ops->rules_list, list) { 4268c2ecf20Sopenharmony_ci if (rule->action && r->action != rule->action) 4278c2ecf20Sopenharmony_ci continue; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (rule->table && r->table != rule->table) 4308c2ecf20Sopenharmony_ci continue; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (user_priority && r->pref != rule->pref) 4338c2ecf20Sopenharmony_ci continue; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (rule->iifname[0] && 4368c2ecf20Sopenharmony_ci memcmp(r->iifname, rule->iifname, IFNAMSIZ)) 4378c2ecf20Sopenharmony_ci continue; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (rule->oifname[0] && 4408c2ecf20Sopenharmony_ci memcmp(r->oifname, rule->oifname, IFNAMSIZ)) 4418c2ecf20Sopenharmony_ci continue; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (rule->mark && r->mark != rule->mark) 4448c2ecf20Sopenharmony_ci continue; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (rule->suppress_ifgroup != -1 && 4478c2ecf20Sopenharmony_ci r->suppress_ifgroup != rule->suppress_ifgroup) 4488c2ecf20Sopenharmony_ci continue; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (rule->suppress_prefixlen != -1 && 4518c2ecf20Sopenharmony_ci r->suppress_prefixlen != rule->suppress_prefixlen) 4528c2ecf20Sopenharmony_ci continue; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (rule->mark_mask && r->mark_mask != rule->mark_mask) 4558c2ecf20Sopenharmony_ci continue; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (rule->tun_id && r->tun_id != rule->tun_id) 4588c2ecf20Sopenharmony_ci continue; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (r->fr_net != rule->fr_net) 4618c2ecf20Sopenharmony_ci continue; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (rule->l3mdev && r->l3mdev != rule->l3mdev) 4648c2ecf20Sopenharmony_ci continue; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (uid_range_set(&rule->uid_range) && 4678c2ecf20Sopenharmony_ci (!uid_eq(r->uid_range.start, rule->uid_range.start) || 4688c2ecf20Sopenharmony_ci !uid_eq(r->uid_range.end, rule->uid_range.end))) 4698c2ecf20Sopenharmony_ci continue; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (rule->ip_proto && r->ip_proto != rule->ip_proto) 4728c2ecf20Sopenharmony_ci continue; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (rule->proto && r->proto != rule->proto) 4758c2ecf20Sopenharmony_ci continue; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (fib_rule_port_range_set(&rule->sport_range) && 4788c2ecf20Sopenharmony_ci !fib_rule_port_range_compare(&r->sport_range, 4798c2ecf20Sopenharmony_ci &rule->sport_range)) 4808c2ecf20Sopenharmony_ci continue; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (fib_rule_port_range_set(&rule->dport_range) && 4838c2ecf20Sopenharmony_ci !fib_rule_port_range_compare(&r->dport_range, 4848c2ecf20Sopenharmony_ci &rule->dport_range)) 4858c2ecf20Sopenharmony_ci continue; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (!ops->compare(r, frh, tb)) 4888c2ecf20Sopenharmony_ci continue; 4898c2ecf20Sopenharmony_ci return r; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return NULL; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_L3_MASTER_DEV 4968c2ecf20Sopenharmony_cistatic int fib_nl2rule_l3mdev(struct nlattr *nla, struct fib_rule *nlrule, 4978c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci nlrule->l3mdev = nla_get_u8(nla); 5008c2ecf20Sopenharmony_ci if (nlrule->l3mdev != 1) { 5018c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid l3mdev attribute"); 5028c2ecf20Sopenharmony_ci return -1; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci#else 5088c2ecf20Sopenharmony_cistatic int fib_nl2rule_l3mdev(struct nlattr *nla, struct fib_rule *nlrule, 5098c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "l3mdev support is not enabled in kernel"); 5128c2ecf20Sopenharmony_ci return -1; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci#endif 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int fib_nl2rule(struct sk_buff *skb, struct nlmsghdr *nlh, 5178c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack, 5188c2ecf20Sopenharmony_ci struct fib_rules_ops *ops, 5198c2ecf20Sopenharmony_ci struct nlattr *tb[], 5208c2ecf20Sopenharmony_ci struct fib_rule **rule, 5218c2ecf20Sopenharmony_ci bool *user_priority) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 5248c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh = nlmsg_data(nlh); 5258c2ecf20Sopenharmony_ci struct fib_rule *nlrule = NULL; 5268c2ecf20Sopenharmony_ci int err = -EINVAL; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (frh->src_len) 5298c2ecf20Sopenharmony_ci if (!tb[FRA_SRC] || 5308c2ecf20Sopenharmony_ci frh->src_len > (ops->addr_size * 8) || 5318c2ecf20Sopenharmony_ci nla_len(tb[FRA_SRC]) != ops->addr_size) { 5328c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid source address"); 5338c2ecf20Sopenharmony_ci goto errout; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (frh->dst_len) 5378c2ecf20Sopenharmony_ci if (!tb[FRA_DST] || 5388c2ecf20Sopenharmony_ci frh->dst_len > (ops->addr_size * 8) || 5398c2ecf20Sopenharmony_ci nla_len(tb[FRA_DST]) != ops->addr_size) { 5408c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid dst address"); 5418c2ecf20Sopenharmony_ci goto errout; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci nlrule = kzalloc(ops->rule_size, GFP_KERNEL); 5458c2ecf20Sopenharmony_ci if (!nlrule) { 5468c2ecf20Sopenharmony_ci err = -ENOMEM; 5478c2ecf20Sopenharmony_ci goto errout; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci refcount_set(&nlrule->refcnt, 1); 5508c2ecf20Sopenharmony_ci nlrule->fr_net = net; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (tb[FRA_PRIORITY]) { 5538c2ecf20Sopenharmony_ci nlrule->pref = nla_get_u32(tb[FRA_PRIORITY]); 5548c2ecf20Sopenharmony_ci *user_priority = true; 5558c2ecf20Sopenharmony_ci } else { 5568c2ecf20Sopenharmony_ci nlrule->pref = fib_default_rule_pref(ops); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci nlrule->proto = tb[FRA_PROTOCOL] ? 5608c2ecf20Sopenharmony_ci nla_get_u8(tb[FRA_PROTOCOL]) : RTPROT_UNSPEC; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (tb[FRA_IIFNAME]) { 5638c2ecf20Sopenharmony_ci struct net_device *dev; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci nlrule->iifindex = -1; 5668c2ecf20Sopenharmony_ci nla_strlcpy(nlrule->iifname, tb[FRA_IIFNAME], IFNAMSIZ); 5678c2ecf20Sopenharmony_ci dev = __dev_get_by_name(net, nlrule->iifname); 5688c2ecf20Sopenharmony_ci if (dev) 5698c2ecf20Sopenharmony_ci nlrule->iifindex = dev->ifindex; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (tb[FRA_OIFNAME]) { 5738c2ecf20Sopenharmony_ci struct net_device *dev; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci nlrule->oifindex = -1; 5768c2ecf20Sopenharmony_ci nla_strlcpy(nlrule->oifname, tb[FRA_OIFNAME], IFNAMSIZ); 5778c2ecf20Sopenharmony_ci dev = __dev_get_by_name(net, nlrule->oifname); 5788c2ecf20Sopenharmony_ci if (dev) 5798c2ecf20Sopenharmony_ci nlrule->oifindex = dev->ifindex; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (tb[FRA_FWMARK]) { 5838c2ecf20Sopenharmony_ci nlrule->mark = nla_get_u32(tb[FRA_FWMARK]); 5848c2ecf20Sopenharmony_ci if (nlrule->mark) 5858c2ecf20Sopenharmony_ci /* compatibility: if the mark value is non-zero all bits 5868c2ecf20Sopenharmony_ci * are compared unless a mask is explicitly specified. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci nlrule->mark_mask = 0xFFFFFFFF; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (tb[FRA_FWMASK]) 5928c2ecf20Sopenharmony_ci nlrule->mark_mask = nla_get_u32(tb[FRA_FWMASK]); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (tb[FRA_TUN_ID]) 5958c2ecf20Sopenharmony_ci nlrule->tun_id = nla_get_be64(tb[FRA_TUN_ID]); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci err = -EINVAL; 5988c2ecf20Sopenharmony_ci if (tb[FRA_L3MDEV] && 5998c2ecf20Sopenharmony_ci fib_nl2rule_l3mdev(tb[FRA_L3MDEV], nlrule, extack) < 0) 6008c2ecf20Sopenharmony_ci goto errout_free; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci nlrule->action = frh->action; 6038c2ecf20Sopenharmony_ci nlrule->flags = frh->flags; 6048c2ecf20Sopenharmony_ci nlrule->table = frh_get_table(frh, tb); 6058c2ecf20Sopenharmony_ci if (tb[FRA_SUPPRESS_PREFIXLEN]) 6068c2ecf20Sopenharmony_ci nlrule->suppress_prefixlen = nla_get_u32(tb[FRA_SUPPRESS_PREFIXLEN]); 6078c2ecf20Sopenharmony_ci else 6088c2ecf20Sopenharmony_ci nlrule->suppress_prefixlen = -1; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (tb[FRA_SUPPRESS_IFGROUP]) 6118c2ecf20Sopenharmony_ci nlrule->suppress_ifgroup = nla_get_u32(tb[FRA_SUPPRESS_IFGROUP]); 6128c2ecf20Sopenharmony_ci else 6138c2ecf20Sopenharmony_ci nlrule->suppress_ifgroup = -1; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (tb[FRA_GOTO]) { 6168c2ecf20Sopenharmony_ci if (nlrule->action != FR_ACT_GOTO) { 6178c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unexpected goto"); 6188c2ecf20Sopenharmony_ci goto errout_free; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci nlrule->target = nla_get_u32(tb[FRA_GOTO]); 6228c2ecf20Sopenharmony_ci /* Backward jumps are prohibited to avoid endless loops */ 6238c2ecf20Sopenharmony_ci if (nlrule->target <= nlrule->pref) { 6248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Backward goto not supported"); 6258c2ecf20Sopenharmony_ci goto errout_free; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci } else if (nlrule->action == FR_ACT_GOTO) { 6288c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing goto target for action goto"); 6298c2ecf20Sopenharmony_ci goto errout_free; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (nlrule->l3mdev && nlrule->table) { 6338c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "l3mdev and table are mutually exclusive"); 6348c2ecf20Sopenharmony_ci goto errout_free; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (tb[FRA_UID_RANGE]) { 6388c2ecf20Sopenharmony_ci if (current_user_ns() != net->user_ns) { 6398c2ecf20Sopenharmony_ci err = -EPERM; 6408c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "No permission to set uid"); 6418c2ecf20Sopenharmony_ci goto errout_free; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci nlrule->uid_range = nla_get_kuid_range(tb); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (!uid_range_set(&nlrule->uid_range) || 6478c2ecf20Sopenharmony_ci !uid_lte(nlrule->uid_range.start, nlrule->uid_range.end)) { 6488c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid uid range"); 6498c2ecf20Sopenharmony_ci goto errout_free; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } else { 6528c2ecf20Sopenharmony_ci nlrule->uid_range = fib_kuid_range_unset; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (tb[FRA_IP_PROTO]) 6568c2ecf20Sopenharmony_ci nlrule->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (tb[FRA_SPORT_RANGE]) { 6598c2ecf20Sopenharmony_ci err = nla_get_port_range(tb[FRA_SPORT_RANGE], 6608c2ecf20Sopenharmony_ci &nlrule->sport_range); 6618c2ecf20Sopenharmony_ci if (err) { 6628c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid sport range"); 6638c2ecf20Sopenharmony_ci goto errout_free; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (tb[FRA_DPORT_RANGE]) { 6688c2ecf20Sopenharmony_ci err = nla_get_port_range(tb[FRA_DPORT_RANGE], 6698c2ecf20Sopenharmony_ci &nlrule->dport_range); 6708c2ecf20Sopenharmony_ci if (err) { 6718c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid dport range"); 6728c2ecf20Sopenharmony_ci goto errout_free; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci *rule = nlrule; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cierrout_free: 6818c2ecf20Sopenharmony_ci kfree(nlrule); 6828c2ecf20Sopenharmony_cierrout: 6838c2ecf20Sopenharmony_ci return err; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, 6878c2ecf20Sopenharmony_ci struct nlattr **tb, struct fib_rule *rule) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct fib_rule *r; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci list_for_each_entry(r, &ops->rules_list, list) { 6928c2ecf20Sopenharmony_ci if (r->action != rule->action) 6938c2ecf20Sopenharmony_ci continue; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (r->table != rule->table) 6968c2ecf20Sopenharmony_ci continue; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (r->pref != rule->pref) 6998c2ecf20Sopenharmony_ci continue; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (memcmp(r->iifname, rule->iifname, IFNAMSIZ)) 7028c2ecf20Sopenharmony_ci continue; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (memcmp(r->oifname, rule->oifname, IFNAMSIZ)) 7058c2ecf20Sopenharmony_ci continue; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (r->mark != rule->mark) 7088c2ecf20Sopenharmony_ci continue; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (r->suppress_ifgroup != rule->suppress_ifgroup) 7118c2ecf20Sopenharmony_ci continue; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (r->suppress_prefixlen != rule->suppress_prefixlen) 7148c2ecf20Sopenharmony_ci continue; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (r->mark_mask != rule->mark_mask) 7178c2ecf20Sopenharmony_ci continue; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (r->tun_id != rule->tun_id) 7208c2ecf20Sopenharmony_ci continue; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (r->fr_net != rule->fr_net) 7238c2ecf20Sopenharmony_ci continue; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (r->l3mdev != rule->l3mdev) 7268c2ecf20Sopenharmony_ci continue; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (!uid_eq(r->uid_range.start, rule->uid_range.start) || 7298c2ecf20Sopenharmony_ci !uid_eq(r->uid_range.end, rule->uid_range.end)) 7308c2ecf20Sopenharmony_ci continue; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (r->ip_proto != rule->ip_proto) 7338c2ecf20Sopenharmony_ci continue; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (r->proto != rule->proto) 7368c2ecf20Sopenharmony_ci continue; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (!fib_rule_port_range_compare(&r->sport_range, 7398c2ecf20Sopenharmony_ci &rule->sport_range)) 7408c2ecf20Sopenharmony_ci continue; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (!fib_rule_port_range_compare(&r->dport_range, 7438c2ecf20Sopenharmony_ci &rule->dport_range)) 7448c2ecf20Sopenharmony_ci continue; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (!ops->compare(r, frh, tb)) 7478c2ecf20Sopenharmony_ci continue; 7488c2ecf20Sopenharmony_ci return 1; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci return 0; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ciint fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, 7548c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 7578c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh = nlmsg_data(nlh); 7588c2ecf20Sopenharmony_ci struct fib_rules_ops *ops = NULL; 7598c2ecf20Sopenharmony_ci struct fib_rule *rule = NULL, *r, *last = NULL; 7608c2ecf20Sopenharmony_ci struct nlattr *tb[FRA_MAX + 1]; 7618c2ecf20Sopenharmony_ci int err = -EINVAL, unresolved = 0; 7628c2ecf20Sopenharmony_ci bool user_priority = false; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) { 7658c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid msg length"); 7668c2ecf20Sopenharmony_ci goto errout; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci ops = lookup_rules_ops(net, frh->family); 7708c2ecf20Sopenharmony_ci if (!ops) { 7718c2ecf20Sopenharmony_ci err = -EAFNOSUPPORT; 7728c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Rule family not supported"); 7738c2ecf20Sopenharmony_ci goto errout; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*frh), tb, FRA_MAX, 7778c2ecf20Sopenharmony_ci ops->policy, extack); 7788c2ecf20Sopenharmony_ci if (err < 0) { 7798c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Error parsing msg"); 7808c2ecf20Sopenharmony_ci goto errout; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci err = fib_nl2rule(skb, nlh, extack, ops, tb, &rule, &user_priority); 7848c2ecf20Sopenharmony_ci if (err) 7858c2ecf20Sopenharmony_ci goto errout; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if ((nlh->nlmsg_flags & NLM_F_EXCL) && 7888c2ecf20Sopenharmony_ci rule_exists(ops, frh, tb, rule)) { 7898c2ecf20Sopenharmony_ci err = -EEXIST; 7908c2ecf20Sopenharmony_ci goto errout_free; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci err = ops->configure(rule, skb, frh, tb, extack); 7948c2ecf20Sopenharmony_ci if (err < 0) 7958c2ecf20Sopenharmony_ci goto errout_free; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci err = call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops, 7988c2ecf20Sopenharmony_ci extack); 7998c2ecf20Sopenharmony_ci if (err < 0) 8008c2ecf20Sopenharmony_ci goto errout_free; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci list_for_each_entry(r, &ops->rules_list, list) { 8038c2ecf20Sopenharmony_ci if (r->pref == rule->target) { 8048c2ecf20Sopenharmony_ci RCU_INIT_POINTER(rule->ctarget, r); 8058c2ecf20Sopenharmony_ci break; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (rcu_dereference_protected(rule->ctarget, 1) == NULL) 8108c2ecf20Sopenharmony_ci unresolved = 1; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci list_for_each_entry(r, &ops->rules_list, list) { 8138c2ecf20Sopenharmony_ci if (r->pref > rule->pref) 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci last = r; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (last) 8198c2ecf20Sopenharmony_ci list_add_rcu(&rule->list, &last->list); 8208c2ecf20Sopenharmony_ci else 8218c2ecf20Sopenharmony_ci list_add_rcu(&rule->list, &ops->rules_list); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (ops->unresolved_rules) { 8248c2ecf20Sopenharmony_ci /* 8258c2ecf20Sopenharmony_ci * There are unresolved goto rules in the list, check if 8268c2ecf20Sopenharmony_ci * any of them are pointing to this new rule. 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_ci list_for_each_entry(r, &ops->rules_list, list) { 8298c2ecf20Sopenharmony_ci if (r->action == FR_ACT_GOTO && 8308c2ecf20Sopenharmony_ci r->target == rule->pref && 8318c2ecf20Sopenharmony_ci rtnl_dereference(r->ctarget) == NULL) { 8328c2ecf20Sopenharmony_ci rcu_assign_pointer(r->ctarget, rule); 8338c2ecf20Sopenharmony_ci if (--ops->unresolved_rules == 0) 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (rule->action == FR_ACT_GOTO) 8408c2ecf20Sopenharmony_ci ops->nr_goto_rules++; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (unresolved) 8438c2ecf20Sopenharmony_ci ops->unresolved_rules++; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (rule->tun_id) 8468c2ecf20Sopenharmony_ci ip_tunnel_need_metadata(); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid); 8498c2ecf20Sopenharmony_ci flush_route_cache(ops); 8508c2ecf20Sopenharmony_ci rules_ops_put(ops); 8518c2ecf20Sopenharmony_ci return 0; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cierrout_free: 8548c2ecf20Sopenharmony_ci kfree(rule); 8558c2ecf20Sopenharmony_cierrout: 8568c2ecf20Sopenharmony_ci rules_ops_put(ops); 8578c2ecf20Sopenharmony_ci return err; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_nl_newrule); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ciint fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, 8628c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 8658c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh = nlmsg_data(nlh); 8668c2ecf20Sopenharmony_ci struct fib_rules_ops *ops = NULL; 8678c2ecf20Sopenharmony_ci struct fib_rule *rule = NULL, *r, *nlrule = NULL; 8688c2ecf20Sopenharmony_ci struct nlattr *tb[FRA_MAX+1]; 8698c2ecf20Sopenharmony_ci int err = -EINVAL; 8708c2ecf20Sopenharmony_ci bool user_priority = false; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) { 8738c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid msg length"); 8748c2ecf20Sopenharmony_ci goto errout; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci ops = lookup_rules_ops(net, frh->family); 8788c2ecf20Sopenharmony_ci if (ops == NULL) { 8798c2ecf20Sopenharmony_ci err = -EAFNOSUPPORT; 8808c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Rule family not supported"); 8818c2ecf20Sopenharmony_ci goto errout; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*frh), tb, FRA_MAX, 8858c2ecf20Sopenharmony_ci ops->policy, extack); 8868c2ecf20Sopenharmony_ci if (err < 0) { 8878c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Error parsing msg"); 8888c2ecf20Sopenharmony_ci goto errout; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci err = fib_nl2rule(skb, nlh, extack, ops, tb, &nlrule, &user_priority); 8928c2ecf20Sopenharmony_ci if (err) 8938c2ecf20Sopenharmony_ci goto errout; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci rule = rule_find(ops, frh, tb, nlrule, user_priority); 8968c2ecf20Sopenharmony_ci if (!rule) { 8978c2ecf20Sopenharmony_ci err = -ENOENT; 8988c2ecf20Sopenharmony_ci goto errout; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (rule->flags & FIB_RULE_PERMANENT) { 9028c2ecf20Sopenharmony_ci err = -EPERM; 9038c2ecf20Sopenharmony_ci goto errout; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (ops->delete) { 9078c2ecf20Sopenharmony_ci err = ops->delete(rule); 9088c2ecf20Sopenharmony_ci if (err) 9098c2ecf20Sopenharmony_ci goto errout; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (rule->tun_id) 9138c2ecf20Sopenharmony_ci ip_tunnel_unneed_metadata(); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci list_del_rcu(&rule->list); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (rule->action == FR_ACT_GOTO) { 9188c2ecf20Sopenharmony_ci ops->nr_goto_rules--; 9198c2ecf20Sopenharmony_ci if (rtnl_dereference(rule->ctarget) == NULL) 9208c2ecf20Sopenharmony_ci ops->unresolved_rules--; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* 9248c2ecf20Sopenharmony_ci * Check if this rule is a target to any of them. If so, 9258c2ecf20Sopenharmony_ci * adjust to the next one with the same preference or 9268c2ecf20Sopenharmony_ci * disable them. As this operation is eventually very 9278c2ecf20Sopenharmony_ci * expensive, it is only performed if goto rules, except 9288c2ecf20Sopenharmony_ci * current if it is goto rule, have actually been added. 9298c2ecf20Sopenharmony_ci */ 9308c2ecf20Sopenharmony_ci if (ops->nr_goto_rules > 0) { 9318c2ecf20Sopenharmony_ci struct fib_rule *n; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci n = list_next_entry(rule, list); 9348c2ecf20Sopenharmony_ci if (&n->list == &ops->rules_list || n->pref != rule->pref) 9358c2ecf20Sopenharmony_ci n = NULL; 9368c2ecf20Sopenharmony_ci list_for_each_entry(r, &ops->rules_list, list) { 9378c2ecf20Sopenharmony_ci if (rtnl_dereference(r->ctarget) != rule) 9388c2ecf20Sopenharmony_ci continue; 9398c2ecf20Sopenharmony_ci rcu_assign_pointer(r->ctarget, n); 9408c2ecf20Sopenharmony_ci if (!n) 9418c2ecf20Sopenharmony_ci ops->unresolved_rules++; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, 9468c2ecf20Sopenharmony_ci NULL); 9478c2ecf20Sopenharmony_ci notify_rule_change(RTM_DELRULE, rule, ops, nlh, 9488c2ecf20Sopenharmony_ci NETLINK_CB(skb).portid); 9498c2ecf20Sopenharmony_ci fib_rule_put(rule); 9508c2ecf20Sopenharmony_ci flush_route_cache(ops); 9518c2ecf20Sopenharmony_ci rules_ops_put(ops); 9528c2ecf20Sopenharmony_ci kfree(nlrule); 9538c2ecf20Sopenharmony_ci return 0; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cierrout: 9568c2ecf20Sopenharmony_ci kfree(nlrule); 9578c2ecf20Sopenharmony_ci rules_ops_put(ops); 9588c2ecf20Sopenharmony_ci return err; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_nl_delrule); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, 9638c2ecf20Sopenharmony_ci struct fib_rule *rule) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci size_t payload = NLMSG_ALIGN(sizeof(struct fib_rule_hdr)) 9668c2ecf20Sopenharmony_ci + nla_total_size(IFNAMSIZ) /* FRA_IIFNAME */ 9678c2ecf20Sopenharmony_ci + nla_total_size(IFNAMSIZ) /* FRA_OIFNAME */ 9688c2ecf20Sopenharmony_ci + nla_total_size(4) /* FRA_PRIORITY */ 9698c2ecf20Sopenharmony_ci + nla_total_size(4) /* FRA_TABLE */ 9708c2ecf20Sopenharmony_ci + nla_total_size(4) /* FRA_SUPPRESS_PREFIXLEN */ 9718c2ecf20Sopenharmony_ci + nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */ 9728c2ecf20Sopenharmony_ci + nla_total_size(4) /* FRA_FWMARK */ 9738c2ecf20Sopenharmony_ci + nla_total_size(4) /* FRA_FWMASK */ 9748c2ecf20Sopenharmony_ci + nla_total_size_64bit(8) /* FRA_TUN_ID */ 9758c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct fib_kuid_range)) 9768c2ecf20Sopenharmony_ci + nla_total_size(1) /* FRA_PROTOCOL */ 9778c2ecf20Sopenharmony_ci + nla_total_size(1) /* FRA_IP_PROTO */ 9788c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */ 9798c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */ 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (ops->nlmsg_payload) 9828c2ecf20Sopenharmony_ci payload += ops->nlmsg_payload(rule); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci return payload; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, 9888c2ecf20Sopenharmony_ci u32 pid, u32 seq, int type, int flags, 9898c2ecf20Sopenharmony_ci struct fib_rules_ops *ops) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 9928c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, pid, seq, type, sizeof(*frh), flags); 9958c2ecf20Sopenharmony_ci if (nlh == NULL) 9968c2ecf20Sopenharmony_ci return -EMSGSIZE; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci frh = nlmsg_data(nlh); 9998c2ecf20Sopenharmony_ci frh->family = ops->family; 10008c2ecf20Sopenharmony_ci frh->table = rule->table < 256 ? rule->table : RT_TABLE_COMPAT; 10018c2ecf20Sopenharmony_ci if (nla_put_u32(skb, FRA_TABLE, rule->table)) 10028c2ecf20Sopenharmony_ci goto nla_put_failure; 10038c2ecf20Sopenharmony_ci if (nla_put_u32(skb, FRA_SUPPRESS_PREFIXLEN, rule->suppress_prefixlen)) 10048c2ecf20Sopenharmony_ci goto nla_put_failure; 10058c2ecf20Sopenharmony_ci frh->res1 = 0; 10068c2ecf20Sopenharmony_ci frh->res2 = 0; 10078c2ecf20Sopenharmony_ci frh->action = rule->action; 10088c2ecf20Sopenharmony_ci frh->flags = rule->flags; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (nla_put_u8(skb, FRA_PROTOCOL, rule->proto)) 10118c2ecf20Sopenharmony_ci goto nla_put_failure; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (rule->action == FR_ACT_GOTO && 10148c2ecf20Sopenharmony_ci rcu_access_pointer(rule->ctarget) == NULL) 10158c2ecf20Sopenharmony_ci frh->flags |= FIB_RULE_UNRESOLVED; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (rule->iifname[0]) { 10188c2ecf20Sopenharmony_ci if (nla_put_string(skb, FRA_IIFNAME, rule->iifname)) 10198c2ecf20Sopenharmony_ci goto nla_put_failure; 10208c2ecf20Sopenharmony_ci if (rule->iifindex == -1) 10218c2ecf20Sopenharmony_ci frh->flags |= FIB_RULE_IIF_DETACHED; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (rule->oifname[0]) { 10258c2ecf20Sopenharmony_ci if (nla_put_string(skb, FRA_OIFNAME, rule->oifname)) 10268c2ecf20Sopenharmony_ci goto nla_put_failure; 10278c2ecf20Sopenharmony_ci if (rule->oifindex == -1) 10288c2ecf20Sopenharmony_ci frh->flags |= FIB_RULE_OIF_DETACHED; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if ((rule->pref && 10328c2ecf20Sopenharmony_ci nla_put_u32(skb, FRA_PRIORITY, rule->pref)) || 10338c2ecf20Sopenharmony_ci (rule->mark && 10348c2ecf20Sopenharmony_ci nla_put_u32(skb, FRA_FWMARK, rule->mark)) || 10358c2ecf20Sopenharmony_ci ((rule->mark_mask || rule->mark) && 10368c2ecf20Sopenharmony_ci nla_put_u32(skb, FRA_FWMASK, rule->mark_mask)) || 10378c2ecf20Sopenharmony_ci (rule->target && 10388c2ecf20Sopenharmony_ci nla_put_u32(skb, FRA_GOTO, rule->target)) || 10398c2ecf20Sopenharmony_ci (rule->tun_id && 10408c2ecf20Sopenharmony_ci nla_put_be64(skb, FRA_TUN_ID, rule->tun_id, FRA_PAD)) || 10418c2ecf20Sopenharmony_ci (rule->l3mdev && 10428c2ecf20Sopenharmony_ci nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || 10438c2ecf20Sopenharmony_ci (uid_range_set(&rule->uid_range) && 10448c2ecf20Sopenharmony_ci nla_put_uid_range(skb, &rule->uid_range)) || 10458c2ecf20Sopenharmony_ci (fib_rule_port_range_set(&rule->sport_range) && 10468c2ecf20Sopenharmony_ci nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || 10478c2ecf20Sopenharmony_ci (fib_rule_port_range_set(&rule->dport_range) && 10488c2ecf20Sopenharmony_ci nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) || 10498c2ecf20Sopenharmony_ci (rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto))) 10508c2ecf20Sopenharmony_ci goto nla_put_failure; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (rule->suppress_ifgroup != -1) { 10538c2ecf20Sopenharmony_ci if (nla_put_u32(skb, FRA_SUPPRESS_IFGROUP, rule->suppress_ifgroup)) 10548c2ecf20Sopenharmony_ci goto nla_put_failure; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (ops->fill(rule, skb, frh) < 0) 10588c2ecf20Sopenharmony_ci goto nla_put_failure; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 10618c2ecf20Sopenharmony_ci return 0; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cinla_put_failure: 10648c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 10658c2ecf20Sopenharmony_ci return -EMSGSIZE; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, 10698c2ecf20Sopenharmony_ci struct fib_rules_ops *ops) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci int idx = 0; 10728c2ecf20Sopenharmony_ci struct fib_rule *rule; 10738c2ecf20Sopenharmony_ci int err = 0; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci rcu_read_lock(); 10768c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rule, &ops->rules_list, list) { 10778c2ecf20Sopenharmony_ci if (idx < cb->args[1]) 10788c2ecf20Sopenharmony_ci goto skip; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci err = fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).portid, 10818c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, RTM_NEWRULE, 10828c2ecf20Sopenharmony_ci NLM_F_MULTI, ops); 10838c2ecf20Sopenharmony_ci if (err) 10848c2ecf20Sopenharmony_ci break; 10858c2ecf20Sopenharmony_ciskip: 10868c2ecf20Sopenharmony_ci idx++; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci rcu_read_unlock(); 10898c2ecf20Sopenharmony_ci cb->args[1] = idx; 10908c2ecf20Sopenharmony_ci rules_ops_put(ops); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return err; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int fib_valid_dumprule_req(const struct nlmsghdr *nlh, 10968c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) { 11018c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid header for fib rule dump request"); 11028c2ecf20Sopenharmony_ci return -EINVAL; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci frh = nlmsg_data(nlh); 11068c2ecf20Sopenharmony_ci if (frh->dst_len || frh->src_len || frh->tos || frh->table || 11078c2ecf20Sopenharmony_ci frh->res1 || frh->res2 || frh->action || frh->flags) { 11088c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 11098c2ecf20Sopenharmony_ci "Invalid values in header for fib rule dump request"); 11108c2ecf20Sopenharmony_ci return -EINVAL; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*frh))) { 11148c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid data after header in fib rule dump request"); 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 11248c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 11258c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 11268c2ecf20Sopenharmony_ci int idx = 0, family; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (cb->strict_check) { 11298c2ecf20Sopenharmony_ci int err = fib_valid_dumprule_req(nlh, cb->extack); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (err < 0) 11328c2ecf20Sopenharmony_ci return err; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci family = rtnl_msg_family(nlh); 11368c2ecf20Sopenharmony_ci if (family != AF_UNSPEC) { 11378c2ecf20Sopenharmony_ci /* Protocol specific dump request */ 11388c2ecf20Sopenharmony_ci ops = lookup_rules_ops(net, family); 11398c2ecf20Sopenharmony_ci if (ops == NULL) 11408c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci dump_rules(skb, cb, ops); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return skb->len; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci rcu_read_lock(); 11488c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ops, &net->rules_ops, list) { 11498c2ecf20Sopenharmony_ci if (idx < cb->args[0] || !try_module_get(ops->owner)) 11508c2ecf20Sopenharmony_ci goto skip; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (dump_rules(skb, cb, ops) < 0) 11538c2ecf20Sopenharmony_ci break; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci cb->args[1] = 0; 11568c2ecf20Sopenharmony_ciskip: 11578c2ecf20Sopenharmony_ci idx++; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci rcu_read_unlock(); 11608c2ecf20Sopenharmony_ci cb->args[0] = idx; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci return skb->len; 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic void notify_rule_change(int event, struct fib_rule *rule, 11668c2ecf20Sopenharmony_ci struct fib_rules_ops *ops, struct nlmsghdr *nlh, 11678c2ecf20Sopenharmony_ci u32 pid) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci struct net *net; 11708c2ecf20Sopenharmony_ci struct sk_buff *skb; 11718c2ecf20Sopenharmony_ci int err = -ENOMEM; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci net = ops->fro_net; 11748c2ecf20Sopenharmony_ci skb = nlmsg_new(fib_rule_nlmsg_size(ops, rule), GFP_KERNEL); 11758c2ecf20Sopenharmony_ci if (skb == NULL) 11768c2ecf20Sopenharmony_ci goto errout; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci err = fib_nl_fill_rule(skb, rule, pid, nlh->nlmsg_seq, event, 0, ops); 11798c2ecf20Sopenharmony_ci if (err < 0) { 11808c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in fib_rule_nlmsg_size() */ 11818c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 11828c2ecf20Sopenharmony_ci kfree_skb(skb); 11838c2ecf20Sopenharmony_ci goto errout; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci rtnl_notify(skb, net, pid, ops->nlgroup, nlh, GFP_KERNEL); 11878c2ecf20Sopenharmony_ci return; 11888c2ecf20Sopenharmony_cierrout: 11898c2ecf20Sopenharmony_ci if (err < 0) 11908c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, ops->nlgroup, err); 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic void attach_rules(struct list_head *rules, struct net_device *dev) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct fib_rule *rule; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci list_for_each_entry(rule, rules, list) { 11988c2ecf20Sopenharmony_ci if (rule->iifindex == -1 && 11998c2ecf20Sopenharmony_ci strcmp(dev->name, rule->iifname) == 0) 12008c2ecf20Sopenharmony_ci rule->iifindex = dev->ifindex; 12018c2ecf20Sopenharmony_ci if (rule->oifindex == -1 && 12028c2ecf20Sopenharmony_ci strcmp(dev->name, rule->oifname) == 0) 12038c2ecf20Sopenharmony_ci rule->oifindex = dev->ifindex; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic void detach_rules(struct list_head *rules, struct net_device *dev) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct fib_rule *rule; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci list_for_each_entry(rule, rules, list) { 12128c2ecf20Sopenharmony_ci if (rule->iifindex == dev->ifindex) 12138c2ecf20Sopenharmony_ci rule->iifindex = -1; 12148c2ecf20Sopenharmony_ci if (rule->oifindex == dev->ifindex) 12158c2ecf20Sopenharmony_ci rule->oifindex = -1; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_cistatic int fib_rules_event(struct notifier_block *this, unsigned long event, 12218c2ecf20Sopenharmony_ci void *ptr) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 12248c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 12258c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci ASSERT_RTNL(); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci switch (event) { 12308c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 12318c2ecf20Sopenharmony_ci list_for_each_entry(ops, &net->rules_ops, list) 12328c2ecf20Sopenharmony_ci attach_rules(&ops->rules_list, dev); 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci case NETDEV_CHANGENAME: 12368c2ecf20Sopenharmony_ci list_for_each_entry(ops, &net->rules_ops, list) { 12378c2ecf20Sopenharmony_ci detach_rules(&ops->rules_list, dev); 12388c2ecf20Sopenharmony_ci attach_rules(&ops->rules_list, dev); 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci break; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 12438c2ecf20Sopenharmony_ci list_for_each_entry(ops, &net->rules_ops, list) 12448c2ecf20Sopenharmony_ci detach_rules(&ops->rules_list, dev); 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci return NOTIFY_DONE; 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_cistatic struct notifier_block fib_rules_notifier = { 12528c2ecf20Sopenharmony_ci .notifier_call = fib_rules_event, 12538c2ecf20Sopenharmony_ci}; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_cistatic int __net_init fib_rules_net_init(struct net *net) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->rules_ops); 12588c2ecf20Sopenharmony_ci spin_lock_init(&net->rules_mod_lock); 12598c2ecf20Sopenharmony_ci return 0; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic void __net_exit fib_rules_net_exit(struct net *net) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci WARN_ON_ONCE(!list_empty(&net->rules_ops)); 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic struct pernet_operations fib_rules_net_ops = { 12688c2ecf20Sopenharmony_ci .init = fib_rules_net_init, 12698c2ecf20Sopenharmony_ci .exit = fib_rules_net_exit, 12708c2ecf20Sopenharmony_ci}; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic int __init fib_rules_init(void) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci int err; 12758c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, 0); 12768c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, 0); 12778c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, 0); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci err = register_pernet_subsys(&fib_rules_net_ops); 12808c2ecf20Sopenharmony_ci if (err < 0) 12818c2ecf20Sopenharmony_ci goto fail; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&fib_rules_notifier); 12848c2ecf20Sopenharmony_ci if (err < 0) 12858c2ecf20Sopenharmony_ci goto fail_unregister; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci return 0; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cifail_unregister: 12908c2ecf20Sopenharmony_ci unregister_pernet_subsys(&fib_rules_net_ops); 12918c2ecf20Sopenharmony_cifail: 12928c2ecf20Sopenharmony_ci rtnl_unregister(PF_UNSPEC, RTM_NEWRULE); 12938c2ecf20Sopenharmony_ci rtnl_unregister(PF_UNSPEC, RTM_DELRULE); 12948c2ecf20Sopenharmony_ci rtnl_unregister(PF_UNSPEC, RTM_GETRULE); 12958c2ecf20Sopenharmony_ci return err; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cisubsys_initcall(fib_rules_init); 1299