net: add wireless TX status socket option
authorJohannes Berg <johannes.berg@intel.com>
Wed, 9 Nov 2011 09:15:42 +0000 (10:15 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 9 Nov 2011 21:01:02 +0000 (16:01 -0500)
The 802.1X EAPOL handshake hostapd does requires
knowing whether the frame was ack'ed by the peer.
Currently, we fudge this pretty badly by not even
transmitting the frame as a normal data frame but
injecting it with radiotap and getting the status
out of radiotap monitor as well. This is rather
complex, confuses users (mon.wlan0 presence) and
doesn't work with all hardware.

To get rid of that hack, introduce a real wifi TX
status option for data frame transmissions.

This works similar to the existing TX timestamping
in that it reflects the SKB back to the socket's
error queue with a SCM_WIFI_STATUS cmsg that has
an int indicating ACK status (0/1).

Since it is possible that at some point we will
want to have TX timestamping and wifi status in a
single errqueue SKB (there's little point in not
doing that), redefine SO_EE_ORIGIN_TIMESTAMPING
to SO_EE_ORIGIN_TXSTATUS which can collect more
than just the timestamp; keep the old constant
as an alias of course. Currently the internal APIs
don't make that possible, but it wouldn't be hard
to split them up in a way that makes it possible.

Thanks to Neil Horman for helping me figure out
the functions that add the control messages.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
23 files changed:
arch/alpha/include/asm/socket.h
arch/arm/include/asm/socket.h
arch/avr32/include/asm/socket.h
arch/cris/include/asm/socket.h
arch/frv/include/asm/socket.h
arch/h8300/include/asm/socket.h
arch/ia64/include/asm/socket.h
arch/m32r/include/asm/socket.h
arch/m68k/include/asm/socket.h
arch/mips/include/asm/socket.h
arch/mn10300/include/asm/socket.h
arch/parisc/include/asm/socket.h
arch/powerpc/include/asm/socket.h
arch/s390/include/asm/socket.h
arch/sparc/include/asm/socket.h
arch/xtensa/include/asm/socket.h
include/asm-generic/socket.h
include/linux/errqueue.h
include/linux/skbuff.h
include/net/sock.h
net/core/skbuff.c
net/core/sock.c
net/socket.c

index 06edfefc337332915833c7141fac17f2b1a60bba..082355f159e67930c284b76d33a4bb7f9a090b3a 100644 (file)
@@ -69,6 +69,9 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
index 90ffd04b8e74fb191a47ab79211cf1e128ad7b87..dec6f9afb3cf949de01bb4928dc9ba1429bc9b59 100644 (file)
@@ -62,4 +62,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_SOCKET_H */
index c8d1fae494763c2459279afd824dc576b7471795..247b88c760bef0615b9b470defb29e8b7645bd1d 100644 (file)
@@ -62,4 +62,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* __ASM_AVR32_SOCKET_H */
index 1a4a61909ca8705b3d0f11f186c717ba3b5141f1..e269264df7c4f38b35c8aafa07579a935afd1a23 100644 (file)
@@ -64,6 +64,9 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_SOCKET_H */
 
 
index a6b26880c1ec2e79e316ff5a0f32bbc7fe09a79e..ce80fdadcce57ad1975c9daab73c823721a21f10 100644 (file)
@@ -62,5 +62,8 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_SOCKET_H */
 
index 04c0f4596eb5b8ae072109677a35069a7c15d4a4..cf1daab6f27efb11007af681ee93fb29f64bb63c 100644 (file)
@@ -62,4 +62,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_SOCKET_H */
index 51427eaa51ba996031aa40bf6800f35917a430b3..4b03664e3fb50534b27d9429c951f8b78a272210 100644 (file)
@@ -71,4 +71,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_IA64_SOCKET_H */
index 469787c30098aea921ddc988087162749840b3de..e8b8c5bb053c12135328d43fbc00fbe12b7db1b9 100644 (file)
@@ -62,4 +62,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_M32R_SOCKET_H */
index 9bf49c87d954a6c5ecf89cc2779ae8642dbe4f34..d4708ce466e068d6bc8935cc222eb923fc27b1a7 100644 (file)
@@ -62,4 +62,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_SOCKET_H */
index 9de5190f248743a5c8a3926993ad88e5025d4437..ad5c0a7a02a7e34eaff203cf40d0d4b2f235c795 100644 (file)
@@ -82,6 +82,9 @@ To add: #define SO_REUSEPORT 0x0200   /* Allow local address and port reuse.  */
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #ifdef __KERNEL__
 
 /** sock_type - Socket types
index 4e60c42812880b8e3dcc05ddaf90e1815cfc198c..876356d7852248898968f012c8f70c22ad474e1d 100644 (file)
@@ -62,4 +62,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_SOCKET_H */
index 225b7d6a1a0af44447521de4b133efa6be591bf8..d28c51b61067edab8da863ef0bd76861e2a0b4ff 100644 (file)
@@ -61,6 +61,9 @@
 
 #define SO_RXQ_OVFL             0x4021
 
+#define SO_WIFI_STATUS         0x4022
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
index 866f7606da6803924f17d71f74ac4ed70309f8f3..2fc2af8fbf59b892598844db7a254da4f4251b57 100644 (file)
@@ -69,4 +69,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_POWERPC_SOCKET_H */
index fdff1e995c73d45145ceb56afcf0f26a948636e6..67b5c1b14b51177851efca5e6d86f5a2729e61ea 100644 (file)
@@ -70,4 +70,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _ASM_SOCKET_H */
index 9d3fefcff2f576b277ebc776c3bd7ddcada49272..8af1b64168b3abb17ddcf2711b9de2ca07b5ce58 100644 (file)
@@ -58,6 +58,9 @@
 
 #define SO_RXQ_OVFL             0x0024
 
+#define SO_WIFI_STATUS         0x0025
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index cbdf2ffaacff9cdc82afb41be8723a55b141c390..bb06968be227b0337ab7d77dbee537204fa898c4 100644 (file)
@@ -73,4 +73,7 @@
 
 #define SO_RXQ_OVFL             40
 
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS                SO_WIFI_STATUS
+
 #endif /* _XTENSA_SOCKET_H */
index 9a6115e7cf631583f808d6807619c0207c0a5a0c..49c1704173e757a5e75db1dca27fbf4081aa2dd7 100644 (file)
@@ -64,4 +64,7 @@
 #define SO_DOMAIN              39
 
 #define SO_RXQ_OVFL             40
+
+#define SO_WIFI_STATUS         41
+#define SCM_WIFI_STATUS        SO_WIFI_STATUS
 #endif /* __ASM_GENERIC_SOCKET_H */
index 034072cea853eafc7f4eb5bed66649ee129da448..c9f522bd17e4df214e297eafa73cae7003f1b763 100644 (file)
@@ -17,7 +17,8 @@ struct sock_extended_err {
 #define SO_EE_ORIGIN_LOCAL     1
 #define SO_EE_ORIGIN_ICMP      2
 #define SO_EE_ORIGIN_ICMP6     3
-#define SO_EE_ORIGIN_TIMESTAMPING 4
+#define SO_EE_ORIGIN_TXSTATUS  4
+#define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS
 
 #define SO_EE_OFFENDER(ee)     ((struct sockaddr*)((ee)+1))
 
index 6a6b352326d7002197158f7ade024fffec3b07ae..ff7e1306a2d2c79703f8c4358246253578068fa1 100644 (file)
@@ -218,6 +218,9 @@ enum {
 
        /* device driver supports TX zero-copy buffers */
        SKBTX_DEV_ZEROCOPY = 1 << 4,
+
+       /* generate wifi status information (where possible) */
+       SKBTX_WIFI_STATUS = 1 << 5,
 };
 
 /*
@@ -352,6 +355,8 @@ typedef unsigned char *sk_buff_data_t;
  *     @ooo_okay: allow the mapping of a socket to a queue to be changed
  *     @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport
  *             ports.
+ *     @wifi_acked_valid: wifi_acked was set
+ *     @wifi_acked: whether frame was acked on wifi or not
  *     @dma_cookie: a cookie to one of several possible DMA operations
  *             done by skb DMA functions
  *     @secmark: security marking
@@ -445,10 +450,11 @@ struct sk_buff {
 #endif
        __u8                    ooo_okay:1;
        __u8                    l4_rxhash:1;
+       __u8                    wifi_acked_valid:1;
+       __u8                    wifi_acked:1;
+       /* 10/12 bit hole (depending on ndisc_nodetype presence) */
        kmemcheck_bitfield_end(flags2);
 
-       /* 0/13 bit hole */
-
 #ifdef CONFIG_NET_DMA
        dma_cookie_t            dma_cookie;
 #endif
@@ -2263,6 +2269,15 @@ static inline void skb_tx_timestamp(struct sk_buff *skb)
        sw_tx_timestamp(skb);
 }
 
+/**
+ * skb_complete_wifi_ack - deliver skb with wifi status
+ *
+ * @skb: the original outgoing packet
+ * @acked: ack status
+ *
+ */
+void skb_complete_wifi_ack(struct sk_buff *skb, bool acked);
+
 extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len);
 extern __sum16 __skb_checksum_complete(struct sk_buff *skb);
 
