aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/wext.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-07-01 07:26:02 -0400
committerDavid S. Miller <davem@davemloft.net>2009-07-15 11:53:39 -0400
commit1dacc76d0014a034b8aca14237c127d7c19d7726 (patch)
treed3ba044578fab9076ef4a73694fa7d23d4a50969 /net/wireless/wext.c
parent4f45b2cd4e78b5e49d7d41548345b879d3fdfeae (diff)
net/compat/wext: send different messages to compat tasks
Wireless extensions have the unfortunate problem that events are multicast netlink messages, and are not independent of pointer size. Thus, currently 32-bit tasks on 64-bit platforms cannot properly receive events and fail with all kinds of strange problems, for instance wpa_supplicant never notices disassociations, due to the way the 64-bit event looks (to a 32-bit process), the fact that the address is all zeroes is lost, it thinks instead it is 00:00:00:00:01:00. The same problem existed with the ioctls, until David Miller fixed those some time ago in an heroic effort. A different problem caused by this is that we cannot send the ASSOCREQIE/ASSOCRESPIE events because sending them causes a 32-bit wpa_supplicant on a 64-bit system to overwrite its internal information, which is worse than it not getting the information at all -- so we currently resort to sending a custom string event that it then parses. This, however, has a severe size limitation we are frequently hitting with modern access points; this limitation would can be lifted after this patch by sending the correct binary, not custom, event. A similar problem apparently happens for some other netlink users on x86_64 with 32-bit tasks due to the alignment for 64-bit quantities. In order to fix these problems, I have implemented a way to send compat messages to tasks. When sending an event, we send the non-compat event data together with a compat event data in skb_shinfo(main_skb)->frag_list. Then, when the event is read from the socket, the netlink code makes sure to pass out only the skb that is compatible with the task. This approach was suggested by David Miller, my original approach required always sending two skbs but that had various small problems. To determine whether compat is needed or not, I have used the MSG_CMSG_COMPAT flag, and adjusted the call path for recv and recvfrom to include it, even if those calls do not have a cmsg parameter. I have not solved one small part of the problem, and I don't think it is necessary to: if a 32-bit application uses read() rather than any form of recvmsg() it will still get the wrong (64-bit) event. However, neither do applications actually do this, nor would it be a regression. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/wireless/wext.c')
-rw-r--r--net/wireless/wext.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index ee35e6422f1f..3fe3c2c0ce11 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
421static 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/*
@@ -1348,6 +1363,22 @@ void wireless_send_event(struct net_device * dev,
1348 struct sk_buff *skb; 1363 struct sk_buff *skb;
1349 struct nlmsghdr *nlh; 1364 struct nlmsghdr *nlh;
1350 struct nlattr *nla; 1365 struct nlattr *nla;
1366#ifdef CONFIG_COMPAT
1367 struct __compat_iw_event *compat_event;
1368 struct compat_iw_point compat_wrqu;
1369 struct sk_buff *compskb;
1370#endif
1371
1372 /*
1373 * Nothing in the kernel sends scan events with data, be safe.
1374 * This is necessary because we cannot fix up scan event data
1375 * for compat, due to being contained in 'extra', but normally
1376 * applications are required to retrieve the scan data anyway
1377 * and no data is included in the event, this codifies that
1378 * practice.
1379 */
1380 if (WARN_ON(cmd == SIOCGIWSCAN && extra))
1381 extra = NULL;
1351 1382
1352 /* Get the description of the Event */ 1383 /* Get the description of the Event */
1353 if (cmd <= SIOCIWLAST) { 1384 if (cmd <= SIOCIWLAST) {
@@ -1446,7 +1477,54 @@ void wireless_send_event(struct net_device * dev,
1446 memcpy(((char *) event) + hdr_len, extra, extra_len); 1477 memcpy(((char *) event) + hdr_len, extra, extra_len);
1447 1478
1448 nlmsg_end(skb, nlh); 1479 nlmsg_end(skb, nlh);
1480#ifdef CONFIG_COMPAT
1481 hdr_len = compat_event_type_size[descr->header_type];
1482 event_len = hdr_len + extra_len;
1449 1483
1484 compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
1485 if (!compskb) {
1486 kfree_skb(skb);
1487 return;
1488 }
1489
1490 /* Send via the RtNetlink event channel */
1491 nlh = rtnetlink_ifinfo_prep(dev, compskb);
1492 if (WARN_ON(!nlh)) {
1493 kfree_skb(skb);
1494 kfree_skb(compskb);
1495 return;
1496 }
1497
1498 /* Add the wireless events in the netlink packet */
1499 nla = nla_reserve(compskb, IFLA_WIRELESS, event_len);
1500 if (!nla) {
1501 kfree_skb(skb);
1502 kfree_skb(compskb);
1503 return;
1504 }
1505 compat_event = nla_data(nla);
1506
1507 compat_event->len = event_len;
1508 compat_event->cmd = cmd;
1509 if (descr->header_type == IW_HEADER_TYPE_POINT) {
1510 compat_wrqu.length = wrqu->data.length;
1511 compat_wrqu.flags = wrqu->data.flags;
1512 memcpy(&compat_event->pointer,
1513 ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF,
1514 hdr_len - IW_EV_COMPAT_LCP_LEN);
1515 if (extra_len)
1516 memcpy(((char *) compat_event) + hdr_len,
1517 extra, extra_len);
1518 } else {
1519 /* extra_len must be zero, so no if (extra) needed */
1520 memcpy(&compat_event->pointer, wrqu,
1521 hdr_len - IW_EV_COMPAT_LCP_LEN);
1522 }
1523
1524 nlmsg_end(compskb, nlh);
1525
1526 skb_shinfo(skb)->frag_list = compskb;
1527#endif
1450 skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); 1528 skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
1451 schedule_work(&wireless_nlevent_work); 1529 schedule_work(&wireless_nlevent_work);
1452} 1530}