xref: /openbmc/linux/drivers/net/ipvlan/ipvlan_l3s.c (revision a347279d)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
3  */
4 
5 #include "ipvlan.h"
6 
7 static unsigned int ipvlan_netid __read_mostly;
8 
9 struct ipvlan_netns {
10 	unsigned int ipvl_nf_hook_refcnt;
11 };
12 
13 static 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);
33 out:
34 	return addr;
35 }
36 
37 static 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 #endif
82 	default:
83 		break;
84 	}
85 out:
86 	return skb;
87 }
88 
89 static const struct l3mdev_ops ipvl_l3mdev_ops = {
90 	.l3mdev_l3_rcv = ipvlan_l3_rcv,
91 };
92 
93 static 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 	len = skb->len + ETH_HLEN;
106 	ipvlan_count_rx(addr->master, len, true, false);
107 out:
108 	return NF_ACCEPT;
109 }
110 
111 static const struct nf_hook_ops ipvl_nfops[] = {
112 	{
113 		.hook     = ipvlan_nf_input,
114 		.pf       = NFPROTO_IPV4,
115 		.hooknum  = NF_INET_LOCAL_IN,
116 		.priority = INT_MAX,
117 	},
118 #if IS_ENABLED(CONFIG_IPV6)
119 	{
120 		.hook     = ipvlan_nf_input,
121 		.pf       = NFPROTO_IPV6,
122 		.hooknum  = NF_INET_LOCAL_IN,
123 		.priority = INT_MAX,
124 	},
125 #endif
126 };
127 
128 static int ipvlan_register_nf_hook(struct net *net)
129 {
130 	struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
131 	int err = 0;
132 
133 	if (!vnet->ipvl_nf_hook_refcnt) {
134 		err = nf_register_net_hooks(net, ipvl_nfops,
135 					    ARRAY_SIZE(ipvl_nfops));
136 		if (!err)
137 			vnet->ipvl_nf_hook_refcnt = 1;
138 	} else {
139 		vnet->ipvl_nf_hook_refcnt++;
140 	}
141 
142 	return err;
143 }
144 
145 static void ipvlan_unregister_nf_hook(struct net *net)
146 {
147 	struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
148 
149 	if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))
150 		return;
151 
152 	vnet->ipvl_nf_hook_refcnt--;
153 	if (!vnet->ipvl_nf_hook_refcnt)
154 		nf_unregister_net_hooks(net, ipvl_nfops,
155 					ARRAY_SIZE(ipvl_nfops));
156 }
157 
158 void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet)
159 {
160 	struct ipvlan_netns *old_vnet;
161 
162 	ASSERT_RTNL();
163 
164 	old_vnet = net_generic(oldnet, ipvlan_netid);
165 	if (!old_vnet->ipvl_nf_hook_refcnt)
166 		return;
167 
168 	ipvlan_register_nf_hook(newnet);
169 	ipvlan_unregister_nf_hook(oldnet);
170 }
171 
172 static void ipvlan_ns_exit(struct net *net)
173 {
174 	struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
175 
176 	if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {
177 		vnet->ipvl_nf_hook_refcnt = 0;
178 		nf_unregister_net_hooks(net, ipvl_nfops,
179 					ARRAY_SIZE(ipvl_nfops));
180 	}
181 }
182 
183 static struct pernet_operations ipvlan_net_ops = {
184 	.id   = &ipvlan_netid,
185 	.size = sizeof(struct ipvlan_netns),
186 	.exit = ipvlan_ns_exit,
187 };
188 
189 int ipvlan_l3s_init(void)
190 {
191 	return register_pernet_subsys(&ipvlan_net_ops);
192 }
193 
194 void ipvlan_l3s_cleanup(void)
195 {
196 	unregister_pernet_subsys(&ipvlan_net_ops);
197 }
198 
199 int ipvlan_l3s_register(struct ipvl_port *port)
200 {
201 	struct net_device *dev = port->dev;
202 	int ret;
203 
204 	ASSERT_RTNL();
205 
206 	ret = ipvlan_register_nf_hook(read_pnet(&port->pnet));
207 	if (!ret) {
208 		dev->l3mdev_ops = &ipvl_l3mdev_ops;
209 		dev->priv_flags |= IFF_L3MDEV_RX_HANDLER;
210 	}
211 
212 	return ret;
213 }
214 
215 void ipvlan_l3s_unregister(struct ipvl_port *port)
216 {
217 	struct net_device *dev = port->dev;
218 
219 	ASSERT_RTNL();
220 
221 	dev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER;
222 	ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
223 	dev->l3mdev_ops = NULL;
224 }
225