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