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