GNU Linux-libre 5.10.153-gnu1
[releases.git] / drivers / net / ipvlan / ipvlan_l3s.c
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         len = skb->len + ETH_HLEN;
105         ipvlan_count_rx(addr->master, len, true, false);
106 out:
107         return NF_ACCEPT;
108 }
109
110 static const struct nf_hook_ops ipvl_nfops[] = {
111         {
112                 .hook     = ipvlan_nf_input,
113                 .pf       = NFPROTO_IPV4,
114                 .hooknum  = NF_INET_LOCAL_IN,
115                 .priority = INT_MAX,
116         },
117 #if IS_ENABLED(CONFIG_IPV6)
118         {
119                 .hook     = ipvlan_nf_input,
120                 .pf       = NFPROTO_IPV6,
121                 .hooknum  = NF_INET_LOCAL_IN,
122                 .priority = INT_MAX,
123         },
124 #endif
125 };
126
127 static int ipvlan_register_nf_hook(struct net *net)
128 {
129         struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
130         int err = 0;
131
132         if (!vnet->ipvl_nf_hook_refcnt) {
133                 err = nf_register_net_hooks(net, ipvl_nfops,
134                                             ARRAY_SIZE(ipvl_nfops));
135                 if (!err)
136                         vnet->ipvl_nf_hook_refcnt = 1;
137         } else {
138                 vnet->ipvl_nf_hook_refcnt++;
139         }
140
141         return err;
142 }
143
144 static void ipvlan_unregister_nf_hook(struct net *net)
145 {
146         struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
147
148         if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))
149                 return;
150
151         vnet->ipvl_nf_hook_refcnt--;
152         if (!vnet->ipvl_nf_hook_refcnt)
153                 nf_unregister_net_hooks(net, ipvl_nfops,
154                                         ARRAY_SIZE(ipvl_nfops));
155 }
156
157 void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet)
158 {
159         struct ipvlan_netns *old_vnet;
160
161         ASSERT_RTNL();
162
163         old_vnet = net_generic(oldnet, ipvlan_netid);
164         if (!old_vnet->ipvl_nf_hook_refcnt)
165                 return;
166
167         ipvlan_register_nf_hook(newnet);
168         ipvlan_unregister_nf_hook(oldnet);
169 }
170
171 static void ipvlan_ns_exit(struct net *net)
172 {
173         struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
174
175         if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {
176                 vnet->ipvl_nf_hook_refcnt = 0;
177                 nf_unregister_net_hooks(net, ipvl_nfops,
178                                         ARRAY_SIZE(ipvl_nfops));
179         }
180 }
181
182 static struct pernet_operations ipvlan_net_ops = {
183         .id   = &ipvlan_netid,
184         .size = sizeof(struct ipvlan_netns),
185         .exit = ipvlan_ns_exit,
186 };
187
188 int ipvlan_l3s_init(void)
189 {
190         return register_pernet_subsys(&ipvlan_net_ops);
191 }
192
193 void ipvlan_l3s_cleanup(void)
194 {
195         unregister_pernet_subsys(&ipvlan_net_ops);
196 }
197
198 int ipvlan_l3s_register(struct ipvl_port *port)
199 {
200         struct net_device *dev = port->dev;
201         int ret;
202
203         ASSERT_RTNL();
204
205         ret = ipvlan_register_nf_hook(read_pnet(&port->pnet));
206         if (!ret) {
207                 dev->l3mdev_ops = &ipvl_l3mdev_ops;
208                 dev->priv_flags |= IFF_L3MDEV_RX_HANDLER;
209         }
210
211         return ret;
212 }
213
214 void ipvlan_l3s_unregister(struct ipvl_port *port)
215 {
216         struct net_device *dev = port->dev;
217
218         ASSERT_RTNL();
219
220         dev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER;
221         ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
222         dev->l3mdev_ops = NULL;
223 }