Thread (5 messages) flat view 5 messages, 2 authors, 2014-12-05
STALE4281d

Revision v1 of 2 in this series.

Revisions (2)
  1. v1 current
  2. v1 [diff vs current]

[PATCH net-next 3/3] ip: Add support for IP_CHECKSUM cmsg

From: Tom Herbert <hidden>
Date: 2014-12-04 00:45:05
Subsystem: networking [general], networking [ipv4/ipv6], the rest, user datagram protocol (udp) · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds, Willem de Bruijn

New cmsg type is IP_CHECKSUM under SOL_IP. Enabled by standard
setsockopt.

The value returned is the unfolded 32 bit checksum of the packet
being received starting from the first byte returned in recvmsg
through the end of the packet (truncation is disregarded).

Modified UDP to postpull checksum beyond UDP header before returning
checksum for UDP data to userspace.

Signed-off-by: Tom Herbert <redacted>
---
 include/net/inet_sock.h |  1 +
 include/uapi/linux/in.h |  1 +
 net/ipv4/ip_sockglue.c  | 34 +++++++++++++++++++++++++++++++++-
 net/ipv4/udp.c          | 10 +++++++++-
 4 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index 4091fab..2823fc0 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -203,6 +203,7 @@ struct inet_sock {
 #define IP_CMSG_RETOPTS		(1 << 4)
 #define IP_CMSG_PASSSEC		(1 << 5)
 #define IP_CMSG_ORIGDSTADDR	(1 << 6)
+#define IP_CMSG_CHECKSUM	(1 << 7)
 
 static inline struct inet_sock *inet_sk(const struct sock *sk)
 {
diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h
index c33a65e..589ced0 100644
--- a/include/uapi/linux/in.h
+++ b/include/uapi/linux/in.h
@@ -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 */
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index d4406aa..054280f 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -96,6 +96,14 @@ 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)
+{
+	if (skb->ip_summed != CHECKSUM_COMPLETE)
+		return;
+
+	put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &skb->csum);
+}
+
 static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
 {
 	char *secdata;
@@ -190,9 +198,16 @@ void ip_cmsg_recv(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);
 }
 EXPORT_SYMBOL(ip_cmsg_recv);
 
@@ -512,6 +527,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;
@@ -609,6 +625,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;
@@ -1212,6 +1241,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;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 221b53f..bba2e06 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1315,8 +1315,16 @@ try_again:
 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 		*addr_len = sizeof(*sin);
 	}
-	if (inet->cmsg_flags)
+	if (inet->cmsg_flags) {
+		/* Pull checksum past UDP header in case we are providing
+		 * checksum in cmsg.
+		 */
+		if (inet->cmsg_flags & IP_CMSG_CHECKSUM)
+			skb_postpull_rcsum(skb, skb->data,
+					   sizeof(struct udphdr));
+
 		ip_cmsg_recv(msg, skb);
+	}
 
 	err = copied;
 	if (flags & MSG_TRUNC)
-- 
2.2.0.rc0.207.ga3a616c
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help