]> Git Repo - linux.git/blob - drivers/net/ipvlan/ipvlan_l3s.c
Linux 6.14-rc3
[linux.git] / drivers / net / ipvlan / ipvlan_l3s.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Copyright (c) 2014 Mahesh Bandewar <[email protected]>
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 }
This page took 0.045612 seconds and 4 git commands to generate.