Commit 789336360e0a2aeb9750c16ab704a02cbe035e9e
Committed by
David S. Miller
1 parent
e3c9851278
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
l2tp: fix oops in l2tp_eth_create() error path
When creating an L2TPv3 Ethernet session, if register_netdev() should fail for any reason (for example, automatic naming for "l2tpeth%d" interfaces hits the 32k-interface limit), the netdev is freed in the error path. However, the l2tp_eth_sess structure's dev pointer is left uncleared, and this results in l2tp_eth_delete() then attempting to unregister the same netdev later in the session teardown. This results in an oops. To avoid this, clear the session dev pointer in the error path. Signed-off-by: Tom Parkin <tparkin@katalix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 1 changed file with 1 additions and 0 deletions Inline Diff
net/l2tp/l2tp_eth.c
1 | /* | 1 | /* |
2 | * L2TPv3 ethernet pseudowire driver | 2 | * L2TPv3 ethernet pseudowire driver |
3 | * | 3 | * |
4 | * Copyright (c) 2008,2009,2010 Katalix Systems Ltd | 4 | * Copyright (c) 2008,2009,2010 Katalix Systems Ltd |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License | 7 | * modify it under the terms of the GNU General Public License |
8 | * as published by the Free Software Foundation; either version | 8 | * as published by the Free Software Foundation; either version |
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
13 | 13 | ||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/skbuff.h> | 15 | #include <linux/skbuff.h> |
16 | #include <linux/socket.h> | 16 | #include <linux/socket.h> |
17 | #include <linux/hash.h> | 17 | #include <linux/hash.h> |
18 | #include <linux/l2tp.h> | 18 | #include <linux/l2tp.h> |
19 | #include <linux/in.h> | 19 | #include <linux/in.h> |
20 | #include <linux/etherdevice.h> | 20 | #include <linux/etherdevice.h> |
21 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
22 | #include <net/sock.h> | 22 | #include <net/sock.h> |
23 | #include <net/ip.h> | 23 | #include <net/ip.h> |
24 | #include <net/icmp.h> | 24 | #include <net/icmp.h> |
25 | #include <net/udp.h> | 25 | #include <net/udp.h> |
26 | #include <net/inet_common.h> | 26 | #include <net/inet_common.h> |
27 | #include <net/inet_hashtables.h> | 27 | #include <net/inet_hashtables.h> |
28 | #include <net/tcp_states.h> | 28 | #include <net/tcp_states.h> |
29 | #include <net/protocol.h> | 29 | #include <net/protocol.h> |
30 | #include <net/xfrm.h> | 30 | #include <net/xfrm.h> |
31 | #include <net/net_namespace.h> | 31 | #include <net/net_namespace.h> |
32 | #include <net/netns/generic.h> | 32 | #include <net/netns/generic.h> |
33 | 33 | ||
34 | #include "l2tp_core.h" | 34 | #include "l2tp_core.h" |
35 | 35 | ||
36 | /* Default device name. May be overridden by name specified by user */ | 36 | /* Default device name. May be overridden by name specified by user */ |
37 | #define L2TP_ETH_DEV_NAME "l2tpeth%d" | 37 | #define L2TP_ETH_DEV_NAME "l2tpeth%d" |
38 | 38 | ||
39 | /* via netdev_priv() */ | 39 | /* via netdev_priv() */ |
40 | struct l2tp_eth { | 40 | struct l2tp_eth { |
41 | struct net_device *dev; | 41 | struct net_device *dev; |
42 | struct sock *tunnel_sock; | 42 | struct sock *tunnel_sock; |
43 | struct l2tp_session *session; | 43 | struct l2tp_session *session; |
44 | struct list_head list; | 44 | struct list_head list; |
45 | atomic_long_t tx_bytes; | 45 | atomic_long_t tx_bytes; |
46 | atomic_long_t tx_packets; | 46 | atomic_long_t tx_packets; |
47 | atomic_long_t tx_dropped; | 47 | atomic_long_t tx_dropped; |
48 | atomic_long_t rx_bytes; | 48 | atomic_long_t rx_bytes; |
49 | atomic_long_t rx_packets; | 49 | atomic_long_t rx_packets; |
50 | atomic_long_t rx_errors; | 50 | atomic_long_t rx_errors; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | /* via l2tp_session_priv() */ | 53 | /* via l2tp_session_priv() */ |
54 | struct l2tp_eth_sess { | 54 | struct l2tp_eth_sess { |
55 | struct net_device *dev; | 55 | struct net_device *dev; |
56 | }; | 56 | }; |
57 | 57 | ||
58 | /* per-net private data for this module */ | 58 | /* per-net private data for this module */ |
59 | static unsigned int l2tp_eth_net_id; | 59 | static unsigned int l2tp_eth_net_id; |
60 | struct l2tp_eth_net { | 60 | struct l2tp_eth_net { |
61 | struct list_head l2tp_eth_dev_list; | 61 | struct list_head l2tp_eth_dev_list; |
62 | spinlock_t l2tp_eth_lock; | 62 | spinlock_t l2tp_eth_lock; |
63 | }; | 63 | }; |
64 | 64 | ||
65 | static inline struct l2tp_eth_net *l2tp_eth_pernet(struct net *net) | 65 | static inline struct l2tp_eth_net *l2tp_eth_pernet(struct net *net) |
66 | { | 66 | { |
67 | return net_generic(net, l2tp_eth_net_id); | 67 | return net_generic(net, l2tp_eth_net_id); |
68 | } | 68 | } |
69 | 69 | ||
70 | static struct lock_class_key l2tp_eth_tx_busylock; | 70 | static struct lock_class_key l2tp_eth_tx_busylock; |
71 | static int l2tp_eth_dev_init(struct net_device *dev) | 71 | static int l2tp_eth_dev_init(struct net_device *dev) |
72 | { | 72 | { |
73 | struct l2tp_eth *priv = netdev_priv(dev); | 73 | struct l2tp_eth *priv = netdev_priv(dev); |
74 | 74 | ||
75 | priv->dev = dev; | 75 | priv->dev = dev; |
76 | eth_hw_addr_random(dev); | 76 | eth_hw_addr_random(dev); |
77 | memset(&dev->broadcast[0], 0xff, 6); | 77 | memset(&dev->broadcast[0], 0xff, 6); |
78 | dev->qdisc_tx_busylock = &l2tp_eth_tx_busylock; | 78 | dev->qdisc_tx_busylock = &l2tp_eth_tx_busylock; |
79 | return 0; | 79 | return 0; |
80 | } | 80 | } |
81 | 81 | ||
82 | static void l2tp_eth_dev_uninit(struct net_device *dev) | 82 | static void l2tp_eth_dev_uninit(struct net_device *dev) |
83 | { | 83 | { |
84 | struct l2tp_eth *priv = netdev_priv(dev); | 84 | struct l2tp_eth *priv = netdev_priv(dev); |
85 | struct l2tp_eth_net *pn = l2tp_eth_pernet(dev_net(dev)); | 85 | struct l2tp_eth_net *pn = l2tp_eth_pernet(dev_net(dev)); |
86 | 86 | ||
87 | spin_lock(&pn->l2tp_eth_lock); | 87 | spin_lock(&pn->l2tp_eth_lock); |
88 | list_del_init(&priv->list); | 88 | list_del_init(&priv->list); |
89 | spin_unlock(&pn->l2tp_eth_lock); | 89 | spin_unlock(&pn->l2tp_eth_lock); |
90 | dev_put(dev); | 90 | dev_put(dev); |
91 | } | 91 | } |
92 | 92 | ||
93 | static int l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev) | 93 | static int l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev) |
94 | { | 94 | { |
95 | struct l2tp_eth *priv = netdev_priv(dev); | 95 | struct l2tp_eth *priv = netdev_priv(dev); |
96 | struct l2tp_session *session = priv->session; | 96 | struct l2tp_session *session = priv->session; |
97 | unsigned int len = skb->len; | 97 | unsigned int len = skb->len; |
98 | int ret = l2tp_xmit_skb(session, skb, session->hdr_len); | 98 | int ret = l2tp_xmit_skb(session, skb, session->hdr_len); |
99 | 99 | ||
100 | if (likely(ret == NET_XMIT_SUCCESS)) { | 100 | if (likely(ret == NET_XMIT_SUCCESS)) { |
101 | atomic_long_add(len, &priv->tx_bytes); | 101 | atomic_long_add(len, &priv->tx_bytes); |
102 | atomic_long_inc(&priv->tx_packets); | 102 | atomic_long_inc(&priv->tx_packets); |
103 | } else { | 103 | } else { |
104 | atomic_long_inc(&priv->tx_dropped); | 104 | atomic_long_inc(&priv->tx_dropped); |
105 | } | 105 | } |
106 | return NETDEV_TX_OK; | 106 | return NETDEV_TX_OK; |
107 | } | 107 | } |
108 | 108 | ||
109 | static struct rtnl_link_stats64 *l2tp_eth_get_stats64(struct net_device *dev, | 109 | static struct rtnl_link_stats64 *l2tp_eth_get_stats64(struct net_device *dev, |
110 | struct rtnl_link_stats64 *stats) | 110 | struct rtnl_link_stats64 *stats) |
111 | { | 111 | { |
112 | struct l2tp_eth *priv = netdev_priv(dev); | 112 | struct l2tp_eth *priv = netdev_priv(dev); |
113 | 113 | ||
114 | stats->tx_bytes = atomic_long_read(&priv->tx_bytes); | 114 | stats->tx_bytes = atomic_long_read(&priv->tx_bytes); |
115 | stats->tx_packets = atomic_long_read(&priv->tx_packets); | 115 | stats->tx_packets = atomic_long_read(&priv->tx_packets); |
116 | stats->tx_dropped = atomic_long_read(&priv->tx_dropped); | 116 | stats->tx_dropped = atomic_long_read(&priv->tx_dropped); |
117 | stats->rx_bytes = atomic_long_read(&priv->rx_bytes); | 117 | stats->rx_bytes = atomic_long_read(&priv->rx_bytes); |
118 | stats->rx_packets = atomic_long_read(&priv->rx_packets); | 118 | stats->rx_packets = atomic_long_read(&priv->rx_packets); |
119 | stats->rx_errors = atomic_long_read(&priv->rx_errors); | 119 | stats->rx_errors = atomic_long_read(&priv->rx_errors); |
120 | return stats; | 120 | return stats; |
121 | } | 121 | } |
122 | 122 | ||
123 | 123 | ||
124 | static struct net_device_ops l2tp_eth_netdev_ops = { | 124 | static struct net_device_ops l2tp_eth_netdev_ops = { |
125 | .ndo_init = l2tp_eth_dev_init, | 125 | .ndo_init = l2tp_eth_dev_init, |
126 | .ndo_uninit = l2tp_eth_dev_uninit, | 126 | .ndo_uninit = l2tp_eth_dev_uninit, |
127 | .ndo_start_xmit = l2tp_eth_dev_xmit, | 127 | .ndo_start_xmit = l2tp_eth_dev_xmit, |
128 | .ndo_get_stats64 = l2tp_eth_get_stats64, | 128 | .ndo_get_stats64 = l2tp_eth_get_stats64, |
129 | }; | 129 | }; |
130 | 130 | ||
131 | static void l2tp_eth_dev_setup(struct net_device *dev) | 131 | static void l2tp_eth_dev_setup(struct net_device *dev) |
132 | { | 132 | { |
133 | ether_setup(dev); | 133 | ether_setup(dev); |
134 | dev->priv_flags &= ~IFF_TX_SKB_SHARING; | 134 | dev->priv_flags &= ~IFF_TX_SKB_SHARING; |
135 | dev->features |= NETIF_F_LLTX; | 135 | dev->features |= NETIF_F_LLTX; |
136 | dev->netdev_ops = &l2tp_eth_netdev_ops; | 136 | dev->netdev_ops = &l2tp_eth_netdev_ops; |
137 | dev->destructor = free_netdev; | 137 | dev->destructor = free_netdev; |
138 | } | 138 | } |
139 | 139 | ||
140 | static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len) | 140 | static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len) |
141 | { | 141 | { |
142 | struct l2tp_eth_sess *spriv = l2tp_session_priv(session); | 142 | struct l2tp_eth_sess *spriv = l2tp_session_priv(session); |
143 | struct net_device *dev = spriv->dev; | 143 | struct net_device *dev = spriv->dev; |
144 | struct l2tp_eth *priv = netdev_priv(dev); | 144 | struct l2tp_eth *priv = netdev_priv(dev); |
145 | 145 | ||
146 | if (session->debug & L2TP_MSG_DATA) { | 146 | if (session->debug & L2TP_MSG_DATA) { |
147 | unsigned int length; | 147 | unsigned int length; |
148 | 148 | ||
149 | length = min(32u, skb->len); | 149 | length = min(32u, skb->len); |
150 | if (!pskb_may_pull(skb, length)) | 150 | if (!pskb_may_pull(skb, length)) |
151 | goto error; | 151 | goto error; |
152 | 152 | ||
153 | pr_debug("%s: eth recv\n", session->name); | 153 | pr_debug("%s: eth recv\n", session->name); |
154 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, length); | 154 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, length); |
155 | } | 155 | } |
156 | 156 | ||
157 | if (!pskb_may_pull(skb, ETH_HLEN)) | 157 | if (!pskb_may_pull(skb, ETH_HLEN)) |
158 | goto error; | 158 | goto error; |
159 | 159 | ||
160 | secpath_reset(skb); | 160 | secpath_reset(skb); |
161 | 161 | ||
162 | /* checksums verified by L2TP */ | 162 | /* checksums verified by L2TP */ |
163 | skb->ip_summed = CHECKSUM_NONE; | 163 | skb->ip_summed = CHECKSUM_NONE; |
164 | 164 | ||
165 | skb_dst_drop(skb); | 165 | skb_dst_drop(skb); |
166 | nf_reset(skb); | 166 | nf_reset(skb); |
167 | 167 | ||
168 | if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS) { | 168 | if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS) { |
169 | atomic_long_inc(&priv->rx_packets); | 169 | atomic_long_inc(&priv->rx_packets); |
170 | atomic_long_add(data_len, &priv->rx_bytes); | 170 | atomic_long_add(data_len, &priv->rx_bytes); |
171 | } else { | 171 | } else { |
172 | atomic_long_inc(&priv->rx_errors); | 172 | atomic_long_inc(&priv->rx_errors); |
173 | } | 173 | } |
174 | return; | 174 | return; |
175 | 175 | ||
176 | error: | 176 | error: |
177 | atomic_long_inc(&priv->rx_errors); | 177 | atomic_long_inc(&priv->rx_errors); |
178 | kfree_skb(skb); | 178 | kfree_skb(skb); |
179 | } | 179 | } |
180 | 180 | ||
181 | static void l2tp_eth_delete(struct l2tp_session *session) | 181 | static void l2tp_eth_delete(struct l2tp_session *session) |
182 | { | 182 | { |
183 | struct l2tp_eth_sess *spriv; | 183 | struct l2tp_eth_sess *spriv; |
184 | struct net_device *dev; | 184 | struct net_device *dev; |
185 | 185 | ||
186 | if (session) { | 186 | if (session) { |
187 | spriv = l2tp_session_priv(session); | 187 | spriv = l2tp_session_priv(session); |
188 | dev = spriv->dev; | 188 | dev = spriv->dev; |
189 | if (dev) { | 189 | if (dev) { |
190 | unregister_netdev(dev); | 190 | unregister_netdev(dev); |
191 | spriv->dev = NULL; | 191 | spriv->dev = NULL; |
192 | module_put(THIS_MODULE); | 192 | module_put(THIS_MODULE); |
193 | } | 193 | } |
194 | } | 194 | } |
195 | } | 195 | } |
196 | 196 | ||
197 | #if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE) | 197 | #if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE) |
198 | static void l2tp_eth_show(struct seq_file *m, void *arg) | 198 | static void l2tp_eth_show(struct seq_file *m, void *arg) |
199 | { | 199 | { |
200 | struct l2tp_session *session = arg; | 200 | struct l2tp_session *session = arg; |
201 | struct l2tp_eth_sess *spriv = l2tp_session_priv(session); | 201 | struct l2tp_eth_sess *spriv = l2tp_session_priv(session); |
202 | struct net_device *dev = spriv->dev; | 202 | struct net_device *dev = spriv->dev; |
203 | 203 | ||
204 | seq_printf(m, " interface %s\n", dev->name); | 204 | seq_printf(m, " interface %s\n", dev->name); |
205 | } | 205 | } |
206 | #endif | 206 | #endif |
207 | 207 | ||
208 | static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) | 208 | static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) |
209 | { | 209 | { |
210 | struct net_device *dev; | 210 | struct net_device *dev; |
211 | char name[IFNAMSIZ]; | 211 | char name[IFNAMSIZ]; |
212 | struct l2tp_tunnel *tunnel; | 212 | struct l2tp_tunnel *tunnel; |
213 | struct l2tp_session *session; | 213 | struct l2tp_session *session; |
214 | struct l2tp_eth *priv; | 214 | struct l2tp_eth *priv; |
215 | struct l2tp_eth_sess *spriv; | 215 | struct l2tp_eth_sess *spriv; |
216 | int rc; | 216 | int rc; |
217 | struct l2tp_eth_net *pn; | 217 | struct l2tp_eth_net *pn; |
218 | 218 | ||
219 | tunnel = l2tp_tunnel_find(net, tunnel_id); | 219 | tunnel = l2tp_tunnel_find(net, tunnel_id); |
220 | if (!tunnel) { | 220 | if (!tunnel) { |
221 | rc = -ENODEV; | 221 | rc = -ENODEV; |
222 | goto out; | 222 | goto out; |
223 | } | 223 | } |
224 | 224 | ||
225 | session = l2tp_session_find(net, tunnel, session_id); | 225 | session = l2tp_session_find(net, tunnel, session_id); |
226 | if (session) { | 226 | if (session) { |
227 | rc = -EEXIST; | 227 | rc = -EEXIST; |
228 | goto out; | 228 | goto out; |
229 | } | 229 | } |
230 | 230 | ||
231 | if (cfg->ifname) { | 231 | if (cfg->ifname) { |
232 | dev = dev_get_by_name(net, cfg->ifname); | 232 | dev = dev_get_by_name(net, cfg->ifname); |
233 | if (dev) { | 233 | if (dev) { |
234 | dev_put(dev); | 234 | dev_put(dev); |
235 | rc = -EEXIST; | 235 | rc = -EEXIST; |
236 | goto out; | 236 | goto out; |
237 | } | 237 | } |
238 | strlcpy(name, cfg->ifname, IFNAMSIZ); | 238 | strlcpy(name, cfg->ifname, IFNAMSIZ); |
239 | } else | 239 | } else |
240 | strcpy(name, L2TP_ETH_DEV_NAME); | 240 | strcpy(name, L2TP_ETH_DEV_NAME); |
241 | 241 | ||
242 | session = l2tp_session_create(sizeof(*spriv), tunnel, session_id, | 242 | session = l2tp_session_create(sizeof(*spriv), tunnel, session_id, |
243 | peer_session_id, cfg); | 243 | peer_session_id, cfg); |
244 | if (!session) { | 244 | if (!session) { |
245 | rc = -ENOMEM; | 245 | rc = -ENOMEM; |
246 | goto out; | 246 | goto out; |
247 | } | 247 | } |
248 | 248 | ||
249 | dev = alloc_netdev(sizeof(*priv), name, l2tp_eth_dev_setup); | 249 | dev = alloc_netdev(sizeof(*priv), name, l2tp_eth_dev_setup); |
250 | if (!dev) { | 250 | if (!dev) { |
251 | rc = -ENOMEM; | 251 | rc = -ENOMEM; |
252 | goto out_del_session; | 252 | goto out_del_session; |
253 | } | 253 | } |
254 | 254 | ||
255 | dev_net_set(dev, net); | 255 | dev_net_set(dev, net); |
256 | if (session->mtu == 0) | 256 | if (session->mtu == 0) |
257 | session->mtu = dev->mtu - session->hdr_len; | 257 | session->mtu = dev->mtu - session->hdr_len; |
258 | dev->mtu = session->mtu; | 258 | dev->mtu = session->mtu; |
259 | dev->needed_headroom += session->hdr_len; | 259 | dev->needed_headroom += session->hdr_len; |
260 | 260 | ||
261 | priv = netdev_priv(dev); | 261 | priv = netdev_priv(dev); |
262 | priv->dev = dev; | 262 | priv->dev = dev; |
263 | priv->session = session; | 263 | priv->session = session; |
264 | INIT_LIST_HEAD(&priv->list); | 264 | INIT_LIST_HEAD(&priv->list); |
265 | 265 | ||
266 | priv->tunnel_sock = tunnel->sock; | 266 | priv->tunnel_sock = tunnel->sock; |
267 | session->recv_skb = l2tp_eth_dev_recv; | 267 | session->recv_skb = l2tp_eth_dev_recv; |
268 | session->session_close = l2tp_eth_delete; | 268 | session->session_close = l2tp_eth_delete; |
269 | #if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE) | 269 | #if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE) |
270 | session->show = l2tp_eth_show; | 270 | session->show = l2tp_eth_show; |
271 | #endif | 271 | #endif |
272 | 272 | ||
273 | spriv = l2tp_session_priv(session); | 273 | spriv = l2tp_session_priv(session); |
274 | spriv->dev = dev; | 274 | spriv->dev = dev; |
275 | 275 | ||
276 | rc = register_netdev(dev); | 276 | rc = register_netdev(dev); |
277 | if (rc < 0) | 277 | if (rc < 0) |
278 | goto out_del_dev; | 278 | goto out_del_dev; |
279 | 279 | ||
280 | __module_get(THIS_MODULE); | 280 | __module_get(THIS_MODULE); |
281 | /* Must be done after register_netdev() */ | 281 | /* Must be done after register_netdev() */ |
282 | strlcpy(session->ifname, dev->name, IFNAMSIZ); | 282 | strlcpy(session->ifname, dev->name, IFNAMSIZ); |
283 | 283 | ||
284 | dev_hold(dev); | 284 | dev_hold(dev); |
285 | pn = l2tp_eth_pernet(dev_net(dev)); | 285 | pn = l2tp_eth_pernet(dev_net(dev)); |
286 | spin_lock(&pn->l2tp_eth_lock); | 286 | spin_lock(&pn->l2tp_eth_lock); |
287 | list_add(&priv->list, &pn->l2tp_eth_dev_list); | 287 | list_add(&priv->list, &pn->l2tp_eth_dev_list); |
288 | spin_unlock(&pn->l2tp_eth_lock); | 288 | spin_unlock(&pn->l2tp_eth_lock); |
289 | 289 | ||
290 | return 0; | 290 | return 0; |
291 | 291 | ||
292 | out_del_dev: | 292 | out_del_dev: |
293 | free_netdev(dev); | 293 | free_netdev(dev); |
294 | spriv->dev = NULL; | ||
294 | out_del_session: | 295 | out_del_session: |
295 | l2tp_session_delete(session); | 296 | l2tp_session_delete(session); |
296 | out: | 297 | out: |
297 | return rc; | 298 | return rc; |
298 | } | 299 | } |
299 | 300 | ||
300 | static __net_init int l2tp_eth_init_net(struct net *net) | 301 | static __net_init int l2tp_eth_init_net(struct net *net) |
301 | { | 302 | { |
302 | struct l2tp_eth_net *pn = net_generic(net, l2tp_eth_net_id); | 303 | struct l2tp_eth_net *pn = net_generic(net, l2tp_eth_net_id); |
303 | 304 | ||
304 | INIT_LIST_HEAD(&pn->l2tp_eth_dev_list); | 305 | INIT_LIST_HEAD(&pn->l2tp_eth_dev_list); |
305 | spin_lock_init(&pn->l2tp_eth_lock); | 306 | spin_lock_init(&pn->l2tp_eth_lock); |
306 | 307 | ||
307 | return 0; | 308 | return 0; |
308 | } | 309 | } |
309 | 310 | ||
310 | static struct pernet_operations l2tp_eth_net_ops = { | 311 | static struct pernet_operations l2tp_eth_net_ops = { |
311 | .init = l2tp_eth_init_net, | 312 | .init = l2tp_eth_init_net, |
312 | .id = &l2tp_eth_net_id, | 313 | .id = &l2tp_eth_net_id, |
313 | .size = sizeof(struct l2tp_eth_net), | 314 | .size = sizeof(struct l2tp_eth_net), |
314 | }; | 315 | }; |
315 | 316 | ||
316 | 317 | ||
317 | static const struct l2tp_nl_cmd_ops l2tp_eth_nl_cmd_ops = { | 318 | static const struct l2tp_nl_cmd_ops l2tp_eth_nl_cmd_ops = { |
318 | .session_create = l2tp_eth_create, | 319 | .session_create = l2tp_eth_create, |
319 | .session_delete = l2tp_session_delete, | 320 | .session_delete = l2tp_session_delete, |
320 | }; | 321 | }; |
321 | 322 | ||
322 | 323 | ||
323 | static int __init l2tp_eth_init(void) | 324 | static int __init l2tp_eth_init(void) |
324 | { | 325 | { |
325 | int err = 0; | 326 | int err = 0; |
326 | 327 | ||
327 | err = l2tp_nl_register_ops(L2TP_PWTYPE_ETH, &l2tp_eth_nl_cmd_ops); | 328 | err = l2tp_nl_register_ops(L2TP_PWTYPE_ETH, &l2tp_eth_nl_cmd_ops); |
328 | if (err) | 329 | if (err) |
329 | goto out; | 330 | goto out; |
330 | 331 | ||
331 | err = register_pernet_device(&l2tp_eth_net_ops); | 332 | err = register_pernet_device(&l2tp_eth_net_ops); |
332 | if (err) | 333 | if (err) |
333 | goto out_unreg; | 334 | goto out_unreg; |
334 | 335 | ||
335 | pr_info("L2TP ethernet pseudowire support (L2TPv3)\n"); | 336 | pr_info("L2TP ethernet pseudowire support (L2TPv3)\n"); |
336 | 337 | ||
337 | return 0; | 338 | return 0; |
338 | 339 | ||
339 | out_unreg: | 340 | out_unreg: |
340 | l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH); | 341 | l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH); |
341 | out: | 342 | out: |
342 | return err; | 343 | return err; |
343 | } | 344 | } |
344 | 345 | ||
345 | static void __exit l2tp_eth_exit(void) | 346 | static void __exit l2tp_eth_exit(void) |
346 | { | 347 | { |
347 | unregister_pernet_device(&l2tp_eth_net_ops); | 348 | unregister_pernet_device(&l2tp_eth_net_ops); |
348 | l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH); | 349 | l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH); |
349 | } | 350 | } |
350 | 351 | ||
351 | module_init(l2tp_eth_init); | 352 | module_init(l2tp_eth_init); |
352 | module_exit(l2tp_eth_exit); | 353 | module_exit(l2tp_eth_exit); |
353 | 354 | ||
354 | MODULE_LICENSE("GPL"); | 355 | MODULE_LICENSE("GPL"); |
355 | MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); | 356 | MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); |
356 | MODULE_DESCRIPTION("L2TP ethernet pseudowire driver"); | 357 | MODULE_DESCRIPTION("L2TP ethernet pseudowire driver"); |
357 | MODULE_VERSION("1.0"); | 358 | MODULE_VERSION("1.0"); |
358 | 359 |