index 5ac682f73d6389ab4fe4977fb183ab13044929b1..fa6f5381c5d6299b6cbd6acade5a3ca18e48dade 100644 (file)
@@ -564,6 +564,7 @@ enum sock_flags {
        SOCK_FASYNC, /* fasync() active */
        SOCK_RXQ_OVFL,
        SOCK_ZEROCOPY, /* buffers from userspace */
+       SOCK_WIFI_STATUS, /* push wifi status to userspace */
 };
 
 static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
@@ -1714,6 +1715,8 @@ static inline int sock_intr_errno(long timeo)
 
 extern void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
        struct sk_buff *skb);
+extern void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
+       struct sk_buff *skb);
 
 static __inline__ void
 sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
@@ -1741,6 +1744,9 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
                __sock_recv_timestamp(msg, sk, skb);
        else
                sk->sk_stamp = kt;
+
+       if (sock_flag(sk, SOCK_WIFI_STATUS) && skb->wifi_acked_valid)
+               __sock_recv_wifi_status(msg, sk, skb);
 }
 
 extern void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
index ca4db40e75b84becaab9c9acef48e5677d202a6c..2f6babd5a5703061f67e007d0395a9bc8e6ab607 100644 (file)
@@ -3168,6 +3168,26 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
 }
 EXPORT_SYMBOL_GPL(skb_tstamp_tx);
 
