Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 12 Oct 2014 17:07:16 +0000 (UTC)
From:      Michael Tuexen <tuexen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-releng@freebsd.org
Subject:   svn commit: r272991 - in releng/10.1: share/man/man4 sys/netinet sys/netinet6
Message-ID:  <201410121707.s9CH7GYK058546@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tuexen
Date: Sun Oct 12 17:07:15 2014
New Revision: 272991
URL: https://svnweb.freebsd.org/changeset/base/272991

Log:
  MFC r272627:
  Checksum coverage values larger than 65535 for UDPLite are invalid.
  Check for this when the user calls setsockopt using UDPLITE_{SEND,RECV}CSCOV.
  
  MFC r272628:
  When plen != ulen, it should only be checked when this is UDP.
  
  MFC r272645:
  If the checksum coverage field in the UDPLITE header is the length
  of the complete UDPLITE packet, the packet has full checksum coverage.
  So fix the condition.
  
  MFC r272660:
  UDPLite requires a checksum. Therefore, discard a received packet if
  the checksum is 0.
  
  MFC r272661:
  The default for UDPLITE_RECV_CSCOV is zero. RFC 3828 recommend
  that this means full checksum coverage for received packets.
  If an application is willing to accept packets with partial
  coverage, it is expected to use the socket option and provide
  the minimum coverage it accepts.
  
  MFC r272662:
  Fix the checksum computation for UDPLite/IPv6. This requires the
  usage of a function computing the checksum only over a part of the function.
  Therefore introduce in6_cksum_partial() and implement in6_cksum() based
  on that.
  While there, ensure that the UDPLite packet contains at least enough bytes
  to contain the header.
  
  MFC r272663:
  Check for UDP/IPv6 packets that the length in the UDP header is at least
  the minimum. Make the check similar to the one for UDPLite/IPv6.
  
  MFC r272664:
  UDP/IPv6 and UDPLite/IPv6 require a checksum. So check for it.
  
  MFC r272754:
  Fix a bug introduced in
  https://svnweb.freebsd.org/base?view=revision&revision=272347
  
  Approved by: re (gjb)

Modified:
  releng/10.1/share/man/man4/udplite.4
  releng/10.1/sys/netinet/udp_usrreq.c
  releng/10.1/sys/netinet6/in6.h
  releng/10.1/sys/netinet6/in6_cksum.c
  releng/10.1/sys/netinet6/udp6_usrreq.c
Directory Properties:
  releng/10.1/   (props changed)

Modified: releng/10.1/share/man/man4/udplite.4
==============================================================================
--- releng/10.1/share/man/man4/udplite.4	Sun Oct 12 17:03:47 2014	(r272990)
+++ releng/10.1/share/man/man4/udplite.4	Sun Oct 12 17:07:15 2014	(r272991)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd April 7, 2014
+.Dd October 1, 2014
 .Dt UDPLITE 4
 .Os
 .Sh NAME
@@ -55,16 +55,16 @@ and tested with
 .Bl -tag -width ".Dv UDPLITE_SEND_CSCOV"
 .It Dv UDPLITE_SEND_CSCOV
 This option sets the sender checksum coverage.
-A value of zero indicates that the entire packet
-is covered by the checksum. 
-A value of 1 to 7 must be discarded by the receiver.
+A value of zero indicates that all sent packets will have
+full checksum coverage.
+A value of 8 to 65535 limits the checksum coverage of all sent packets
+to the value given.
 .It Dv UDPLITE_RECV_CSCOV
 This option is the receiver-side analogue.
-It is truly optional, i.e. not required to enable traffic
-with partial checksum coverage.
-Its function is that of a traffic filter:
-when enabled, it instructs the kernel to drop
-all packets which have a coverage less than this value.
+A value of zero instructs the kernel to drop all received packets
+not having full checksum coverage.
+A value of 8 to 65535 instructs the kernel to drop all received
+packets with a partial checksum coverage smaller than the value specified.
 .El
 .Sh ERRORS
 A socket operation may fail with one of the following errors returned:

Modified: releng/10.1/sys/netinet/udp_usrreq.c
==============================================================================
--- releng/10.1/sys/netinet/udp_usrreq.c	Sun Oct 12 17:03:47 2014	(r272990)
+++ releng/10.1/sys/netinet/udp_usrreq.c	Sun Oct 12 17:07:15 2014	(r272991)
@@ -434,9 +434,10 @@ udp_input(struct mbuf *m, int off)
 	 */
 	len = ntohs((u_short)uh->uh_ulen);
 	ip_len = ntohs(ip->ip_len) - iphlen;
