Bug 202788 - 'proto' should be used to get protocol number instead of ip6->ip6_nxt in udp6_input()
Summary: 'proto' should be used to get protocol number instead of ip6->ip6_nxt in udp6...
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Only Me
Assignee: Bjoern A. Zeeb
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-08-31 10:01 UTC by Tiwei Bie
Modified: 2015-09-21 12:46 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tiwei Bie 2015-08-31 10:01:28 UTC
The 'proto' parameter should be used to get the protocol number (UDP or UDPLITE), instead of ip6->ip6_nxt in udp6_input().

Because ip6->ip6_nxt may be the protocol number of extension header,
such as:

If a UDP packet is an "atomic" fragment, frag6_input() will return
directly, and ip6->ip6_nxt will be IPPROTO_FRAGMENT (if the first
extension header is the fragment header) instead of IPPROTO_UDP or
IPPROTO_UDPLITE:

int
frag6_input(struct mbuf **mp, int *offp, int proto)
{
	......

	/*
	 * RFC 6946: Handle "atomic" fragments (offset and m bit set to 0)
	 * upfront, unrelated to any reassembly.  Just skip the fragment header.
	 */
	if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) {
		/* XXX-BZ we want dedicated counters for this. */
		IP6STAT_INC(ip6s_reassembled);
		in6_ifstat_inc(dstifp, ifs6_reass_ok);
		*offp = offset;
		return (ip6f->ip6f_nxt);
	}


And this is the patch to fix this bug:

diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 98790a8..da72f00 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -207,7 +207,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
 	struct sockaddr_in6 fromsa;
 	struct m_tag *fwd_tag;
 	uint16_t uh_sum;
-	uint8_t nxt;
+	uint8_t nxt = proto;
 
 	ifp = m->m_pkthdr.rcvif;
 	ip6 = mtod(m, struct ip6_hdr *);
@@ -233,7 +233,6 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
 	plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
 	ulen = ntohs((u_short)uh->uh_ulen);
 
-	nxt = ip6->ip6_nxt;
 	cscov_partial = (nxt == IPPROTO_UDPLITE) ? 1 : 0;
 	if (nxt == IPPROTO_UDPLITE) {
 		/* Zero means checksum over the complete packet. */
Comment 1 Bjoern A. Zeeb freebsd_committer freebsd_triage 2015-09-17 11:18:26 UTC
Sorry, did not forget.  This is yours (shortened) and two more extra UDP-Lite "nxt" fixes.  I'll put them up for review.

Index: sys/netinet6/udp6_usrreq.c
===================================================================
--- sys/netinet6/udp6_usrreq.c  (revision 287907)
+++ sys/netinet6/udp6_usrreq.c  (working copy)
@@ -233,7 +233,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
        plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
        ulen = ntohs((u_short)uh->uh_ulen);

-       nxt = ip6->ip6_nxt;
+       nxt = proto;
        cscov_partial = (nxt == IPPROTO_UDPLITE) ? 1 : 0;
        if (nxt == IPPROTO_UDPLITE) {
                /* Zero means checksum over the complete packet. */
@@ -668,9 +668,11 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str
                        return (error);
        }

+       nxt = (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDP) ?
+           IPPROTO_UDP : IPPROTO_UDPLITE;
        if (control) {
                if ((error = ip6_setpktopts(control, &opt,
-                   inp->in6p_outputopts, td->td_ucred, IPPROTO_UDP)) != 0)
+                   inp->in6p_outputopts, td->td_ucred, nxt)) != 0)
                        goto release;
                optp = &opt;
        } else
@@ -794,8 +796,6 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str
        /*
         * Stuff checksum and output datagram.
         */
-       nxt = (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDP) ?
-           IPPROTO_UDP : IPPROTO_UDPLITE;
        udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
        udp6->uh_sport = inp->inp_lport; /* lport is always set in the PCB */
        udp6->uh_dport = fport;
@@ -1218,7 +1218,10 @@ udp6_send(struct socket *so, int flags, struct mbu
                }
                if (hasv4addr) {
                        struct pr_usrreqs *pru;
+                       uint8_t nxt;

+                       nxt = (inp->inp_socket->so_proto->pr_protocol ==
+                           IPPROTO_UDP) ? IPPROTO_UDP : IPPROTO_UDPLITE;
                        /*
                         * XXXRW: We release UDP-layer locks before calling
                         * udp_send() in order to avoid recursion.  However,
@@ -1230,7 +1233,7 @@ udp6_send(struct socket *so, int flags, struct mbu
                        INP_WUNLOCK(inp);
                        if (sin6)
                                in6_sin6_2_sin_in_sock(addr);
-                       pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
+                       pru = inetsw[ip_protox[nxt]].pr_usrreqs;
                        /* addr will just be freed in sendit(). */
                        return ((*pru->pru_send)(so, flags, m, addr, control,
                            td));
Comment 2 Bjoern A. Zeeb freebsd_committer freebsd_triage 2015-09-17 11:24:43 UTC
https://reviews.freebsd.org/D3686
Comment 3 Tiwei Bie 2015-09-17 11:48:18 UTC
(In reply to Bjoern A. Zeeb from comment #1)

Never mind! Thank you very much! ^_^
Comment 4 commit-hook freebsd_committer freebsd_triage 2015-09-21 12:32:50 UTC
A commit references this bug:

Author: bz
Date: Mon Sep 21 12:32:37 UTC 2015
New revision: 288065
URL: https://svnweb.freebsd.org/changeset/base/288065

Log:
  In the UDP over IPv6 implementation several cases are using the wrong protocol,
  e.g., based on wrong "next header" assumptions (which does not have to point to
  the upper layer protocol), or using hard-coded UDP instead of UDP or UDP-Lite
  possibly switching protocols.  Fix those cases for UDP-Lite to work correctly.

  PR:			202788
  Submitted by:		Tiwei Bie (btw mail.ustc.edu.cn) [parts]
  Reviewed by:		gnn, Tiwei Bie (btw mail.ustc.edu.cn),
  			kevlo (earlier version)
  MFC after:		2 weeks
  Differential Revision:	https://reviews.freebsd.org/D3686

Changes:
  head/sys/netinet6/udp6_usrreq.c