diff options
Diffstat (limited to 'net/wireless/wext.c')
-rw-r--r-- | net/wireless/wext.c | 257 |
1 files changed, 169 insertions, 88 deletions
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 252c2010c2e2..5b4a0cee4418 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -417,6 +417,21 @@ static const int event_type_size[] = { | |||
417 | IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ | 417 | IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ |
418 | }; | 418 | }; |
419 | 419 | ||
420 | #ifdef CONFIG_COMPAT | ||
421 | static const int compat_event_type_size[] = { | ||
422 | IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ | ||
423 | 0, | ||
424 | IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ | ||
425 | 0, | ||
426 | IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ | ||
427 | IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ | ||
428 | IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ | ||
429 | 0, | ||
430 | IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ | ||
431 | IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ | ||
432 | IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ | ||
433 | }; | ||
434 | #endif | ||
420 | 435 | ||
421 | /************************ COMMON SUBROUTINES ************************/ | 436 | /************************ COMMON SUBROUTINES ************************/ |
422 | /* | 437 | /* |
@@ -610,6 +625,11 @@ static void wireless_seq_printf_stats(struct seq_file *seq, | |||
610 | { | 625 | { |
611 | /* Get stats from the driver */ | 626 | /* Get stats from the driver */ |
612 | struct iw_statistics *stats = get_wireless_stats(dev); | 627 | struct iw_statistics *stats = get_wireless_stats(dev); |
628 | static struct iw_statistics nullstats = {}; | ||
629 | |||
630 | /* show device if it's wireless regardless of current stats */ | ||
631 | if (!stats && dev->wireless_handlers) | ||
632 | stats = &nullstats; | ||
613 | 633 | ||
614 | if (stats) { | 634 | if (stats) { |
615 | seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " | 635 | seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " |
@@ -628,7 +648,9 @@ static void wireless_seq_printf_stats(struct seq_file *seq, | |||
628 | stats->discard.nwid, stats->discard.code, | 648 | stats->discard.nwid, stats->discard.code, |
629 | stats->discard.fragment, stats->discard.retries, | 649 | stats->discard.fragment, stats->discard.retries, |
630 | stats->discard.misc, stats->miss.beacon); | 650 | stats->discard.misc, stats->miss.beacon); |
631 | stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; | 651 | |
652 | if (stats != &nullstats) | ||
653 | stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; | ||
632 | } | 654 | } |
633 | } | 655 | } |
634 | 656 | ||
@@ -1250,65 +1272,57 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, | |||
1250 | } | 1272 | } |
1251 | #endif | 1273 | #endif |
1252 | 1274 | ||
1253 | /************************* EVENT PROCESSING *************************/ | 1275 | static int __net_init wext_pernet_init(struct net *net) |
1254 | /* | 1276 | { |
1255 | * Process events generated by the wireless layer or the driver. | 1277 | skb_queue_head_init(&net->wext_nlevents); |
1256 | * Most often, the event will be propagated through rtnetlink | 1278 | return 0; |
1257 | */ | 1279 | } |
1258 | 1280 | ||
1259 | /* ---------------------------------------------------------------- */ | 1281 | static void __net_exit wext_pernet_exit(struct net *net) |
1260 | /* | 1282 | { |
1261 | * Locking... | 1283 | skb_queue_purge(&net->wext_nlevents); |
1262 | * ---------- | 1284 | } |
1263 | * | ||
1264 | * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing | ||
1265 | * the locking issue in here and implementing this code ! | ||
1266 | * | ||
1267 | * The issue : wireless_send_event() is often called in interrupt context, | ||
1268 | * while the Netlink layer can never be called in interrupt context. | ||
1269 | * The fully formed RtNetlink events are queued, and then a tasklet is run | ||
1270 | * to feed those to Netlink. | ||
1271 | * The skb_queue is interrupt safe, and its lock is not held while calling | ||
1272 | * Netlink, so there is no possibility of dealock. | ||
1273 | * Jean II | ||
1274 | */ | ||
1275 | 1285 | ||
1276 | static struct sk_buff_head wireless_nlevent_queue; | 1286 | static struct pernet_operations wext_pernet_ops = { |
1287 | .init = wext_pernet_init, | ||
1288 | .exit = wext_pernet_exit, | ||
1289 | }; | ||
1277 | 1290 | ||
1278 | static int __init wireless_nlevent_init(void) | 1291 | static int __init wireless_nlevent_init(void) |
1279 | { | 1292 | { |
1280 | skb_queue_head_init(&wireless_nlevent_queue); | 1293 | return register_pernet_subsys(&wext_pernet_ops); |
1281 | return 0; | ||
1282 | } | 1294 | } |
1283 | 1295 | ||
1284 | subsys_initcall(wireless_nlevent_init); | 1296 | subsys_initcall(wireless_nlevent_init); |
1285 | 1297 | ||
1286 | static void wireless_nlevent_process(unsigned long data) | 1298 | /* Process events generated by the wireless layer or the driver. */ |
1299 | static void wireless_nlevent_process(struct work_struct *work) | ||
1287 | { | 1300 | { |
1288 | struct sk_buff *skb; | 1301 | struct sk_buff *skb; |
1302 | struct net *net; | ||
1289 | 1303 | ||
1290 | while ((skb = skb_dequeue(&wireless_nlevent_queue))) | 1304 | rtnl_lock(); |
1291 | rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); | 1305 | |
1306 | for_each_net(net) { | ||
1307 | while ((skb = skb_dequeue(&net->wext_nlevents))) | ||
1308 | rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, | ||
1309 | GFP_KERNEL); | ||
1310 | } | ||
1311 | |||
1312 | rtnl_unlock(); | ||
1292 | } | 1313 | } |
1293 | 1314 | ||
1294 | static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); | 1315 | static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); |
1295 | 1316 | ||
1296 | /* ---------------------------------------------------------------- */ | 1317 | static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, |
1297 | /* | 1318 | struct sk_buff *skb) |
1298 | * Fill a rtnetlink message with our event data. | ||
1299 | * Note that we propage only the specified event and don't dump the | ||
1300 | * current wireless config. Dumping the wireless config is far too | ||
1301 | * expensive (for each parameter, the driver need to query the hardware). | ||
1302 | */ | ||
1303 | static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, | ||
1304 | int type, char *event, int event_len) | ||
1305 | { | 1319 | { |
1306 | struct ifinfomsg *r; | 1320 | struct ifinfomsg *r; |
1307 | struct nlmsghdr *nlh; | 1321 | struct nlmsghdr *nlh; |
1308 | 1322 | ||
1309 | nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0); | 1323 | nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); |
1310 | if (nlh == NULL) | 1324 | if (!nlh) |
1311 | return -EMSGSIZE; | 1325 | return NULL; |
1312 | 1326 | ||
1313 | r = nlmsg_data(nlh); | 1327 | r = nlmsg_data(nlh); |
1314 | r->ifi_family = AF_UNSPEC; | 1328 | r->ifi_family = AF_UNSPEC; |
@@ -1319,48 +1333,14 @@ static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, | |||
1319 | r->ifi_change = 0; /* Wireless changes don't affect those flags */ | 1333 | r->ifi_change = 0; /* Wireless changes don't affect those flags */ |
1320 | 1334 | ||
1321 | NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); | 1335 | NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); |
1322 | /* Add the wireless events in the netlink packet */ | ||
1323 | NLA_PUT(skb, IFLA_WIRELESS, event_len, event); | ||
1324 | 1336 | ||
1325 | return nlmsg_end(skb, nlh); | 1337 | return nlh; |
1326 | 1338 | nla_put_failure: | |
1327 | nla_put_failure: | ||
1328 | nlmsg_cancel(skb, nlh); | 1339 | nlmsg_cancel(skb, nlh); |
1329 | return -EMSGSIZE; | 1340 | return NULL; |
1330 | } | 1341 | } |
1331 | 1342 | ||
1332 | /* ---------------------------------------------------------------- */ | ||
1333 | /* | ||
1334 | * Create and broadcast and send it on the standard rtnetlink socket | ||
1335 | * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c | ||
1336 | * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field | ||
1337 | * within a RTM_NEWLINK event. | ||
1338 | */ | ||
1339 | static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | ||
1340 | { | ||
1341 | struct sk_buff *skb; | ||
1342 | int err; | ||
1343 | |||
1344 | if (!net_eq(dev_net(dev), &init_net)) | ||
1345 | return; | ||
1346 | |||
1347 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
1348 | if (!skb) | ||
1349 | return; | ||
1350 | 1343 | ||
1351 | err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len); | ||
1352 | if (err < 0) { | ||
1353 | WARN_ON(err == -EMSGSIZE); | ||
1354 | kfree_skb(skb); | ||
1355 | return; | ||
1356 | } | ||
1357 | |||
1358 | NETLINK_CB(skb).dst_group = RTNLGRP_LINK; | ||
1359 | skb_queue_tail(&wireless_nlevent_queue, skb); | ||
1360 | tasklet_schedule(&wireless_nlevent_tasklet); | ||
1361 | } | ||
1362 | |||
1363 | /* ---------------------------------------------------------------- */ | ||
1364 | /* | 1344 | /* |
1365 | * Main event dispatcher. Called from other parts and drivers. | 1345 | * Main event dispatcher. Called from other parts and drivers. |
1366 | * Send the event on the appropriate channels. | 1346 | * Send the event on the appropriate channels. |
@@ -1369,7 +1349,7 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | |||
1369 | void wireless_send_event(struct net_device * dev, | 1349 | void wireless_send_event(struct net_device * dev, |
1370 | unsigned int cmd, | 1350 | unsigned int cmd, |
1371 | union iwreq_data * wrqu, | 1351 | union iwreq_data * wrqu, |
1372 | char * extra) | 1352 | const char * extra) |
1373 | { | 1353 | { |
1374 | const struct iw_ioctl_description * descr = NULL; | 1354 | const struct iw_ioctl_description * descr = NULL; |
1375 | int extra_len = 0; | 1355 | int extra_len = 0; |
@@ -1379,6 +1359,25 @@ void wireless_send_event(struct net_device * dev, | |||
1379 | int wrqu_off = 0; /* Offset in wrqu */ | 1359 | int wrqu_off = 0; /* Offset in wrqu */ |
1380 | /* Don't "optimise" the following variable, it will crash */ | 1360 | /* Don't "optimise" the following variable, it will crash */ |
1381 | unsigned cmd_index; /* *MUST* be unsigned */ | 1361 | unsigned cmd_index; /* *MUST* be unsigned */ |
1362 | struct sk_buff *skb; | ||
1363 | struct nlmsghdr *nlh; | ||
1364 | struct nlattr *nla; | ||
1365 | #ifdef CONFIG_COMPAT | ||
1366 | struct __compat_iw_event *compat_event; | ||
1367 | struct compat_iw_point compat_wrqu; | ||
1368 | struct sk_buff *compskb; | ||
1369 | #endif | ||
1370 | |||
1371 | /* | ||
1372 | * Nothing in the kernel sends scan events with data, be safe. | ||
1373 | * This is necessary because we cannot fix up scan event data | ||
1374 | * for compat, due to being contained in 'extra', but normally | ||
1375 | * applications are required to retrieve the scan data anyway | ||
1376 | * and no data is included in the event, this codifies that | ||
1377 | * practice. | ||
1378 | */ | ||
1379 | if (WARN_ON(cmd == SIOCGIWSCAN && extra)) | ||
1380 | extra = NULL; | ||
1382 | 1381 | ||
1383 | /* Get the description of the Event */ | 1382 | /* Get the description of the Event */ |
1384 | if (cmd <= SIOCIWLAST) { | 1383 | if (cmd <= SIOCIWLAST) { |
@@ -1426,25 +1425,107 @@ void wireless_send_event(struct net_device * dev, | |||
1426 | hdr_len = event_type_size[descr->header_type]; | 1425 | hdr_len = event_type_size[descr->header_type]; |
1427 | event_len = hdr_len + extra_len; | 1426 | event_len = hdr_len + extra_len; |
1428 | 1427 | ||
1429 | /* Create temporary buffer to hold the event */ | 1428 | /* |
1430 | event = kmalloc(event_len, GFP_ATOMIC); | 1429 | * The problem for 64/32 bit. |
1431 | if (event == NULL) | 1430 | * |
1431 | * On 64-bit, a regular event is laid out as follows: | ||
1432 | * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | ||
1433 | * | event.len | event.cmd | p a d d i n g | | ||
1434 | * | wrqu data ... (with the correct size) | | ||
1435 | * | ||
1436 | * This padding exists because we manipulate event->u, | ||
1437 | * and 'event' is not packed. | ||
1438 | * | ||
1439 | * An iw_point event is laid out like this instead: | ||
1440 | * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | ||
1441 | * | event.len | event.cmd | p a d d i n g | | ||
1442 | * | iwpnt.len | iwpnt.flg | p a d d i n g | | ||
1443 | * | extra data ... | ||
1444 | * | ||
1445 | * The second padding exists because struct iw_point is extended, | ||
1446 | * but this depends on the platform... | ||
1447 | * | ||
1448 | * On 32-bit, all the padding shouldn't be there. | ||
1449 | */ | ||
1450 | |||
1451 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
1452 | if (!skb) | ||
1453 | return; | ||
1454 | |||
1455 | /* Send via the RtNetlink event channel */ | ||
1456 | nlh = rtnetlink_ifinfo_prep(dev, skb); | ||
1457 | if (WARN_ON(!nlh)) { | ||
1458 | kfree_skb(skb); | ||
1459 | return; | ||
1460 | } | ||
1461 | |||
1462 | /* Add the wireless events in the netlink packet */ | ||
1463 | nla = nla_reserve(skb, IFLA_WIRELESS, event_len); | ||
1464 | if (!nla) { | ||
1465 | kfree_skb(skb); | ||
1432 | return; | 1466 | return; |
1467 | } | ||
1468 | event = nla_data(nla); | ||
1433 | 1469 | ||
1434 | /* Fill event */ | 1470 | /* Fill event - first clear to avoid data leaking */ |
1471 | memset(event, 0, hdr_len); | ||
1435 | event->len = event_len; | 1472 | event->len = event_len; |
1436 | event->cmd = cmd; | 1473 | event->cmd = cmd; |
1437 | memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); | 1474 | memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); |
1438 | if (extra) | 1475 | if (extra_len) |
1439 | memcpy(((char *) event) + hdr_len, extra, extra_len); | 1476 | memcpy(((char *) event) + hdr_len, extra, extra_len); |
1440 | 1477 | ||
1478 | nlmsg_end(skb, nlh); | ||
1479 | #ifdef CONFIG_COMPAT | ||
1480 | hdr_len = compat_event_type_size[descr->header_type]; | ||
1481 | event_len = hdr_len + extra_len; | ||
1482 | |||
1483 | compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
1484 | if (!compskb) { | ||
1485 | kfree_skb(skb); | ||
1486 | return; | ||
1487 | } | ||
1488 | |||
1441 | /* Send via the RtNetlink event channel */ | 1489 | /* Send via the RtNetlink event channel */ |
1442 | rtmsg_iwinfo(dev, (char *) event, event_len); | 1490 | nlh = rtnetlink_ifinfo_prep(dev, compskb); |
1491 | if (WARN_ON(!nlh)) { | ||
1492 | kfree_skb(skb); | ||
1493 | kfree_skb(compskb); | ||
1494 | return; | ||
1495 | } | ||
1443 | 1496 | ||
1444 | /* Cleanup */ | 1497 | /* Add the wireless events in the netlink packet */ |
1445 | kfree(event); | 1498 | nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); |
1499 | if (!nla) { | ||
1500 | kfree_skb(skb); | ||
1501 | kfree_skb(compskb); | ||
1502 | return; | ||
1503 | } | ||
1504 | compat_event = nla_data(nla); | ||
1446 | 1505 | ||
1447 | return; /* Always success, I guess ;-) */ | 1506 | compat_event->len = event_len; |
1507 | compat_event->cmd = cmd; | ||
1508 | if (descr->header_type == IW_HEADER_TYPE_POINT) { | ||
1509 | compat_wrqu.length = wrqu->data.length; | ||
1510 | compat_wrqu.flags = wrqu->data.flags; | ||
1511 | memcpy(&compat_event->pointer, | ||
1512 | ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, | ||
1513 | hdr_len - IW_EV_COMPAT_LCP_LEN); | ||
1514 | if (extra_len) | ||
1515 | memcpy(((char *) compat_event) + hdr_len, | ||
1516 | extra, extra_len); | ||
1517 | } else { | ||
1518 | /* extra_len must be zero, so no if (extra) needed */ | ||
1519 | memcpy(&compat_event->pointer, wrqu, | ||
1520 | hdr_len - IW_EV_COMPAT_LCP_LEN); | ||
1521 | } | ||
1522 | |||
1523 | nlmsg_end(compskb, nlh); | ||
1524 | |||
1525 | skb_shinfo(skb)->frag_list = compskb; | ||
1526 | #endif | ||
1527 | skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); | ||
1528 | schedule_work(&wireless_nlevent_work); | ||
1448 | } | 1529 | } |
1449 | EXPORT_SYMBOL(wireless_send_event); | 1530 | EXPORT_SYMBOL(wireless_send_event); |
1450 | 1531 | ||