brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · d5b05e8 Raw
229 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>3 */4 5#include "ipvlan.h"6 7static unsigned int ipvlan_netid __read_mostly;8 9struct ipvlan_netns {10	unsigned int ipvl_nf_hook_refcnt;11};12 13static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb,14					    struct net_device *dev)15{16	struct ipvl_addr *addr = NULL;17	struct ipvl_port *port;18	int addr_type;19	void *lyr3h;20 21	if (!dev || !netif_is_ipvlan_port(dev))22		goto out;23 24	port = ipvlan_port_get_rcu(dev);25	if (!port || port->mode != IPVLAN_MODE_L3S)26		goto out;27 28	lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);29	if (!lyr3h)30		goto out;31 32	addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);33out:34	return addr;35}36 37static struct sk_buff *ipvlan_l3_rcv(struct net_device *dev,38				     struct sk_buff *skb, u16 proto)39{40	struct ipvl_addr *addr;41	struct net_device *sdev;42 43	addr = ipvlan_skb_to_addr(skb, dev);44	if (!addr)45		goto out;46 47	sdev = addr->master->dev;48	switch (proto) {49	case AF_INET:50	{51		struct iphdr *ip4h = ip_hdr(skb);52		int err;53 54		err = ip_route_input_noref(skb, ip4h->daddr, ip4h->saddr,55					   ip4h->tos, sdev);56		if (unlikely(err))57			goto out;58		break;59	}60#if IS_ENABLED(CONFIG_IPV6)61	case AF_INET6:62	{63		struct dst_entry *dst;64		struct ipv6hdr *ip6h = ipv6_hdr(skb);65		int flags = RT6_LOOKUP_F_HAS_SADDR;66		struct flowi6 fl6 = {67			.flowi6_iif   = sdev->ifindex,68			.daddr        = ip6h->daddr,69			.saddr        = ip6h->saddr,70			.flowlabel    = ip6_flowinfo(ip6h),71			.flowi6_mark  = skb->mark,72			.flowi6_proto = ip6h->nexthdr,73		};74 75		skb_dst_drop(skb);76		dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6,77					     skb, flags);78		skb_dst_set(skb, dst);79		break;80	}81#endif82	default:83		break;84	}85out:86	return skb;87}88 89static const struct l3mdev_ops ipvl_l3mdev_ops = {90	.l3mdev_l3_rcv = ipvlan_l3_rcv,91};92 93static unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,94				    const struct nf_hook_state *state)95{96	struct ipvl_addr *addr;97	unsigned int len;98 99	addr = ipvlan_skb_to_addr(skb, skb->dev);100	if (!addr)101		goto out;102 103	skb->dev = addr->master->dev;104	skb->skb_iif = skb->dev->ifindex;105#if IS_ENABLED(CONFIG_IPV6)106	if (addr->atype == IPVL_IPV6)107		IP6CB(skb)->iif = skb->dev->ifindex;108#endif109	len = skb->len + ETH_HLEN;110	ipvlan_count_rx(addr->master, len, true, false);111out:112	return NF_ACCEPT;113}114 115static const struct nf_hook_ops ipvl_nfops[] = {116	{117		.hook     = ipvlan_nf_input,118		.pf       = NFPROTO_IPV4,119		.hooknum  = NF_INET_LOCAL_IN,120		.priority = INT_MAX,121	},122#if IS_ENABLED(CONFIG_IPV6)123	{124		.hook     = ipvlan_nf_input,125		.pf       = NFPROTO_IPV6,126		.hooknum  = NF_INET_LOCAL_IN,127		.priority = INT_MAX,128	},129#endif130};131 132static int ipvlan_register_nf_hook(struct net *net)133{134	struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);135	int err = 0;136 137	if (!vnet->ipvl_nf_hook_refcnt) {138		err = nf_register_net_hooks(net, ipvl_nfops,139					    ARRAY_SIZE(ipvl_nfops));140		if (!err)141			vnet->ipvl_nf_hook_refcnt = 1;142	} else {143		vnet->ipvl_nf_hook_refcnt++;144	}145 146	return err;147}148 149static void ipvlan_unregister_nf_hook(struct net *net)150{151	struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);152 153	if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))154		return;155 156	vnet->ipvl_nf_hook_refcnt--;157	if (!vnet->ipvl_nf_hook_refcnt)158		nf_unregister_net_hooks(net, ipvl_nfops,159					ARRAY_SIZE(ipvl_nfops));160}161 162void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet)163{164	struct ipvlan_netns *old_vnet;165 166	ASSERT_RTNL();167 168	old_vnet = net_generic(oldnet, ipvlan_netid);169	if (!old_vnet->ipvl_nf_hook_refcnt)170		return;171 172	ipvlan_register_nf_hook(newnet);173	ipvlan_unregister_nf_hook(oldnet);174}175 176static void ipvlan_ns_exit(struct net *net)177{178	struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);179 180	if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {181		vnet->ipvl_nf_hook_refcnt = 0;182		nf_unregister_net_hooks(net, ipvl_nfops,183					ARRAY_SIZE(ipvl_nfops));184	}185}186 187static struct pernet_operations ipvlan_net_ops = {188	.id   = &ipvlan_netid,189	.size = sizeof(struct ipvlan_netns),190	.exit = ipvlan_ns_exit,191};192 193int ipvlan_l3s_init(void)194{195	return register_pernet_subsys(&ipvlan_net_ops);196}197 198void ipvlan_l3s_cleanup(void)199{200	unregister_pernet_subsys(&ipvlan_net_ops);201}202 203int ipvlan_l3s_register(struct ipvl_port *port)204{205	struct net_device *dev = port->dev;206	int ret;207 208	ASSERT_RTNL();209 210	ret = ipvlan_register_nf_hook(read_pnet(&port->pnet));211	if (!ret) {212		dev->l3mdev_ops = &ipvl_l3mdev_ops;213		dev->priv_flags |= IFF_L3MDEV_RX_HANDLER;214	}215 216	return ret;217}218 219void ipvlan_l3s_unregister(struct ipvl_port *port)220{221	struct net_device *dev = port->dev;222 223	ASSERT_RTNL();224 225	dev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER;226	ipvlan_unregister_nf_hook(read_pnet(&port->pnet));227	dev->l3mdev_ops = NULL;228}229