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