Loading...
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
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#endif
82 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#endif
109 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#endif
130};
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}
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
9static unsigned int ipvlan_netid __read_mostly;
10
11struct ipvlan_netns {
12 unsigned int ipvl_nf_hook_refcnt;
13};
14
15static 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);
35out:
36 return addr;
37}
38
39static 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 }
87out:
88 return skb;
89}
90
91static const struct l3mdev_ops ipvl_l3mdev_ops = {
92 .l3mdev_l3_rcv = ipvlan_l3_rcv,
93};
94
95static 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);
113out:
114 return NF_ACCEPT;
115}
116
117static 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
134static 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
151static 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
164void 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
178static 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
189static struct pernet_operations ipvlan_net_ops = {
190 .id = &ipvlan_netid,
191 .size = sizeof(struct ipvlan_netns),
192 .exit = ipvlan_ns_exit,
193};
194
195int ipvlan_l3s_init(void)
196{
197 return register_pernet_subsys(&ipvlan_net_ops);
198}
199
200void ipvlan_l3s_cleanup(void)
201{
202 unregister_pernet_subsys(&ipvlan_net_ops);
203}
204
205int 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
221void 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}