net-timestamp: no-payload option
authorWillem de Bruijn <willemb@google.com>
Fri, 30 Jan 2015 18:29:31 +0000 (13:29 -0500)
committerDavid S. Miller <davem@davemloft.net>
Tue, 3 Feb 2015 02:46:51 +0000 (18:46 -0800)
Add timestamping option SOF_TIMESTAMPING_OPT_TSONLY. For transmit
timestamps, this loops timestamps on top of empty packets.

Doing so reduces the pressure on SO_RCVBUF. Payload inspection and
cmsg reception (aside from timestamps) are no longer possible. This
works together with a follow on patch that allows administrators to
only allow tx timestamping if it does not loop payload or metadata.

Signed-off-by: Willem de Bruijn <willemb@google.com>
----

Changes (rfc -> v1)
  - add documentation
  - remove unnecessary skb->len test (thanks to Richard Cochran)
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/timestamping.txt
include/uapi/linux/net_tstamp.h
net/core/skbuff.c
net/ipv4/ip_sockglue.c
net/ipv6/datagram.c
net/rxrpc/ar-error.c

index a5c784c893122788c8fd1d4db270576330b02518..5f0922613f1a8db7963fe3c44d21280d7290d84e 100644 (file)
@@ -162,6 +162,27 @@ SOF_TIMESTAMPING_OPT_CMSG:
   option IP_PKTINFO simultaneously.
 
 
+SOF_TIMESTAMPING_OPT_TSONLY:
+
+  Applies to transmit timestamps only. Makes the kernel return the
+  timestamp as a cmsg alongside an empty packet, as opposed to
+  alongside the original packet. This reduces the amount of memory
+  charged to the socket's receive budget (SO_RCVBUF) and delivers
+  the timestamp even if sysctl net.core.tstamp_allow_data is 0.
+  This option disables SOF_TIMESTAMPING_OPT_CMSG.
+
+
+New applications are encouraged to pass SOF_TIMESTAMPING_OPT_ID to
+disambiguate timestamps and SOF_TIMESTAMPING_OPT_TSONLY to operate
+regardless of the setting of sysctl net.core.tstamp_allow_data.
+
+An exception is when a process needs additional cmsg data, for
+instance SOL_IP/IP_PKTINFO to detect the egress network interface.
+Then pass option SOF_TIMESTAMPING_OPT_CMSG. This option depends on
+having access to the contents of the original packet, so cannot be
+combined with SOF_TIMESTAMPING_OPT_TSONLY.
+
+
 1.4 Bytestream Timestamps
 
 The SO_TIMESTAMPING interface supports timestamping of bytes in a
index edbc888ceb51c1ffa66b11c1f58eea0343b80faf..6d1abea9746ea7b7d1128a0c1fd1a3ad5f45b96d 100644 (file)
@@ -24,8 +24,9 @@ enum {
        SOF_TIMESTAMPING_TX_SCHED = (1<<8),
        SOF_TIMESTAMPING_TX_ACK = (1<<9),
        SOF_TIMESTAMPING_OPT_CMSG = (1<<10),
+       SOF_TIMESTAMPING_OPT_TSONLY = (1<<11),
 
-       SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_CMSG,
+       SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_TSONLY,
        SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) |
                                 SOF_TIMESTAMPING_LAST
 };
index 56db472e9b864e805e0ab36dd73a0404d2fc66d5..65a3798f43e697f5cb7a5b6039a203069698cbbe 100644 (file)
@@ -3710,19 +3710,28 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb,
                     struct sock *sk, int tstype)
 {
        struct sk_buff *skb;
+       bool tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY;
 
        if (!sk)
                return;
 
-       if (hwtstamps)
-               *skb_hwtstamps(orig_skb) = *hwtstamps;
+       if (tsonly)
+               skb = alloc_skb(0, GFP_ATOMIC);
        else
-               orig_skb->tstamp = ktime_get_real();
-
-       skb = skb_clone(orig_skb, GFP_ATOMIC);
+               skb = skb_clone(orig_skb, GFP_ATOMIC);
        if (!skb)
                return;
 
+       if (tsonly) {
+               skb_shinfo(skb)->tx_flags = skb_shinfo(orig_skb)->tx_flags;
+               skb_shinfo(skb)->tskey = skb_shinfo(orig_skb)->tskey;
+       }
+
+       if (hwtstamps)
+               *skb_hwtstamps(skb) = *hwtstamps;
+       else
+               skb->tstamp = ktime_get_real();
+
        __skb_complete_tx_timestamp(skb, sk, tstype);
 }
 EXPORT_SYMBOL_GPL(__skb_tstamp_tx);
index db5e0f81ce0a45713357b0e71bd2d794321edfb9..31d8c71986b40e28e5c84f5e62d474a639d3bf91 100644 (file)
@@ -483,7 +483,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
 
        serr = SKB_EXT_ERR(skb);
 
-       if (sin) {
+       if (sin && skb->len) {
                sin->sin_family = AF_INET;
                sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
                                                   serr->addr_offset);
@@ -496,8 +496,9 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
        sin = &errhdr.offender;
        memset(sin, 0, sizeof(*sin));
 
-       if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
-           ipv4_pktinfo_prepare_errqueue(sk, skb, serr->ee.ee_origin)) {
+       if (skb->len &&
+           (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
+            ipv4_pktinfo_prepare_errqueue(sk, skb, serr->ee.ee_origin))) {
                sin->sin_family = AF_INET;
                sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
                if (inet_sk(sk)->cmsg_flags)
index 49f5e73db1224549c7f3fc156209a85ed6ce4056..c215be70cac08af78953ea860c5d67cf9d3fa642 100644 (file)
@@ -369,7 +369,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
 
        serr = SKB_EXT_ERR(skb);
 
-       if (sin) {
+       if (sin && skb->len) {
                const unsigned char *nh = skb_network_header(skb);
                sin->sin6_family = AF_INET6;
                sin->sin6_flowinfo = 0;
@@ -394,8 +394,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
        memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
        sin = &errhdr.offender;
        memset(sin, 0, sizeof(*sin));
-
-       if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
+       if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL && skb->len) {
                sin->sin6_family = AF_INET6;
                if (np->rxopt.all) {
                        if (serr->ee.ee_origin != SO_EE_ORIGIN_ICMP &&
index 74c0fcd36838dfc7326eeddb7726483576da0413..5394b6be46ecd5ebb6c677b745a4848977433f70 100644 (file)
@@ -42,6 +42,11 @@ void rxrpc_UDP_error_report(struct sock *sk)
                _leave("UDP socket errqueue empty");
                return;
        }
+       if (!skb->len) {
+               _leave("UDP empty message");
+               kfree_skb(skb);
+               return;
+       }
 
        rxrpc_new_skb(skb);