ip: Add offset parameter to ip_cmsg_recv
authorTom Herbert <therbert@google.com>
Mon, 5 Jan 2015 21:56:17 +0000 (13:56 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 6 Jan 2015 03:44:46 +0000 (22:44 -0500)
Add ip_cmsg_recv_offset function which takes an offset argument
that indicates the starting offset in skb where data is being received
from. This will be useful in the case of UDP and provided checksum
to user space.

ip_cmsg_recv is an inline call to ip_cmsg_recv_offset with offset of
zero.

Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/inet_sock.h
include/uapi/linux/in.h
net/ipv4/ip_sockglue.c
net/ipv4/udp.c

index 605ca421d5ab32f3ae46f6252eeaad1958fcea30..eb16c7beed1e9570168d1ccbc5a7cd9cf31dd52d 100644 (file)
@@ -203,6 +203,7 @@ struct inet_sock {
 #define IP_CMSG_RETOPTS                BIT(4)
 #define IP_CMSG_PASSSEC                BIT(5)
 #define IP_CMSG_ORIGDSTADDR    BIT(6)
+#define IP_CMSG_CHECKSUM       BIT(7)
 
 static inline struct inet_sock *inet_sk(const struct sock *sk)
 {
index c33a65e3d62c85d104d38ab082d997df13cd4c0b..589ced069e8a1a68a9b1c9336517d66a675a75d2 100644 (file)
@@ -109,6 +109,7 @@ struct in_addr {
 
 #define IP_MINTTL       21
 #define IP_NODEFRAG     22
+#define IP_CHECKSUM    23
 
 /* IP_MTU_DISCOVER values */
 #define IP_PMTUDISC_DONT               0       /* Never send DF frames */
index 513d506ffebb811a2211b52fae51609d2243ace2..a317797b3cd020472f3490a82bf7c749b08b8b9e 100644 (file)
@@ -37,6 +37,7 @@
 #include <net/route.h>
 #include <net/xfrm.h>
 #include <net/compat.h>
+#include <net/checksum.h>
 #if IS_ENABLED(CONFIG_IPV6)
 #include <net/transp_v6.h>
 #endif
@@ -96,6 +97,20 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
        put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
 }
 
+static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
+                                 int offset)
+{
+       __wsum csum = skb->csum;
+
+       if (skb->ip_summed != CHECKSUM_COMPLETE)
+               return;
+
+       if (offset != 0)
+               csum = csum_sub(csum, csum_partial(skb->data, offset, 0));
+
+       put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
+}
+
 static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
 {
        char *secdata;
@@ -191,9 +206,16 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
                        return;
        }
 
-       if (flags & IP_CMSG_ORIGDSTADDR)
+       if (flags & IP_CMSG_ORIGDSTADDR) {
                ip_cmsg_recv_dstaddr(msg, skb);
 
+               flags &= ~IP_CMSG_ORIGDSTADDR;
+               if (!flags)
+                       return;
+       }
+
+       if (flags & IP_CMSG_CHECKSUM)
+               ip_cmsg_recv_checksum(msg, skb, offset);
 }
 EXPORT_SYMBOL(ip_cmsg_recv_offset);
 
@@ -533,6 +555,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
        case IP_MULTICAST_ALL:
        case IP_MULTICAST_LOOP:
        case IP_RECVORIGDSTADDR:
+       case IP_CHECKSUM:
                if (optlen >= sizeof(int)) {
                        if (get_user(val, (int __user *) optval))
                                return -EFAULT;
@@ -630,6 +653,19 @@ static int do_ip_setsockopt(struct sock *sk, int level,
                else
                        inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
                break;
+       case IP_CHECKSUM:
+               if (val) {
+                       if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
+                               inet_inc_convert_csum(sk);
+                               inet->cmsg_flags |= IP_CMSG_CHECKSUM;
+                       }
+               } else {
+                       if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
+                               inet_dec_convert_csum(sk);
+                               inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
+                       }
+               }
+               break;
        case IP_TOS:    /* This sets both TOS and Precedence */
                if (sk->sk_type == SOCK_STREAM) {
                        val &= ~INET_ECN_MASK;
@@ -1233,6 +1269,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        case IP_RECVORIGDSTADDR:
                val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
                break;
+       case IP_CHECKSUM:
+               val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
+               break;
        case IP_TOS:
                val = inet->tos;
                break;
index 53358d88f11015e550d5d021af19de472180255f..97ef1f8b7be81ed7d06c599b4158db0507afde44 100644 (file)
@@ -1329,7 +1329,7 @@ try_again:
                *addr_len = sizeof(*sin);
        }
        if (inet->cmsg_flags)
-               ip_cmsg_recv(msg, skb);
+               ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr));
 
        err = copied;
        if (flags & MSG_TRUNC)