+void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
+{
+       struct sock *sk = skb->sk;
+       struct sock_exterr_skb *serr;
+       int err;
+
+       skb->wifi_acked_valid = 1;
+       skb->wifi_acked = acked;
+
+       serr = SKB_EXT_ERR(skb);
+       memset(serr, 0, sizeof(*serr));
+       serr->ee.ee_errno = ENOMSG;
+       serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
+
+       err = sock_queue_err_skb(sk, skb);
+       if (err)
+               kfree_skb(skb);
+}
+EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
+
 
 /**
  * skb_partial_csum_set - set up and verify partial csum values for packet
index 4ed7b1d12f5ecde5b8c2119c0d4cfaaa765ff470..cbdf51c0d5acbb7334ec4e6c3c621ab882d937d8 100644 (file)
@@ -740,6 +740,11 @@ set_rcvbuf:
        case SO_RXQ_OVFL:
                sock_valbool_flag(sk, SOCK_RXQ_OVFL, valbool);
                break;
+
+       case SO_WIFI_STATUS:
+               sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool);
+               break;
+
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -961,6 +966,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                v.val = !!sock_flag(sk, SOCK_RXQ_OVFL);
                break;
 
+       case SO_WIFI_STATUS:
+               v.val = !!sock_flag(sk, SOCK_WIFI_STATUS);
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
index 2877647f347b06e75aaaa7fd355799a43cd65bf3..425ef42704605e83c0f366fc1edb000d18eeb419 100644 (file)
@@ -538,6 +538,8 @@ int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags)
                *tx_flags |= SKBTX_HW_TSTAMP;
        if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
                *tx_flags |= SKBTX_SW_TSTAMP;
+       if (sock_flag(sk, SOCK_WIFI_STATUS))
+               *tx_flags |= SKBTX_WIFI_STATUS;
        return 0;
 }
 EXPORT_SYMBOL(sock_tx_timestamp);
@@ -674,6 +676,22 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 }
 EXPORT_SYMBOL_GPL(__sock_recv_timestamp);
 
+void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
+       struct sk_buff *skb)
+{
+       int ack;
+
+       if (!sock_flag(sk, SOCK_WIFI_STATUS))
+               return;
+       if (!skb->wifi_acked_valid)
+               return;
+
+       ack = skb->wifi_acked;
+
+       put_cmsg(msg, SOL_SOCKET, SCM_WIFI_STATUS, sizeof(ack), &ack);
+}
+EXPORT_SYMBOL_GPL(__sock_recv_wifi_status);
+
 static inline void sock_recv_drops(struct msghdr *msg, struct sock *sk,
                                   struct sk_buff *skb)
 {