cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fib6_rules.c (12611B)


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