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

xfrm6_policy.c (6876B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * xfrm6_policy.c: based on xfrm4_policy.c
      4 *
      5 * Authors:
      6 *	Mitsuru KANDA @USAGI
      7 *	Kazunori MIYAZAWA @USAGI
      8 *	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
      9 *		IPv6 support
     10 *	YOSHIFUJI Hideaki
     11 *		Split up af-specific portion
     12 *
     13 */
     14
     15#include <linux/err.h>
     16#include <linux/kernel.h>
     17#include <linux/netdevice.h>
     18#include <net/addrconf.h>
     19#include <net/dst.h>
     20#include <net/xfrm.h>
     21#include <net/ip.h>
     22#include <net/ipv6.h>
     23#include <net/ip6_route.h>
     24#include <net/l3mdev.h>
     25
     26static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
     27					  const xfrm_address_t *saddr,
     28					  const xfrm_address_t *daddr,
     29					  u32 mark)
     30{
     31	struct flowi6 fl6;
     32	struct dst_entry *dst;
     33	int err;
     34
     35	memset(&fl6, 0, sizeof(fl6));
     36	fl6.flowi6_l3mdev = l3mdev_master_ifindex_by_index(net, oif);
     37	fl6.flowi6_mark = mark;
     38	memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
     39	if (saddr)
     40		memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
     41
     42	dst = ip6_route_output(net, NULL, &fl6);
     43
     44	err = dst->error;
     45	if (dst->error) {
     46		dst_release(dst);
     47		dst = ERR_PTR(err);
     48	}
     49
     50	return dst;
     51}
     52
     53static int xfrm6_get_saddr(struct net *net, int oif,
     54			   xfrm_address_t *saddr, xfrm_address_t *daddr,
     55			   u32 mark)
     56{
     57	struct dst_entry *dst;
     58	struct net_device *dev;
     59
     60	dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark);
     61	if (IS_ERR(dst))
     62		return -EHOSTUNREACH;
     63
     64	dev = ip6_dst_idev(dst)->dev;
     65	ipv6_dev_get_saddr(dev_net(dev), dev, &daddr->in6, 0, &saddr->in6);
     66	dst_release(dst);
     67	return 0;
     68}
     69
     70static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
     71			  const struct flowi *fl)
     72{
     73	struct rt6_info *rt = (struct rt6_info *)xdst->route;
     74
     75	xdst->u.dst.dev = dev;
     76	dev_hold_track(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC);
     77
     78	xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
     79	if (!xdst->u.rt6.rt6i_idev) {
     80		dev_put_track(dev, &xdst->u.dst.dev_tracker);
     81		return -ENODEV;
     82	}
     83
     84	/* Sheit... I remember I did this right. Apparently,
     85	 * it was magically lost, so this code needs audit */
     86	xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
     87						   RTF_LOCAL);
     88	xdst->route_cookie = rt6_get_cookie(rt);
     89	xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
     90	xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
     91	xdst->u.rt6.rt6i_src = rt->rt6i_src;
     92	INIT_LIST_HEAD(&xdst->u.rt6.rt6i_uncached);
     93	rt6_uncached_list_add(&xdst->u.rt6);
     94
     95	return 0;
     96}
     97
     98static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
     99			      struct sk_buff *skb, u32 mtu,
    100			      bool confirm_neigh)
    101{
    102	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
    103	struct dst_entry *path = xdst->route;
    104
    105	path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
    106}
    107
    108static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
    109			   struct sk_buff *skb)
    110{
    111	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
    112	struct dst_entry *path = xdst->route;
    113
    114	path->ops->redirect(path, sk, skb);
    115}
    116
    117static void xfrm6_dst_destroy(struct dst_entry *dst)
    118{
    119	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
    120
    121	if (likely(xdst->u.rt6.rt6i_idev))
    122		in6_dev_put(xdst->u.rt6.rt6i_idev);
    123	dst_destroy_metrics_generic(dst);
    124	if (xdst->u.rt6.rt6i_uncached_list)
    125		rt6_uncached_list_del(&xdst->u.rt6);
    126	xfrm_dst_destroy(xdst);
    127}
    128
    129static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
    130			     int unregister)
    131{
    132	struct xfrm_dst *xdst;
    133
    134	if (!unregister)
    135		return;
    136
    137	xdst = (struct xfrm_dst *)dst;
    138	if (xdst->u.rt6.rt6i_idev->dev == dev) {
    139		struct inet6_dev *loopback_idev =
    140			in6_dev_get(dev_net(dev)->loopback_dev);
    141
    142		do {
    143			in6_dev_put(xdst->u.rt6.rt6i_idev);
    144			xdst->u.rt6.rt6i_idev = loopback_idev;
    145			in6_dev_hold(loopback_idev);
    146			xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst);
    147		} while (xdst->u.dst.xfrm);
    148
    149		__in6_dev_put(loopback_idev);
    150	}
    151
    152	xfrm_dst_ifdown(dst, dev);
    153}
    154
    155static struct dst_ops xfrm6_dst_ops_template = {
    156	.family =		AF_INET6,
    157	.update_pmtu =		xfrm6_update_pmtu,
    158	.redirect =		xfrm6_redirect,
    159	.cow_metrics =		dst_cow_metrics_generic,
    160	.destroy =		xfrm6_dst_destroy,
    161	.ifdown =		xfrm6_dst_ifdown,
    162	.local_out =		__ip6_local_out,
    163	.gc_thresh =		32768,
    164};
    165
    166static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
    167	.dst_ops =		&xfrm6_dst_ops_template,
    168	.dst_lookup =		xfrm6_dst_lookup,
    169	.get_saddr =		xfrm6_get_saddr,
    170	.fill_dst =		xfrm6_fill_dst,
    171	.blackhole_route =	ip6_blackhole_route,
    172};
    173
    174static int __init xfrm6_policy_init(void)
    175{
    176	return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6);
    177}
    178
    179static void xfrm6_policy_fini(void)
    180{
    181	xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
    182}
    183
    184#ifdef CONFIG_SYSCTL
    185static struct ctl_table xfrm6_policy_table[] = {
    186	{
    187		.procname       = "xfrm6_gc_thresh",
    188		.data		= &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
    189		.maxlen		= sizeof(int),
    190		.mode		= 0644,
    191		.proc_handler   = proc_dointvec,
    192	},
    193	{ }
    194};
    195
    196static int __net_init xfrm6_net_sysctl_init(struct net *net)
    197{
    198	struct ctl_table *table;
    199	struct ctl_table_header *hdr;
    200
    201	table = xfrm6_policy_table;
    202	if (!net_eq(net, &init_net)) {
    203		table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
    204		if (!table)
    205			goto err_alloc;
    206
    207		table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
    208	}
    209
    210	hdr = register_net_sysctl(net, "net/ipv6", table);
    211	if (!hdr)
    212		goto err_reg;
    213
    214	net->ipv6.sysctl.xfrm6_hdr = hdr;
    215	return 0;
    216
    217err_reg:
    218	if (!net_eq(net, &init_net))
    219		kfree(table);
    220err_alloc:
    221	return -ENOMEM;
    222}
    223
    224static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
    225{
    226	struct ctl_table *table;
    227
    228	if (!net->ipv6.sysctl.xfrm6_hdr)
    229		return;
    230
    231	table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
    232	unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
    233	if (!net_eq(net, &init_net))
    234		kfree(table);
    235}
    236#else /* CONFIG_SYSCTL */
    237static inline int xfrm6_net_sysctl_init(struct net *net)
    238{
    239	return 0;
    240}
    241
    242static inline void xfrm6_net_sysctl_exit(struct net *net)
    243{
    244}
    245#endif
    246
    247static int __net_init xfrm6_net_init(struct net *net)
    248{
    249	int ret;
    250
    251	memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
    252	       sizeof(xfrm6_dst_ops_template));
    253	ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
    254	if (ret)
    255		return ret;
    256
    257	ret = xfrm6_net_sysctl_init(net);
    258	if (ret)
    259		dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
    260
    261	return ret;
    262}
    263
    264static void __net_exit xfrm6_net_exit(struct net *net)
    265{
    266	xfrm6_net_sysctl_exit(net);
    267	dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
    268}
    269
    270static struct pernet_operations xfrm6_net_ops = {
    271	.init	= xfrm6_net_init,
    272	.exit	= xfrm6_net_exit,
    273};
    274
    275int __init xfrm6_init(void)
    276{
    277	int ret;
    278
    279	ret = xfrm6_policy_init();
    280	if (ret)
    281		goto out;
    282	ret = xfrm6_state_init();
    283	if (ret)
    284		goto out_policy;
    285
    286	ret = xfrm6_protocol_init();
    287	if (ret)
    288		goto out_state;
    289
    290	register_pernet_subsys(&xfrm6_net_ops);
    291out:
    292	return ret;
    293out_state:
    294	xfrm6_state_fini();
    295out_policy:
    296	xfrm6_policy_fini();
    297	goto out;
    298}
    299
    300void xfrm6_fini(void)
    301{
    302	unregister_pernet_subsys(&xfrm6_net_ops);
    303	xfrm6_protocol_fini();
    304	xfrm6_policy_fini();
    305	xfrm6_state_fini();
    306}