Message ID | 1318007970.3988.27.camel@jlt3.sipsolutions.net |
---|---|
State | RFC, archived |
Delegated to: | David Miller |
Headers | show |
On Fri, 2011-10-07 at 19:19 +0200, Johannes Berg wrote: > 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). I should give you a test application. This is based on the code in Documentation/networking/timestamping/timestamping.c and sends the same frames, but to UDP port 1 to a peer you give on the command line. E.g. if you save this file as ack.c, you can do $ ./ack 192.168.100.2 SO_WIFI_STATUS 1 1318010597.494035: sent 124 bytes 1318010597.494084: select 2505916us 1318010597.513457: select returned: 1, success ready for reading 1318010597.513668: received error data, 194 bytes from 69.0.0.152, 72 bytes control messages cmsg len 20: SOL_SOCKET acked: 1 cmsg len 48: IPPROTO_IP IP_RECVERR ee_errno 'No message of desired type' ee_origin 4 => bounced packet => GOT OUR DATA BACK (HURRAY!) 1318010597.513860: select 2486140us (not sure what's with the bogus RX IP, probably just not a valid field here) johannes /* * This program demonstrates how the wifi ack status feature in * the Linux kernel works. It sends some random packets and prints * out whether it was acked. * * Copyright (C) 2009, 2011 Intel Corporation. * Author: Patrick Ohly <patrick.ohly@intel.com> * Author: Johannes Berg <johannes.berg@intel.com> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <net/if.h> #include <asm/types.h> #include <linux/net_tstamp.h> #include <linux/errqueue.h> #ifndef SO_WIFI_STATUS # define SO_WIFI_STATUS 41 # define SCM_WIFI_STATUS SO_WIFI_STATUS #endif #ifndef SO_EE_ORIGIN_TXSTATUS #define SO_EE_ORIGIN_TXSTATUS 4 #endif static void bail(const char *error) { printf("%s: %s\n", error, strerror(errno)); exit(1); } static const unsigned char sync[] = { 0x00, 0x01, 0x00, 0x01, 0x5f, 0x44, 0x46, 0x4c, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, /* fake uuid */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x00, 0x37, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x49, 0x05, 0xcd, 0x01, 0x29, 0xb1, 0x8d, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* fake uuid */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x04, 0x44, 0x46, 0x4c, 0x54, 0x00, 0x00, 0xf0, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xf0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x44, 0x46, 0x4c, 0x54, 0x00, 0x01, /* fake uuid */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len) { struct timeval now; int res; res = sendto(sock, sync, sizeof(sync), 0, addr, addr_len); gettimeofday(&now, 0); if (res < 0) printf("%s: %s\n", "send", strerror(errno)); else printf("%ld.%06ld: sent %d bytes\n", (long)now.tv_sec, (long)now.tv_usec, res); } static void printpacket(struct msghdr *msg, int res, char *data, int sock) { struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; struct cmsghdr *cmsg; struct timeval tv; struct timespec ts; struct timeval now; gettimeofday(&now, 0); printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", (long)now.tv_sec, (long)now.tv_usec, "error", res, inet_ntoa(from_addr->sin_addr), msg->msg_controllen); for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { printf(" cmsg len %zu: ", cmsg->cmsg_len); switch (cmsg->cmsg_level) { case SOL_SOCKET: printf("SOL_SOCKET "); switch (cmsg->cmsg_type) { case SCM_WIFI_STATUS: { int *ack = (void *)CMSG_DATA(cmsg); printf("acked: %d", *ack); break; } default: printf("type %d", cmsg->cmsg_type); break; } break; case IPPROTO_IP: printf("IPPROTO_IP "); switch (cmsg->cmsg_type) { case IP_RECVERR: { struct sock_extended_err *err = (struct sock_extended_err *)CMSG_DATA(cmsg); printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s", strerror(err->ee_errno), err->ee_origin, err->ee_origin == SO_EE_ORIGIN_TXSTATUS ? "bounced packet" : "unexpected origin" ); if (res < sizeof(sync)) printf(" => truncated data?!"); else if (!memcmp(sync, data + res - sizeof(sync), sizeof(sync))) printf(" => GOT OUR DATA BACK (HURRAY!)"); break; } case IP_PKTINFO: { struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); printf("IP_PKTINFO interface index %u", pktinfo->ipi_ifindex); break; } default: printf("type %d", cmsg->cmsg_type); break; } break; default: printf("level %d type %d", cmsg->cmsg_level, cmsg->cmsg_type); break; } printf("\n"); } } static void recvpacket(int sock) { char data[256]; struct msghdr msg; struct iovec entry; struct sockaddr_in from_addr; struct { struct cmsghdr cm; char control[512]; } control; int res; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &entry; msg.msg_iovlen = 1; entry.iov_base = data; entry.iov_len = sizeof(data); msg.msg_name = (caddr_t)&from_addr; msg.msg_namelen = sizeof(from_addr); msg.msg_control = &control; msg.msg_controllen = sizeof(control); res = recvmsg(sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); if (res < 0) { printf("%s %s: %s\n", "recvmsg", "error", strerror(errno)); } else { printpacket(&msg, res, data, sock); } } int main(int argc, char **argv) { int i; int enabled = 1; int sock; struct ifreq device; struct ifreq hwtstamp; struct sockaddr_in addr; struct ip_mreq imr; int val; socklen_t len; struct timeval next; addr.sin_family = AF_INET; addr.sin_port = htons(1); if (argc != 2 || inet_aton(argv[1], &addr.sin_addr) == 0) { printf("%s <ip addr>\n", argv[0]); exit(1); } sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) bail("socket"); /* set socket option for wifi status */ if (setsockopt(sock, SOL_SOCKET, SO_WIFI_STATUS, &enabled, sizeof(enabled)) < 0) { bail("enable ack status"); } /* request IP_PKTINFO for debugging purposes */ if (setsockopt(sock, SOL_IP, IP_PKTINFO, &enabled, sizeof(enabled)) < 0) printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno)); /* verify socket options */ len = sizeof(val); if (getsockopt(sock, SOL_SOCKET, SO_WIFI_STATUS, &val, &len) < 0) printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno)); else printf("SO_WIFI_STATUS %d\n", val); /* send packets forever every five seconds */ gettimeofday(&next, 0); next.tv_sec = (next.tv_sec + 1) / 5 * 5; next.tv_usec = 0; while (1) { struct timeval now; struct timeval delta; long delta_us; int res; fd_set readfs, errorfs; gettimeofday(&now, 0); delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 + (long)(next.tv_usec - now.tv_usec); if (delta_us > 0) { /* continue waiting for timeout or data */ delta.tv_sec = delta_us / 1000000; delta.tv_usec = delta_us % 1000000; FD_ZERO(&readfs); FD_ZERO(&errorfs); FD_SET(sock, &readfs); FD_SET(sock, &errorfs); printf("%ld.%06ld: select %ldus\n", (long)now.tv_sec, (long)now.tv_usec, delta_us); res = select(sock + 1, &readfs, 0, &errorfs, &delta); gettimeofday(&now, 0); printf("%ld.%06ld: select returned: %d, %s\n", (long)now.tv_sec, (long)now.tv_usec, res, res < 0 ? strerror(errno) : "success"); if (res > 0) { if (FD_ISSET(sock, &readfs)) printf("ready for reading\n"); if (FD_ISSET(sock, &errorfs)) printf("has error\n"); recvpacket(sock); } } else { /* write one packet */ sendpacket(sock, (struct sockaddr *)&addr, sizeof(addr)); next.tv_sec += 5; continue; } } return 0; } -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
--- a/include/asm-generic/socket.h 2011-10-07 18:59:12.000000000 +0200 +++ b/include/asm-generic/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/net/core/sock.c 2011-10-07 18:59:12.000000000 +0200 +++ b/net/core/sock.c 2011-10-07 18:59:12.000000000 +0200 @@ -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, 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; } --- a/include/net/sock.h 2011-10-07 18:59:12.000000000 +0200 +++ b/include/net/sock.h 2011-10-07 18:59:12.000000000 +0200 @@ -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) @@ -1705,7 +1706,10 @@ static inline int sock_intr_errno(long t 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); +/* XXX: rename this function now? */ static __inline__ void sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { @@ -1732,6 +1736,9 @@ sock_recv_timestamp(struct msghdr *msg, __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, --- a/arch/alpha/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/alpha/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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. */ --- a/arch/arm/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/arm/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/avr32/include/asm/socket.h 2011-10-07 18:59:10.000000000 +0200 +++ b/arch/avr32/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/cris/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/cris/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/frv/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/frv/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/h8300/include/asm/socket.h 2011-10-07 18:59:10.000000000 +0200 +++ b/arch/h8300/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/ia64/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/ia64/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/m32r/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/m32r/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/m68k/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/m68k/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/mips/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/mips/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -82,6 +82,9 @@ To add: #define SO_REUSEPORT 0x0200 /* A #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #ifdef __KERNEL__ /** sock_type - Socket types --- a/arch/mn10300/include/asm/socket.h 2011-10-07 18:59:10.000000000 +0200 +++ b/arch/mn10300/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/parisc/include/asm/socket.h 2011-10-07 18:59:10.000000000 +0200 +++ b/arch/parisc/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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. */ --- a/arch/powerpc/include/asm/socket.h 2011-10-07 18:59:11.000000000 +0200 +++ b/arch/powerpc/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/s390/include/asm/socket.h 2011-10-07 18:59:10.000000000 +0200 +++ b/arch/s390/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/arch/sparc/include/asm/socket.h 2011-10-07 18:59:10.000000000 +0200 +++ b/arch/sparc/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 --- a/arch/xtensa/include/asm/socket.h 2011-10-07 18:59:10.000000000 +0200 +++ b/arch/xtensa/include/asm/socket.h 2011-10-07 18:59:12.000000000 +0200 @@ -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 */ --- a/include/linux/errqueue.h 2011-10-07 18:59:11.000000000 +0200 +++ b/include/linux/errqueue.h 2011-10-07 18:59:12.000000000 +0200 @@ -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)) --- a/include/linux/skbuff.h 2011-10-07 18:59:11.000000000 +0200 +++ b/include/linux/skbuff.h 2011-10-07 18:59:12.000000000 +0200 @@ -190,6 +190,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, }; /* @@ -322,6 +325,8 @@ typedef unsigned char *sk_buff_data_t; * @queue_mapping: Queue mapping for multiqueue devices * @ndisc_nodetype: router type (from link layer) * @ooo_okay: allow the mapping of a socket to a queue to be changed + * @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 @@ -414,10 +419,10 @@ struct sk_buff { __u8 ndisc_nodetype:2; #endif __u8 ooo_okay:1; + __u8 wifi_acked_valid:1; + __u8 wifi_acked:1; kmemcheck_bitfield_end(flags2); - /* 0/13 bit hole */ - #ifdef CONFIG_NET_DMA dma_cookie_t dma_cookie; #endif @@ -2062,6 +2067,15 @@ static inline void skb_tx_timestamp(stru sw_tx_timestamp(skb); } +/** + * skb_complete_wifi_ack - deliver cloned skb with wifi status + * + * @skb: clone of the 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); --- a/net/core/skbuff.c 2011-10-07 18:59:12.000000000 +0200 +++ b/net/core/skbuff.c 2011-10-07 18:59:24.000000000 +0200 @@ -3150,6 +3150,26 @@ void skb_tstamp_tx(struct sk_buff *orig_ } 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 --- a/net/socket.c 2011-10-07 18:59:12.000000000 +0200 +++ b/net/socket.c 2011-10-07 18:59:12.000000000 +0200 @@ -531,6 +531,7 @@ void sock_release(struct socket *sock) } EXPORT_SYMBOL(sock_release); +/* XXX: rename this function now */ int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags) { *tx_flags = 0; @@ -538,6 +539,8 @@ int sock_tx_timestamp(struct sock *sk, _ *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 +677,22 @@ void __sock_recv_timestamp(struct msghdr } 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) { --- a/net/mac80211/status.c 2011-10-07 18:59:12.000000000 +0200 +++ b/net/mac80211/status.c 2011-10-07 19:05:24.000000000 +0200 @@ -252,8 +252,9 @@ void ieee80211_tx_status(struct ieee8021 struct sta_info *sta, *tmp; int retry_count = -1, i; int rates_idx = -1; - bool send_to_cooked; + bool send_to_cooked, need_for_monitors; bool acked; + bool multicast; struct ieee80211_bar *bar; u16 tid; @@ -278,6 +279,7 @@ void ieee80211_tx_status(struct ieee8021 sband = local->hw.wiphy->bands[info->band]; fc = hdr->frame_control; + multicast = is_multicast_ether_addr(hdr->addr1); for_each_sta_info(local, hdr->addr1, sta, tmp) { /* skip wrong virtual interface */ @@ -443,9 +445,6 @@ void ieee80211_tx_status(struct ieee8021 !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); } - /* this was a transmitted frame, but now we want to reuse it */ - skb_orphan(skb); - /* Need to make a copy before skb->cb gets cleared */ send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) || (type != IEEE80211_FTYPE_DATA); @@ -454,13 +453,33 @@ void ieee80211_tx_status(struct ieee8021 * This is a bit racy but we can avoid a lot of work * with this test... */ - if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) { - dev_kfree_skb(skb); - return; - } + need_for_monitors = local->monitors || + (send_to_cooked && local->cooked_mntrs); /* send frame to monitor interfaces now */ + if (!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) { + struct sk_buff *mon_skb = NULL; + + if (need_for_monitors) + mon_skb = skb_clone(skb, GFP_ATOMIC); + + /* consumes skb */ + skb_complete_wifi_ack(skb, info->flags & IEEE80211_TX_STAT_ACK); + + if (!mon_skb) + return; + + skb = mon_skb; + } else if (!need_for_monitors) { + dev_kfree_skb(skb); + return; + } else { + /* this was a transmitted frame, but now we want to reuse it */ + skb_orphan(skb); + } + if (skb_headroom(skb) < sizeof(*rthdr)) { printk(KERN_ERR "ieee80211_tx_status: headroom too small\n"); dev_kfree_skb(skb); --- a/net/mac80211/tx.c 2011-10-07 18:59:12.000000000 +0200 +++ b/net/mac80211/tx.c 2011-10-07 18:59:12.000000000 +0200 @@ -1686,6 +1686,7 @@ netdev_tx_t ieee80211_subif_start_xmit(s bool wme_sta = false, authorized = false, tdls_auth = false; struct sk_buff *tmp_skb; bool tdls_direct = false; + bool multicast; if (unlikely(skb->len < ETH_HLEN)) { ret = NETDEV_TX_OK; @@ -1872,7 +1873,8 @@ netdev_tx_t ieee80211_subif_start_xmit(s * if it is a multicast address (which can only happen * in AP mode) */ - if (!is_multicast_ether_addr(hdr.addr1)) { + multicast = is_multicast_ether_addr(hdr.addr1); + if (!multicast) { rcu_read_lock(); sta = sta_info_get(sdata, hdr.addr1); if (sta) { @@ -2019,6 +2021,10 @@ netdev_tx_t ieee80211_subif_start_xmit(s memset(info, 0, sizeof(*info)); dev->trans_start = jiffies; + + if (!multicast && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) + info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS; + ieee80211_xmit(sdata, skb); return NETDEV_TX_OK;