162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * INET An implementation of the TCP/IP protocol suite for the LINUX 462306a36Sopenharmony_ci * operating system. INET is implemented using the BSD Socket 562306a36Sopenharmony_ci * interface as the means of communication with the user level. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * IPv4 Forwarding Information Base: policy rules. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 1062306a36Sopenharmony_ci * Thomas Graf <tgraf@suug.ch> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Fixes: 1362306a36Sopenharmony_ci * Rani Assaf : local_rule cannot be deleted 1462306a36Sopenharmony_ci * Marc Boucher : routing by fwmark 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/netdevice.h> 2062306a36Sopenharmony_ci#include <linux/netlink.h> 2162306a36Sopenharmony_ci#include <linux/inetdevice.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/list.h> 2462306a36Sopenharmony_ci#include <linux/rcupdate.h> 2562306a36Sopenharmony_ci#include <linux/export.h> 2662306a36Sopenharmony_ci#include <net/inet_dscp.h> 2762306a36Sopenharmony_ci#include <net/ip.h> 2862306a36Sopenharmony_ci#include <net/route.h> 2962306a36Sopenharmony_ci#include <net/tcp.h> 3062306a36Sopenharmony_ci#include <net/ip_fib.h> 3162306a36Sopenharmony_ci#include <net/nexthop.h> 3262306a36Sopenharmony_ci#include <net/fib_rules.h> 3362306a36Sopenharmony_ci#include <linux/indirect_call_wrapper.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct fib4_rule { 3662306a36Sopenharmony_ci struct fib_rule common; 3762306a36Sopenharmony_ci u8 dst_len; 3862306a36Sopenharmony_ci u8 src_len; 3962306a36Sopenharmony_ci dscp_t dscp; 4062306a36Sopenharmony_ci __be32 src; 4162306a36Sopenharmony_ci __be32 srcmask; 4262306a36Sopenharmony_ci __be32 dst; 4362306a36Sopenharmony_ci __be32 dstmask; 4462306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 4562306a36Sopenharmony_ci u32 tclassid; 4662306a36Sopenharmony_ci#endif 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic bool fib4_rule_matchall(const struct fib_rule *rule) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct fib4_rule *r = container_of(rule, struct fib4_rule, common); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (r->dst_len || r->src_len || r->dscp) 5462306a36Sopenharmony_ci return false; 5562306a36Sopenharmony_ci return fib_rule_matchall(rule); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cibool fib4_rule_default(const struct fib_rule *rule) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (!fib4_rule_matchall(rule) || rule->action != FR_ACT_TO_TBL || 6162306a36Sopenharmony_ci rule->l3mdev) 6262306a36Sopenharmony_ci return false; 6362306a36Sopenharmony_ci if (rule->table != RT_TABLE_LOCAL && rule->table != RT_TABLE_MAIN && 6462306a36Sopenharmony_ci rule->table != RT_TABLE_DEFAULT) 6562306a36Sopenharmony_ci return false; 6662306a36Sopenharmony_ci return true; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib4_rule_default); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciint fib4_rules_dump(struct net *net, struct notifier_block *nb, 7162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return fib_rules_dump(net, nb, AF_INET, extack); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciunsigned int fib4_rules_seq_read(struct net *net) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return fib_rules_seq_read(net, AF_INET); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciint __fib_lookup(struct net *net, struct flowi4 *flp, 8262306a36Sopenharmony_ci struct fib_result *res, unsigned int flags) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct fib_lookup_arg arg = { 8562306a36Sopenharmony_ci .result = res, 8662306a36Sopenharmony_ci .flags = flags, 8762306a36Sopenharmony_ci }; 8862306a36Sopenharmony_ci int err; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* update flow if oif or iif point to device enslaved to l3mdev */ 9162306a36Sopenharmony_ci l3mdev_update_flow(net, flowi4_to_flowi(flp)); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci err = fib_rules_lookup(net->ipv4.rules_ops, flowi4_to_flowi(flp), 0, &arg); 9462306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 9562306a36Sopenharmony_ci if (arg.rule) 9662306a36Sopenharmony_ci res->tclassid = ((struct fib4_rule *)arg.rule)->tclassid; 9762306a36Sopenharmony_ci else 9862306a36Sopenharmony_ci res->tclassid = 0; 9962306a36Sopenharmony_ci#endif 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (err == -ESRCH) 10262306a36Sopenharmony_ci err = -ENETUNREACH; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return err; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__fib_lookup); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE int fib4_rule_action(struct fib_rule *rule, 10962306a36Sopenharmony_ci struct flowi *flp, int flags, 11062306a36Sopenharmony_ci struct fib_lookup_arg *arg) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci int err = -EAGAIN; 11362306a36Sopenharmony_ci struct fib_table *tbl; 11462306a36Sopenharmony_ci u32 tb_id; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci switch (rule->action) { 11762306a36Sopenharmony_ci case FR_ACT_TO_TBL: 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci case FR_ACT_UNREACHABLE: 12162306a36Sopenharmony_ci return -ENETUNREACH; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci case FR_ACT_PROHIBIT: 12462306a36Sopenharmony_ci return -EACCES; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci case FR_ACT_BLACKHOLE: 12762306a36Sopenharmony_ci default: 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci rcu_read_lock(); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci tb_id = fib_rule_get_table(rule, arg); 13462306a36Sopenharmony_ci tbl = fib_get_table(rule->fr_net, tb_id); 13562306a36Sopenharmony_ci if (tbl) 13662306a36Sopenharmony_ci err = fib_table_lookup(tbl, &flp->u.ip4, 13762306a36Sopenharmony_ci (struct fib_result *)arg->result, 13862306a36Sopenharmony_ci arg->flags); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci rcu_read_unlock(); 14162306a36Sopenharmony_ci return err; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE bool fib4_rule_suppress(struct fib_rule *rule, 14562306a36Sopenharmony_ci int flags, 14662306a36Sopenharmony_ci struct fib_lookup_arg *arg) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct fib_result *result = arg->result; 14962306a36Sopenharmony_ci struct net_device *dev = NULL; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (result->fi) { 15262306a36Sopenharmony_ci struct fib_nh_common *nhc = fib_info_nhc(result->fi, 0); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dev = nhc->nhc_dev; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* do not accept result if the route does 15862306a36Sopenharmony_ci * not meet the required prefix length 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci if (result->prefixlen <= rule->suppress_prefixlen) 16162306a36Sopenharmony_ci goto suppress_route; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* do not accept result if the route uses a device 16462306a36Sopenharmony_ci * belonging to a forbidden interface group 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci if (rule->suppress_ifgroup != -1 && dev && dev->group == rule->suppress_ifgroup) 16762306a36Sopenharmony_ci goto suppress_route; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return false; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cisuppress_route: 17262306a36Sopenharmony_ci if (!(arg->flags & FIB_LOOKUP_NOREF)) 17362306a36Sopenharmony_ci fib_info_put(result->fi); 17462306a36Sopenharmony_ci return true; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE int fib4_rule_match(struct fib_rule *rule, 17862306a36Sopenharmony_ci struct flowi *fl, int flags) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct fib4_rule *r = (struct fib4_rule *) rule; 18162306a36Sopenharmony_ci struct flowi4 *fl4 = &fl->u.ip4; 18262306a36Sopenharmony_ci __be32 daddr = fl4->daddr; 18362306a36Sopenharmony_ci __be32 saddr = fl4->saddr; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (((saddr ^ r->src) & r->srcmask) || 18662306a36Sopenharmony_ci ((daddr ^ r->dst) & r->dstmask)) 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (r->dscp && r->dscp != inet_dsfield_to_dscp(fl4->flowi4_tos)) 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (rule->ip_proto && (rule->ip_proto != fl4->flowi4_proto)) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (fib_rule_port_range_set(&rule->sport_range) && 19662306a36Sopenharmony_ci !fib_rule_port_inrange(&rule->sport_range, fl4->fl4_sport)) 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (fib_rule_port_range_set(&rule->dport_range) && 20062306a36Sopenharmony_ci !fib_rule_port_inrange(&rule->dport_range, fl4->fl4_dport)) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 1; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic struct fib_table *fib_empty_table(struct net *net) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci u32 id = 1; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci while (1) { 21162306a36Sopenharmony_ci if (!fib_get_table(net, id)) 21262306a36Sopenharmony_ci return fib_new_table(net, id); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (id++ == RT_TABLE_MAX) 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci return NULL; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 22162306a36Sopenharmony_ci struct fib_rule_hdr *frh, 22262306a36Sopenharmony_ci struct nlattr **tb, 22362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 22662306a36Sopenharmony_ci int err = -EINVAL; 22762306a36Sopenharmony_ci struct fib4_rule *rule4 = (struct fib4_rule *) rule; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (!inet_validate_dscp(frh->tos)) { 23062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 23162306a36Sopenharmony_ci "Invalid dsfield (tos): ECN bits must be 0"); 23262306a36Sopenharmony_ci goto errout; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci /* IPv4 currently doesn't handle high order DSCP bits correctly */ 23562306a36Sopenharmony_ci if (frh->tos & ~IPTOS_TOS_MASK) { 23662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid tos"); 23762306a36Sopenharmony_ci goto errout; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci rule4->dscp = inet_dsfield_to_dscp(frh->tos); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* split local/main if they are not already split */ 24262306a36Sopenharmony_ci err = fib_unmerge(net); 24362306a36Sopenharmony_ci if (err) 24462306a36Sopenharmony_ci goto errout; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (rule->table == RT_TABLE_UNSPEC && !rule->l3mdev) { 24762306a36Sopenharmony_ci if (rule->action == FR_ACT_TO_TBL) { 24862306a36Sopenharmony_ci struct fib_table *table; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci table = fib_empty_table(net); 25162306a36Sopenharmony_ci if (!table) { 25262306a36Sopenharmony_ci err = -ENOBUFS; 25362306a36Sopenharmony_ci goto errout; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci rule->table = table->tb_id; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (frh->src_len) 26162306a36Sopenharmony_ci rule4->src = nla_get_in_addr(tb[FRA_SRC]); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (frh->dst_len) 26462306a36Sopenharmony_ci rule4->dst = nla_get_in_addr(tb[FRA_DST]); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 26762306a36Sopenharmony_ci if (tb[FRA_FLOW]) { 26862306a36Sopenharmony_ci rule4->tclassid = nla_get_u32(tb[FRA_FLOW]); 26962306a36Sopenharmony_ci if (rule4->tclassid) 27062306a36Sopenharmony_ci atomic_inc(&net->ipv4.fib_num_tclassid_users); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci#endif 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (fib_rule_requires_fldissect(rule)) 27562306a36Sopenharmony_ci net->ipv4.fib_rules_require_fldissect++; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci rule4->src_len = frh->src_len; 27862306a36Sopenharmony_ci rule4->srcmask = inet_make_mask(rule4->src_len); 27962306a36Sopenharmony_ci rule4->dst_len = frh->dst_len; 28062306a36Sopenharmony_ci rule4->dstmask = inet_make_mask(rule4->dst_len); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci net->ipv4.fib_has_custom_rules = true; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci err = 0; 28562306a36Sopenharmony_cierrout: 28662306a36Sopenharmony_ci return err; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int fib4_rule_delete(struct fib_rule *rule) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct net *net = rule->fr_net; 29262306a36Sopenharmony_ci int err; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* split local/main if they are not already split */ 29562306a36Sopenharmony_ci err = fib_unmerge(net); 29662306a36Sopenharmony_ci if (err) 29762306a36Sopenharmony_ci goto errout; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 30062306a36Sopenharmony_ci if (((struct fib4_rule *)rule)->tclassid) 30162306a36Sopenharmony_ci atomic_dec(&net->ipv4.fib_num_tclassid_users); 30262306a36Sopenharmony_ci#endif 30362306a36Sopenharmony_ci net->ipv4.fib_has_custom_rules = true; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (net->ipv4.fib_rules_require_fldissect && 30662306a36Sopenharmony_ci fib_rule_requires_fldissect(rule)) 30762306a36Sopenharmony_ci net->ipv4.fib_rules_require_fldissect--; 30862306a36Sopenharmony_cierrout: 30962306a36Sopenharmony_ci return err; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 31362306a36Sopenharmony_ci struct nlattr **tb) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct fib4_rule *rule4 = (struct fib4_rule *) rule; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (frh->src_len && (rule4->src_len != frh->src_len)) 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (frh->dst_len && (rule4->dst_len != frh->dst_len)) 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (frh->tos && inet_dscp_to_dsfield(rule4->dscp) != frh->tos) 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 32762306a36Sopenharmony_ci if (tb[FRA_FLOW] && (rule4->tclassid != nla_get_u32(tb[FRA_FLOW]))) 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci#endif 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (frh->src_len && (rule4->src != nla_get_in_addr(tb[FRA_SRC]))) 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (frh->dst_len && (rule4->dst != nla_get_in_addr(tb[FRA_DST]))) 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return 1; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int fib4_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 34162306a36Sopenharmony_ci struct fib_rule_hdr *frh) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct fib4_rule *rule4 = (struct fib4_rule *) rule; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci frh->dst_len = rule4->dst_len; 34662306a36Sopenharmony_ci frh->src_len = rule4->src_len; 34762306a36Sopenharmony_ci frh->tos = inet_dscp_to_dsfield(rule4->dscp); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if ((rule4->dst_len && 35062306a36Sopenharmony_ci nla_put_in_addr(skb, FRA_DST, rule4->dst)) || 35162306a36Sopenharmony_ci (rule4->src_len && 35262306a36Sopenharmony_ci nla_put_in_addr(skb, FRA_SRC, rule4->src))) 35362306a36Sopenharmony_ci goto nla_put_failure; 35462306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 35562306a36Sopenharmony_ci if (rule4->tclassid && 35662306a36Sopenharmony_ci nla_put_u32(skb, FRA_FLOW, rule4->tclassid)) 35762306a36Sopenharmony_ci goto nla_put_failure; 35862306a36Sopenharmony_ci#endif 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cinla_put_failure: 36262306a36Sopenharmony_ci return -ENOBUFS; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic size_t fib4_rule_nlmsg_payload(struct fib_rule *rule) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci return nla_total_size(4) /* dst */ 36862306a36Sopenharmony_ci + nla_total_size(4) /* src */ 36962306a36Sopenharmony_ci + nla_total_size(4); /* flow */ 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void fib4_rule_flush_cache(struct fib_rules_ops *ops) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci rt_cache_flush(ops->fro_net); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic const struct fib_rules_ops __net_initconst fib4_rules_ops_template = { 37862306a36Sopenharmony_ci .family = AF_INET, 37962306a36Sopenharmony_ci .rule_size = sizeof(struct fib4_rule), 38062306a36Sopenharmony_ci .addr_size = sizeof(u32), 38162306a36Sopenharmony_ci .action = fib4_rule_action, 38262306a36Sopenharmony_ci .suppress = fib4_rule_suppress, 38362306a36Sopenharmony_ci .match = fib4_rule_match, 38462306a36Sopenharmony_ci .configure = fib4_rule_configure, 38562306a36Sopenharmony_ci .delete = fib4_rule_delete, 38662306a36Sopenharmony_ci .compare = fib4_rule_compare, 38762306a36Sopenharmony_ci .fill = fib4_rule_fill, 38862306a36Sopenharmony_ci .nlmsg_payload = fib4_rule_nlmsg_payload, 38962306a36Sopenharmony_ci .flush_cache = fib4_rule_flush_cache, 39062306a36Sopenharmony_ci .nlgroup = RTNLGRP_IPV4_RULE, 39162306a36Sopenharmony_ci .owner = THIS_MODULE, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int fib_default_rules_init(struct fib_rules_ops *ops) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int err; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci err = fib_default_rule_add(ops, 0, RT_TABLE_LOCAL, 0); 39962306a36Sopenharmony_ci if (err < 0) 40062306a36Sopenharmony_ci return err; 40162306a36Sopenharmony_ci err = fib_default_rule_add(ops, 0x7FFE, RT_TABLE_MAIN, 0); 40262306a36Sopenharmony_ci if (err < 0) 40362306a36Sopenharmony_ci return err; 40462306a36Sopenharmony_ci err = fib_default_rule_add(ops, 0x7FFF, RT_TABLE_DEFAULT, 0); 40562306a36Sopenharmony_ci if (err < 0) 40662306a36Sopenharmony_ci return err; 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciint __net_init fib4_rules_init(struct net *net) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci int err; 41362306a36Sopenharmony_ci struct fib_rules_ops *ops; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ops = fib_rules_register(&fib4_rules_ops_template, net); 41662306a36Sopenharmony_ci if (IS_ERR(ops)) 41762306a36Sopenharmony_ci return PTR_ERR(ops); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci err = fib_default_rules_init(ops); 42062306a36Sopenharmony_ci if (err < 0) 42162306a36Sopenharmony_ci goto fail; 42262306a36Sopenharmony_ci net->ipv4.rules_ops = ops; 42362306a36Sopenharmony_ci net->ipv4.fib_has_custom_rules = false; 42462306a36Sopenharmony_ci net->ipv4.fib_rules_require_fldissect = 0; 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cifail: 42862306a36Sopenharmony_ci /* also cleans all rules already added */ 42962306a36Sopenharmony_ci fib_rules_unregister(ops); 43062306a36Sopenharmony_ci return err; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_civoid __net_exit fib4_rules_exit(struct net *net) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci fib_rules_unregister(net->ipv4.rules_ops); 43662306a36Sopenharmony_ci} 437