summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortuexen <tuexen@FreeBSD.org>2014-10-12 17:07:15 +0000
committertuexen <tuexen@FreeBSD.org>2014-10-12 17:07:15 +0000
commit0c3aeca0a4917d4692d43cae936ed45faf99717d (patch)
tree524e80ca357822d16ce0608eb52fa4a15f0789cf
parent4312cb7398483961718347416773a13258c75680 (diff)
downloadFreeBSD-src-0c3aeca0a4917d4692d43cae936ed45faf99717d.zip
FreeBSD-src-0c3aeca0a4917d4692d43cae936ed45faf99717d.tar.gz
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)
-rw-r--r--share/man/man4/udplite.418
-rw-r--r--sys/netinet/udp_usrreq.c21
-rw-r--r--sys/netinet6/in6.h2
-rw-r--r--sys/netinet6/in6_cksum.c28
-rw-r--r--sys/netinet6/udp6_usrreq.c41
5 files changed, 67 insertions, 43 deletions
diff --git a/share/man/man4/udplite.4 b/share/man/man4/udplite.4
index 5d06b14..859c966 100644
--- a/share/man/man4/udplite.4
+++ b/share/man/man4/udplite.4
@@ -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:
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 20e4534..f065f80 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -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 sockopt *sopt)
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;
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index f7d7e76..639194a 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -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 *);
diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c
index da8a2e6..ce1961a 100644
--- a/sys/netinet6/in6_cksum.c
+++ b/sys/netinet6/in6_cksum.c
@@ -145,9 +145,11 @@ in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
* 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, u_int32_t off, u_int32_t len)
}
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, u_int32_t off, u_int32_t len)
* 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, u_int32_t off, u_int32_t len)
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, u_int32_t off, u_int32_t len)
} 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, u_int32_t off, u_int32_t len)
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));
+}
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index faa6427..56f1eb2 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -225,21 +225,26 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
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, int proto)
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, int proto)
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 mbuf *m, struct sockaddr *addr6,
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);
OpenPOWER on IntegriCloud