1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* drivers/net/wireless/virt_wifi.c |
3 | * |
4 | * A fake implementation of cfg80211_ops that can be tacked on to an ethernet |
5 | * net_device to make it appear as a wireless connection. |
6 | * |
7 | * Copyright (C) 2018 Google, Inc. |
8 | * |
9 | * Author: schuffelen@google.com |
10 | */ |
11 | |
12 | #include <net/cfg80211.h> |
13 | #include <net/rtnetlink.h> |
14 | #include <linux/etherdevice.h> |
15 | #include <linux/math64.h> |
16 | #include <linux/module.h> |
17 | |
18 | static struct wiphy *common_wiphy; |
19 | |
20 | struct virt_wifi_wiphy_priv { |
21 | struct delayed_work scan_result; |
22 | struct cfg80211_scan_request *scan_request; |
23 | bool being_deleted; |
24 | }; |
25 | |
26 | static struct ieee80211_channel channel_2ghz = { |
27 | .band = NL80211_BAND_2GHZ, |
28 | .center_freq = 2432, |
29 | .hw_value = 2432, |
30 | .max_power = 20, |
31 | }; |
32 | |
33 | static struct ieee80211_rate bitrates_2ghz[] = { |
34 | { .bitrate = 10 }, |
35 | { .bitrate = 20 }, |
36 | { .bitrate = 55 }, |
37 | { .bitrate = 110 }, |
38 | { .bitrate = 60 }, |
39 | { .bitrate = 120 }, |
40 | { .bitrate = 240 }, |
41 | }; |
42 | |
43 | static struct ieee80211_supported_band band_2ghz = { |
44 | .channels = &channel_2ghz, |
45 | .bitrates = bitrates_2ghz, |
46 | .band = NL80211_BAND_2GHZ, |
47 | .n_channels = 1, |
48 | .n_bitrates = ARRAY_SIZE(bitrates_2ghz), |
49 | .ht_cap = { |
50 | .ht_supported = true, |
51 | .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | |
52 | IEEE80211_HT_CAP_GRN_FLD | |
53 | IEEE80211_HT_CAP_SGI_20 | |
54 | IEEE80211_HT_CAP_SGI_40 | |
55 | IEEE80211_HT_CAP_DSSSCCK40, |
56 | .ampdu_factor = 0x3, |
57 | .ampdu_density = 0x6, |
58 | .mcs = { |
59 | .rx_mask = {0xff, 0xff}, |
60 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, |
61 | }, |
62 | }, |
63 | }; |
64 | |
65 | static struct ieee80211_channel channel_5ghz = { |
66 | .band = NL80211_BAND_5GHZ, |
67 | .center_freq = 5240, |
68 | .hw_value = 5240, |
69 | .max_power = 20, |
70 | }; |
71 | |
72 | static struct ieee80211_rate bitrates_5ghz[] = { |
73 | { .bitrate = 60 }, |
74 | { .bitrate = 120 }, |
75 | { .bitrate = 240 }, |
76 | }; |
77 | |
78 | #define RX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \ |
79 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \ |
80 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \ |
81 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \ |
82 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \ |
83 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \ |
84 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \ |
85 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) |
86 | |
87 | #define TX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \ |
88 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \ |
89 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \ |
90 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \ |
91 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \ |
92 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \ |
93 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \ |
94 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) |
95 | |
96 | static struct ieee80211_supported_band band_5ghz = { |
97 | .channels = &channel_5ghz, |
98 | .bitrates = bitrates_5ghz, |
99 | .band = NL80211_BAND_5GHZ, |
100 | .n_channels = 1, |
101 | .n_bitrates = ARRAY_SIZE(bitrates_5ghz), |
102 | .ht_cap = { |
103 | .ht_supported = true, |
104 | .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | |
105 | IEEE80211_HT_CAP_GRN_FLD | |
106 | IEEE80211_HT_CAP_SGI_20 | |
107 | IEEE80211_HT_CAP_SGI_40 | |
108 | IEEE80211_HT_CAP_DSSSCCK40, |
109 | .ampdu_factor = 0x3, |
110 | .ampdu_density = 0x6, |
111 | .mcs = { |
112 | .rx_mask = {0xff, 0xff}, |
113 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, |
114 | }, |
115 | }, |
116 | .vht_cap = { |
117 | .vht_supported = true, |
118 | .cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | |
119 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | |
120 | IEEE80211_VHT_CAP_RXLDPC | |
121 | IEEE80211_VHT_CAP_SHORT_GI_80 | |
122 | IEEE80211_VHT_CAP_SHORT_GI_160 | |
123 | IEEE80211_VHT_CAP_TXSTBC | |
124 | IEEE80211_VHT_CAP_RXSTBC_1 | |
125 | IEEE80211_VHT_CAP_RXSTBC_2 | |
126 | IEEE80211_VHT_CAP_RXSTBC_3 | |
127 | IEEE80211_VHT_CAP_RXSTBC_4 | |
128 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, |
129 | .vht_mcs = { |
130 | .rx_mcs_map = cpu_to_le16(RX_MCS_MAP), |
131 | .tx_mcs_map = cpu_to_le16(TX_MCS_MAP), |
132 | } |
133 | }, |
134 | }; |
135 | |
136 | /* Assigned at module init. Guaranteed locally-administered and unicast. */ |
137 | static u8 fake_router_bssid[ETH_ALEN] __ro_after_init = {}; |
138 | |
139 | static void virt_wifi_inform_bss(struct wiphy *wiphy) |
140 | { |
141 | u64 tsf = div_u64(dividend: ktime_get_boottime_ns(), divisor: 1000); |
142 | struct cfg80211_bss *informed_bss; |
143 | static const struct { |
144 | u8 tag; |
145 | u8 len; |
146 | u8 ssid[8]; |
147 | } __packed ssid = { |
148 | .tag = WLAN_EID_SSID, |
149 | .len = 8, |
150 | .ssid = "VirtWifi" , |
151 | }; |
152 | |
153 | informed_bss = cfg80211_inform_bss(wiphy, rx_channel: &channel_5ghz, |
154 | ftype: CFG80211_BSS_FTYPE_PRESP, |
155 | bssid: fake_router_bssid, tsf, |
156 | WLAN_CAPABILITY_ESS, beacon_interval: 0, |
157 | ie: (void *)&ssid, ielen: sizeof(ssid), |
158 | DBM_TO_MBM(-50), GFP_KERNEL); |
159 | cfg80211_put_bss(wiphy, bss: informed_bss); |
160 | } |
161 | |
162 | /* Called with the rtnl lock held. */ |
163 | static int virt_wifi_scan(struct wiphy *wiphy, |
164 | struct cfg80211_scan_request *request) |
165 | { |
166 | struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy); |
167 | |
168 | wiphy_debug(wiphy, "scan\n" ); |
169 | |
170 | if (priv->scan_request || priv->being_deleted) |
171 | return -EBUSY; |
172 | |
173 | priv->scan_request = request; |
174 | schedule_delayed_work(dwork: &priv->scan_result, HZ * 2); |
175 | |
176 | return 0; |
177 | } |
178 | |
179 | /* Acquires and releases the rdev BSS lock. */ |
180 | static void virt_wifi_scan_result(struct work_struct *work) |
181 | { |
182 | struct virt_wifi_wiphy_priv *priv = |
183 | container_of(work, struct virt_wifi_wiphy_priv, |
184 | scan_result.work); |
185 | struct wiphy *wiphy = priv_to_wiphy(priv); |
186 | struct cfg80211_scan_info scan_info = { .aborted = false }; |
187 | |
188 | virt_wifi_inform_bss(wiphy); |
189 | |
190 | /* Schedules work which acquires and releases the rtnl lock. */ |
191 | cfg80211_scan_done(request: priv->scan_request, info: &scan_info); |
192 | priv->scan_request = NULL; |
193 | } |
194 | |
195 | /* May acquire and release the rdev BSS lock. */ |
196 | static void virt_wifi_cancel_scan(struct wiphy *wiphy) |
197 | { |
198 | struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy); |
199 | |
200 | cancel_delayed_work_sync(dwork: &priv->scan_result); |
201 | /* Clean up dangling callbacks if necessary. */ |
202 | if (priv->scan_request) { |
203 | struct cfg80211_scan_info scan_info = { .aborted = true }; |
204 | /* Schedules work which acquires and releases the rtnl lock. */ |
205 | cfg80211_scan_done(request: priv->scan_request, info: &scan_info); |
206 | priv->scan_request = NULL; |
207 | } |
208 | } |
209 | |
210 | struct virt_wifi_netdev_priv { |
211 | struct delayed_work connect; |
212 | struct net_device *lowerdev; |
213 | struct net_device *upperdev; |
214 | u32 tx_packets; |
215 | u32 tx_failed; |
216 | u8 connect_requested_bss[ETH_ALEN]; |
217 | bool is_up; |
218 | bool is_connected; |
219 | bool being_deleted; |
220 | }; |
221 | |
222 | /* Called with the rtnl lock held. */ |
223 | static int virt_wifi_connect(struct wiphy *wiphy, struct net_device *netdev, |
224 | struct cfg80211_connect_params *sme) |
225 | { |
226 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev: netdev); |
227 | bool could_schedule; |
228 | |
229 | if (priv->being_deleted || !priv->is_up) |
230 | return -EBUSY; |
231 | |
232 | could_schedule = schedule_delayed_work(dwork: &priv->connect, HZ * 2); |
233 | if (!could_schedule) |
234 | return -EBUSY; |
235 | |
236 | if (sme->bssid) { |
237 | ether_addr_copy(dst: priv->connect_requested_bss, src: sme->bssid); |
238 | } else { |
239 | virt_wifi_inform_bss(wiphy); |
240 | eth_zero_addr(addr: priv->connect_requested_bss); |
241 | } |
242 | |
243 | wiphy_debug(wiphy, "connect\n" ); |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | /* Acquires and releases the rdev event lock. */ |
249 | static void virt_wifi_connect_complete(struct work_struct *work) |
250 | { |
251 | struct virt_wifi_netdev_priv *priv = |
252 | container_of(work, struct virt_wifi_netdev_priv, connect.work); |
253 | u8 *requested_bss = priv->connect_requested_bss; |
254 | bool right_addr = ether_addr_equal(addr1: requested_bss, addr2: fake_router_bssid); |
255 | u16 status = WLAN_STATUS_SUCCESS; |
256 | |
257 | if (is_zero_ether_addr(addr: requested_bss)) |
258 | requested_bss = NULL; |
259 | |
260 | if (!priv->is_up || (requested_bss && !right_addr)) |
261 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
262 | else |
263 | priv->is_connected = true; |
264 | |
265 | /* Schedules an event that acquires the rtnl lock. */ |
266 | cfg80211_connect_result(dev: priv->upperdev, bssid: requested_bss, NULL, req_ie_len: 0, NULL, resp_ie_len: 0, |
267 | status, GFP_KERNEL); |
268 | netif_carrier_on(dev: priv->upperdev); |
269 | } |
270 | |
271 | /* May acquire and release the rdev event lock. */ |
272 | static void virt_wifi_cancel_connect(struct net_device *netdev) |
273 | { |
274 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev: netdev); |
275 | |
276 | /* If there is work pending, clean up dangling callbacks. */ |
277 | if (cancel_delayed_work_sync(dwork: &priv->connect)) { |
278 | /* Schedules an event that acquires the rtnl lock. */ |
279 | cfg80211_connect_result(dev: priv->upperdev, |
280 | bssid: priv->connect_requested_bss, NULL, req_ie_len: 0, |
281 | NULL, resp_ie_len: 0, |
282 | status: WLAN_STATUS_UNSPECIFIED_FAILURE, |
283 | GFP_KERNEL); |
284 | } |
285 | } |
286 | |
287 | /* Called with the rtnl lock held. Acquires the rdev event lock. */ |
288 | static int virt_wifi_disconnect(struct wiphy *wiphy, struct net_device *netdev, |
289 | u16 reason_code) |
290 | { |
291 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev: netdev); |
292 | |
293 | if (priv->being_deleted) |
294 | return -EBUSY; |
295 | |
296 | wiphy_debug(wiphy, "disconnect\n" ); |
297 | virt_wifi_cancel_connect(netdev); |
298 | |
299 | cfg80211_disconnected(dev: netdev, reason: reason_code, NULL, ie_len: 0, locally_generated: true, GFP_KERNEL); |
300 | priv->is_connected = false; |
301 | netif_carrier_off(dev: netdev); |
302 | |
303 | return 0; |
304 | } |
305 | |
306 | /* Called with the rtnl lock held. */ |
307 | static int virt_wifi_get_station(struct wiphy *wiphy, struct net_device *dev, |
308 | const u8 *mac, struct station_info *sinfo) |
309 | { |
310 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev); |
311 | |
312 | wiphy_debug(wiphy, "get_station\n" ); |
313 | |
314 | if (!priv->is_connected || !ether_addr_equal(addr1: mac, addr2: fake_router_bssid)) |
315 | return -ENOENT; |
316 | |
317 | sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) | |
318 | BIT_ULL(NL80211_STA_INFO_TX_FAILED) | |
319 | BIT_ULL(NL80211_STA_INFO_SIGNAL) | |
320 | BIT_ULL(NL80211_STA_INFO_TX_BITRATE); |
321 | sinfo->tx_packets = priv->tx_packets; |
322 | sinfo->tx_failed = priv->tx_failed; |
323 | /* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_ */ |
324 | sinfo->signal = -50; |
325 | sinfo->txrate = (struct rate_info) { |
326 | .legacy = 10, /* units are 100kbit/s */ |
327 | }; |
328 | return 0; |
329 | } |
330 | |
331 | /* Called with the rtnl lock held. */ |
332 | static int virt_wifi_dump_station(struct wiphy *wiphy, struct net_device *dev, |
333 | int idx, u8 *mac, struct station_info *sinfo) |
334 | { |
335 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev); |
336 | |
337 | wiphy_debug(wiphy, "dump_station\n" ); |
338 | |
339 | if (idx != 0 || !priv->is_connected) |
340 | return -ENOENT; |
341 | |
342 | ether_addr_copy(dst: mac, src: fake_router_bssid); |
343 | return virt_wifi_get_station(wiphy, dev, mac: fake_router_bssid, sinfo); |
344 | } |
345 | |
346 | static const struct cfg80211_ops virt_wifi_cfg80211_ops = { |
347 | .scan = virt_wifi_scan, |
348 | |
349 | .connect = virt_wifi_connect, |
350 | .disconnect = virt_wifi_disconnect, |
351 | |
352 | .get_station = virt_wifi_get_station, |
353 | .dump_station = virt_wifi_dump_station, |
354 | }; |
355 | |
356 | /* Acquires and releases the rtnl lock. */ |
357 | static struct wiphy *virt_wifi_make_wiphy(void) |
358 | { |
359 | struct wiphy *wiphy; |
360 | struct virt_wifi_wiphy_priv *priv; |
361 | int err; |
362 | |
363 | wiphy = wiphy_new(ops: &virt_wifi_cfg80211_ops, sizeof_priv: sizeof(*priv)); |
364 | |
365 | if (!wiphy) |
366 | return NULL; |
367 | |
368 | wiphy->max_scan_ssids = 4; |
369 | wiphy->max_scan_ie_len = 1000; |
370 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; |
371 | |
372 | wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz; |
373 | wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz; |
374 | wiphy->bands[NL80211_BAND_60GHZ] = NULL; |
375 | |
376 | wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); |
377 | |
378 | priv = wiphy_priv(wiphy); |
379 | priv->being_deleted = false; |
380 | priv->scan_request = NULL; |
381 | INIT_DELAYED_WORK(&priv->scan_result, virt_wifi_scan_result); |
382 | |
383 | err = wiphy_register(wiphy); |
384 | if (err < 0) { |
385 | wiphy_free(wiphy); |
386 | return NULL; |
387 | } |
388 | |
389 | return wiphy; |
390 | } |
391 | |
392 | /* Acquires and releases the rtnl lock. */ |
393 | static void virt_wifi_destroy_wiphy(struct wiphy *wiphy) |
394 | { |
395 | struct virt_wifi_wiphy_priv *priv; |
396 | |
397 | WARN(!wiphy, "%s called with null wiphy" , __func__); |
398 | if (!wiphy) |
399 | return; |
400 | |
401 | priv = wiphy_priv(wiphy); |
402 | priv->being_deleted = true; |
403 | virt_wifi_cancel_scan(wiphy); |
404 | |
405 | if (wiphy->registered) |
406 | wiphy_unregister(wiphy); |
407 | wiphy_free(wiphy); |
408 | } |
409 | |
410 | /* Enters and exits a RCU-bh critical section. */ |
411 | static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb, |
412 | struct net_device *dev) |
413 | { |
414 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev); |
415 | |
416 | priv->tx_packets++; |
417 | if (!priv->is_connected) { |
418 | priv->tx_failed++; |
419 | return NET_XMIT_DROP; |
420 | } |
421 | |
422 | skb->dev = priv->lowerdev; |
423 | return dev_queue_xmit(skb); |
424 | } |
425 | |
426 | /* Called with rtnl lock held. */ |
427 | static int virt_wifi_net_device_open(struct net_device *dev) |
428 | { |
429 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev); |
430 | |
431 | priv->is_up = true; |
432 | return 0; |
433 | } |
434 | |
435 | /* Called with rtnl lock held. */ |
436 | static int virt_wifi_net_device_stop(struct net_device *dev) |
437 | { |
438 | struct virt_wifi_netdev_priv *n_priv = netdev_priv(dev); |
439 | |
440 | n_priv->is_up = false; |
441 | |
442 | if (!dev->ieee80211_ptr) |
443 | return 0; |
444 | |
445 | virt_wifi_cancel_scan(wiphy: dev->ieee80211_ptr->wiphy); |
446 | virt_wifi_cancel_connect(netdev: dev); |
447 | netif_carrier_off(dev); |
448 | |
449 | return 0; |
450 | } |
451 | |
452 | static int virt_wifi_net_device_get_iflink(const struct net_device *dev) |
453 | { |
454 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev); |
455 | |
456 | return READ_ONCE(priv->lowerdev->ifindex); |
457 | } |
458 | |
459 | static const struct net_device_ops virt_wifi_ops = { |
460 | .ndo_start_xmit = virt_wifi_start_xmit, |
461 | .ndo_open = virt_wifi_net_device_open, |
462 | .ndo_stop = virt_wifi_net_device_stop, |
463 | .ndo_get_iflink = virt_wifi_net_device_get_iflink, |
464 | }; |
465 | |
466 | /* Invoked as part of rtnl lock release. */ |
467 | static void virt_wifi_net_device_destructor(struct net_device *dev) |
468 | { |
469 | /* Delayed past dellink to allow nl80211 to react to the device being |
470 | * deleted. |
471 | */ |
472 | kfree(objp: dev->ieee80211_ptr); |
473 | dev->ieee80211_ptr = NULL; |
474 | } |
475 | |
476 | /* No lock interaction. */ |
477 | static void virt_wifi_setup(struct net_device *dev) |
478 | { |
479 | ether_setup(dev); |
480 | dev->netdev_ops = &virt_wifi_ops; |
481 | dev->needs_free_netdev = true; |
482 | } |
483 | |
484 | /* Called in a RCU read critical section from netif_receive_skb */ |
485 | static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb) |
486 | { |
487 | struct sk_buff *skb = *pskb; |
488 | struct virt_wifi_netdev_priv *priv = |
489 | rcu_dereference(skb->dev->rx_handler_data); |
490 | |
491 | if (!priv->is_connected) |
492 | return RX_HANDLER_PASS; |
493 | |
494 | /* GFP_ATOMIC because this is a packet interrupt handler. */ |
495 | skb = skb_share_check(skb, GFP_ATOMIC); |
496 | if (!skb) { |
497 | dev_err(&priv->upperdev->dev, "can't skb_share_check\n" ); |
498 | return RX_HANDLER_CONSUMED; |
499 | } |
500 | |
501 | *pskb = skb; |
502 | skb->dev = priv->upperdev; |
503 | skb->pkt_type = PACKET_HOST; |
504 | return RX_HANDLER_ANOTHER; |
505 | } |
506 | |
507 | /* Called with rtnl lock held. */ |
508 | static int virt_wifi_newlink(struct net *src_net, struct net_device *dev, |
509 | struct nlattr *tb[], struct nlattr *data[], |
510 | struct netlink_ext_ack *extack) |
511 | { |
512 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev); |
513 | int err; |
514 | |
515 | if (!tb[IFLA_LINK]) |
516 | return -EINVAL; |
517 | |
518 | netif_carrier_off(dev); |
519 | |
520 | priv->upperdev = dev; |
521 | priv->lowerdev = __dev_get_by_index(net: src_net, |
522 | ifindex: nla_get_u32(nla: tb[IFLA_LINK])); |
523 | |
524 | if (!priv->lowerdev) |
525 | return -ENODEV; |
526 | if (!tb[IFLA_MTU]) |
527 | dev->mtu = priv->lowerdev->mtu; |
528 | else if (dev->mtu > priv->lowerdev->mtu) |
529 | return -EINVAL; |
530 | |
531 | err = netdev_rx_handler_register(dev: priv->lowerdev, rx_handler: virt_wifi_rx_handler, |
532 | rx_handler_data: priv); |
533 | if (err) { |
534 | dev_err(&priv->lowerdev->dev, |
535 | "can't netdev_rx_handler_register: %d\n" , err); |
536 | return err; |
537 | } |
538 | |
539 | eth_hw_addr_inherit(dst: dev, src: priv->lowerdev); |
540 | netif_stacked_transfer_operstate(rootdev: priv->lowerdev, dev); |
541 | |
542 | SET_NETDEV_DEV(dev, &priv->lowerdev->dev); |
543 | dev->ieee80211_ptr = kzalloc(size: sizeof(*dev->ieee80211_ptr), GFP_KERNEL); |
544 | |
545 | if (!dev->ieee80211_ptr) { |
546 | err = -ENOMEM; |
547 | goto remove_handler; |
548 | } |
549 | |
550 | dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; |
551 | dev->ieee80211_ptr->wiphy = common_wiphy; |
552 | |
553 | err = register_netdevice(dev); |
554 | if (err) { |
555 | dev_err(&priv->lowerdev->dev, "can't register_netdevice: %d\n" , |
556 | err); |
557 | goto free_wireless_dev; |
558 | } |
559 | |
560 | err = netdev_upper_dev_link(dev: priv->lowerdev, upper_dev: dev, extack); |
561 | if (err) { |
562 | dev_err(&priv->lowerdev->dev, "can't netdev_upper_dev_link: %d\n" , |
563 | err); |
564 | goto unregister_netdev; |
565 | } |
566 | |
567 | dev->priv_destructor = virt_wifi_net_device_destructor; |
568 | priv->being_deleted = false; |
569 | priv->is_connected = false; |
570 | priv->is_up = false; |
571 | INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete); |
572 | __module_get(THIS_MODULE); |
573 | |
574 | return 0; |
575 | unregister_netdev: |
576 | unregister_netdevice(dev); |
577 | free_wireless_dev: |
578 | kfree(objp: dev->ieee80211_ptr); |
579 | dev->ieee80211_ptr = NULL; |
580 | remove_handler: |
581 | netdev_rx_handler_unregister(dev: priv->lowerdev); |
582 | |
583 | return err; |
584 | } |
585 | |
586 | /* Called with rtnl lock held. */ |
587 | static void virt_wifi_dellink(struct net_device *dev, |
588 | struct list_head *head) |
589 | { |
590 | struct virt_wifi_netdev_priv *priv = netdev_priv(dev); |
591 | |
592 | if (dev->ieee80211_ptr) |
593 | virt_wifi_cancel_scan(wiphy: dev->ieee80211_ptr->wiphy); |
594 | |
595 | priv->being_deleted = true; |
596 | virt_wifi_cancel_connect(netdev: dev); |
597 | netif_carrier_off(dev); |
598 | |
599 | netdev_rx_handler_unregister(dev: priv->lowerdev); |
600 | netdev_upper_dev_unlink(dev: priv->lowerdev, upper_dev: dev); |
601 | |
602 | unregister_netdevice_queue(dev, head); |
603 | module_put(THIS_MODULE); |
604 | |
605 | /* Deleting the wiphy is handled in the module destructor. */ |
606 | } |
607 | |
608 | static struct rtnl_link_ops virt_wifi_link_ops = { |
609 | .kind = "virt_wifi" , |
610 | .setup = virt_wifi_setup, |
611 | .newlink = virt_wifi_newlink, |
612 | .dellink = virt_wifi_dellink, |
613 | .priv_size = sizeof(struct virt_wifi_netdev_priv), |
614 | }; |
615 | |
616 | static bool netif_is_virt_wifi_dev(const struct net_device *dev) |
617 | { |
618 | return rcu_access_pointer(dev->rx_handler) == virt_wifi_rx_handler; |
619 | } |
620 | |
621 | static int virt_wifi_event(struct notifier_block *this, unsigned long event, |
622 | void *ptr) |
623 | { |
624 | struct net_device *lower_dev = netdev_notifier_info_to_dev(info: ptr); |
625 | struct virt_wifi_netdev_priv *priv; |
626 | struct net_device *upper_dev; |
627 | LIST_HEAD(list_kill); |
628 | |
629 | if (!netif_is_virt_wifi_dev(dev: lower_dev)) |
630 | return NOTIFY_DONE; |
631 | |
632 | switch (event) { |
633 | case NETDEV_UNREGISTER: |
634 | priv = rtnl_dereference(lower_dev->rx_handler_data); |
635 | if (!priv) |
636 | return NOTIFY_DONE; |
637 | |
638 | upper_dev = priv->upperdev; |
639 | |
640 | upper_dev->rtnl_link_ops->dellink(upper_dev, &list_kill); |
641 | unregister_netdevice_many(head: &list_kill); |
642 | break; |
643 | } |
644 | |
645 | return NOTIFY_DONE; |
646 | } |
647 | |
648 | static struct notifier_block virt_wifi_notifier = { |
649 | .notifier_call = virt_wifi_event, |
650 | }; |
651 | |
652 | /* Acquires and releases the rtnl lock. */ |
653 | static int __init virt_wifi_init_module(void) |
654 | { |
655 | int err; |
656 | |
657 | /* Guaranteed to be locally-administered and not multicast. */ |
658 | eth_random_addr(addr: fake_router_bssid); |
659 | |
660 | err = register_netdevice_notifier(nb: &virt_wifi_notifier); |
661 | if (err) |
662 | return err; |
663 | |
664 | err = -ENOMEM; |
665 | common_wiphy = virt_wifi_make_wiphy(); |
666 | if (!common_wiphy) |
667 | goto notifier; |
668 | |
669 | err = rtnl_link_register(ops: &virt_wifi_link_ops); |
670 | if (err) |
671 | goto destroy_wiphy; |
672 | |
673 | return 0; |
674 | |
675 | destroy_wiphy: |
676 | virt_wifi_destroy_wiphy(wiphy: common_wiphy); |
677 | notifier: |
678 | unregister_netdevice_notifier(nb: &virt_wifi_notifier); |
679 | return err; |
680 | } |
681 | |
682 | /* Acquires and releases the rtnl lock. */ |
683 | static void __exit virt_wifi_cleanup_module(void) |
684 | { |
685 | /* Will delete any devices that depend on the wiphy. */ |
686 | rtnl_link_unregister(ops: &virt_wifi_link_ops); |
687 | virt_wifi_destroy_wiphy(wiphy: common_wiphy); |
688 | unregister_netdevice_notifier(nb: &virt_wifi_notifier); |
689 | } |
690 | |
691 | module_init(virt_wifi_init_module); |
692 | module_exit(virt_wifi_cleanup_module); |
693 | |
694 | MODULE_LICENSE("GPL v2" ); |
695 | MODULE_AUTHOR("Cody Schuffelen <schuffelen@google.com>" ); |
696 | MODULE_DESCRIPTION("Driver for a wireless wrapper of ethernet devices" ); |
697 | MODULE_ALIAS_RTNL_LINK("virt_wifi" ); |
698 | |