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