-	if (pr == IPPROTO_UDPLITE && len == 0) {
+	if (pr == IPPROTO_UDPLITE && (len == 0 || len == ip_len)) {
 		/* Zero means checksum over the complete packet. */
-		len = ip_len;
+		if (len == 0)
+			len = ip_len;
 		cscov_partial = 0;
 	}
 	if (ip_len != len) {
@@ -487,8 +488,16 @@ udp_input(struct mbuf *m, int off)
 			m_freem(m);
 			return;
 		}
-	} else
-		UDPSTAT_INC(udps_nosum);
+	} else {
+		if (pr == IPPROTO_UDP) {
+			UDPSTAT_INC(udps_nosum);
+		} else {
+			/* UDPLite requires a checksum */
+			/* XXX: What is the right UDPLite MIB counter here? */
+			m_freem(m);
+			return;
+		}
+	}
 
 	pcbinfo = get_inpcbinfo(pr);
 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
@@ -670,7 +679,7 @@ udp_input(struct mbuf *m, int off)
 		struct udpcb *up;
 
 		up = intoudpcb(inp);
-		if (up->u_rxcslen > len) {
+		if (up->u_rxcslen == 0 || up->u_rxcslen > len) {
 			INP_RUNLOCK(inp);
 			m_freem(m);
 			return;
@@ -1006,7 +1015,7 @@ udp_ctloutput(struct socket *so, struct 
 			INP_WLOCK(inp);
 			up = intoudpcb(inp);
 			KASSERT(up != NULL, ("%s: up == NULL", __func__));
-			if (optval != 0 && optval < 8) {
+			if ((optval != 0 && optval < 8) || (optval > 65535)) {
 				INP_WUNLOCK(inp);
 				error = EINVAL;
 				break;

Modified: releng/10.1/sys/netinet6/in6.h
==============================================================================
--- releng/10.1/sys/netinet6/in6.h	Sun Oct 12 17:03:47 2014	(r272990)
+++ releng/10.1/sys/netinet6/in6.h	Sun Oct 12 17:07:15 2014	(r272991)
@@ -641,6 +641,8 @@ struct ip6_hdr;
 
 int	in6_cksum_pseudo(struct ip6_hdr *, uint32_t, uint8_t, uint16_t);
 int	in6_cksum(struct mbuf *, u_int8_t, u_int32_t, u_int32_t);
+int	in6_cksum_partial(struct mbuf *, u_int8_t, u_int32_t, u_int32_t,
+			  u_int32_t);
 int	in6_localaddr(struct in6_addr *);
 int	in6_localip(struct in6_addr *);
 int	in6_addrscope(struct in6_addr *);

Modified: releng/10.1/sys/netinet6/in6_cksum.c
==============================================================================
--- releng/10.1/sys/netinet6/in6_cksum.c	Sun Oct 12 17:03:47 2014	(r272990)
+++ releng/10.1/sys/netinet6/in6_cksum.c	Sun Oct 12 17:07:15 2014	(r272991)
@@ -145,9 +145,11 @@ in6_cksum_pseudo(struct ip6_hdr *ip6, ui
  * off is an offset where TCP/UDP/ICMP6 header starts.
  * len is a total length of a transport segment.
  * (e.g. TCP header + TCP payload)
+ * cov is the number of bytes to be taken into account for the checksum
  */
 int
-in6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len)
+in6_cksum_partial(struct mbuf *m, u_int8_t nxt, u_int32_t off,
+    u_int32_t len, u_int32_t cov)
 {
 	struct ip6_hdr *ip6;
 	u_int16_t *w, scope;
@@ -215,9 +217,9 @@ in6_cksum(struct mbuf *m, u_int8_t nxt, 
 	}
 	w = (u_int16_t *)(mtod(m, u_char *) + off);
 	mlen = m->m_len - off;
-	if (len < mlen)
-		mlen = len;
-	len -= mlen;
+	if (cov < mlen)
+		mlen = cov;
+	cov -= mlen;
 	/*
 	 * Force to even boundary.
 	 */
@@ -273,7 +275,7 @@ in6_cksum(struct mbuf *m, u_int8_t nxt, 
 	 * Lastly calculate a summary of the rest of mbufs.
 	 */
 
-	for (;m && len; m = m->m_next) {
+	for (;m && cov; m = m->m_next) {
 		if (m->m_len == 0)
 			continue;
 		w = mtod(m, u_int16_t *);
@@ -290,12 +292,12 @@ in6_cksum(struct mbuf *m, u_int8_t nxt, 
 			sum += s_util.s;
 			w = (u_int16_t *)((char *)w + 1);
 			mlen = m->m_len - 1;
-			len--;
+			cov--;
 		} else
 			mlen = m->m_len;
-		if (len < mlen)
-			mlen = len;
-		len -= mlen;
+		if (cov < mlen)
+			mlen = cov;
+		cov -= mlen;
 		/*
 		 * Force to even boundary.
 		 */
@@ -343,7 +345,7 @@ in6_cksum(struct mbuf *m, u_int8_t nxt, 
 		} else if (mlen == -1)
 			s_util.c[0] = *(char *)w;
 	}
-	if (len)
+	if (cov)
 		panic("in6_cksum: out of data");
 	if (mlen == -1) {
 		/* The last mbuf has odd # of bytes. Follow the
@@ -355,3 +357,9 @@ in6_cksum(struct mbuf *m, u_int8_t nxt, 
 	REDUCE;
 	return (~sum & 0xffff);
 }
+
+int
+in6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len)
+{
+	return (in6_cksum_partial(m, nxt, off, len, len));
+}

Modified: releng/10.1/sys/netinet6/udp6_usrreq.c
==============================================================================
--- releng/10.1/sys/netinet6/udp6_usrreq.c	Sun Oct 12 17:03:47 2014	(r272990)
+++ releng/10.1/sys/netinet6/udp6_usrreq.c	Sun Oct 12 17:07:15 2014	(r272991)
@@ -225,21 +225,26 @@ udp6_input(struct mbuf **mp, int *offp, 
 
 	nxt = ip6->ip6_nxt;
 	cscov_partial = (nxt == IPPROTO_UDPLITE) ? 1 : 0;
-	if (nxt == IPPROTO_UDPLITE && ulen == 0) {
+	if (nxt == IPPROTO_UDPLITE) {
 		/* Zero means checksum over the complete packet. */
-		ulen = plen;
-		cscov_partial = 0;
-	}
-	if (plen != ulen) {
-		UDPSTAT_INC(udps_badlen);
-		goto badunlocked;
-	}
-
-	/*
-	 * Checksum extended UDP header and data.
-	 */
-	if (uh->uh_sum == 0) {
-		if (ulen > plen || ulen < sizeof(struct udphdr)) {
+		if (ulen == 0)
+			ulen = plen;
+		if (ulen == plen)
+			cscov_partial = 0;
+		if ((ulen < sizeof(struct udphdr)) || (ulen > plen)) {
+			/* XXX: What is the right UDPLite MIB counter? */
+			goto badunlocked;
+		}
+		if (uh->uh_sum == 0) {
+			/* XXX: What is the right UDPLite MIB counter? */
+			goto badunlocked;
+		}
+	} else {
+		if ((ulen < sizeof(struct udphdr)) || (plen != ulen)) {
+			UDPSTAT_INC(udps_badlen);
+			goto badunlocked;
+		}
+		if (uh->uh_sum == 0) {
 			UDPSTAT_INC(udps_nosum);
 			goto badunlocked;
 		}
@@ -254,7 +259,7 @@ udp6_input(struct mbuf **mp, int *offp, 
 			    m->m_pkthdr.csum_data);
 		uh_sum ^= 0xffff;
 	} else
-		uh_sum = in6_cksum(m, nxt, off, ulen);
+		uh_sum = in6_cksum_partial(m, nxt, off, plen, ulen);
 
 	if (uh_sum != 0) {
 		UDPSTAT_INC(udps_badsum);
@@ -478,7 +483,7 @@ udp6_input(struct mbuf **mp, int *offp, 
 	INP_RLOCK_ASSERT(inp);
 	up = intoudpcb(inp);
 	if (cscov_partial) {
-		if (up->u_rxcslen > ulen) {
+		if (up->u_rxcslen == 0 || up->u_rxcslen > ulen) {
 			INP_RUNLOCK(inp);
 			m_freem(m);
 			return (IPPROTO_DONE);
@@ -841,8 +846,8 @@ udp6_output(struct inpcb *inp, struct mb
 		ip6->ip6_dst	= *faddr;
 
 		if (cscov_partial) {
-			if ((udp6->uh_sum = in6_cksum(m, 0,
-			    sizeof(struct ip6_hdr), cscov)) == 0)
+			if ((udp6->uh_sum = in6_cksum_partial(m, nxt,
+			    sizeof(struct ip6_hdr), plen, cscov)) == 0)
 				udp6->uh_sum = 0xffff;
 		} else {
 			udp6->uh_sum = in6_cksum_pseudo(ip6, plen, nxt, 0);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201410121707.s9CH7GYK058546>