Module Name:    src
Committed By:   martin
Date:           Thu Mar  8 13:41:41 UTC 2018

Modified Files:
        src/sys/net [netbsd-8]: if_l2tp.c if_l2tp.h
        src/sys/netinet [netbsd-8]: in_l2tp.c
        src/sys/netinet6 [netbsd-8]: in6_l2tp.c

Log Message:
Pull up following revision(s) (requested by knakahara in ticket #614):
        sys/net/if_l2tp.c: revision 1.20
        sys/netinet6/in6_l2tp.c: revision 1.13
        sys/netinet6/in6_l2tp.c: revision 1.14
        sys/net/if_l2tp.h: revision 1.3
        sys/net/if_l2tp.c: revision 1.13
        sys/netinet/in_l2tp.c: revision 1.10
        sys/net/if_l2tp.c: revision 1.18
        sys/netinet/in_l2tp.c: revision 1.11
        sys/net/if_l2tp.c: revision 1.19
        sys/netinet/in_l2tp.c: revision 1.12

If if_attach() failed in the attach function, return. Add comments about 
if_initialize().
suggested by ozaki-r@n.o.

Fix null deref, m could be NULL if M_PREPEND fails.

style

Style, reduce the indentation level when possible, and add a missing NULL
check after M_PREPEND.

Several fixes in L2TP:
 * l2tp_input(): use m_copydata, and ensure there is enough space in the
   chain. Otherwise overflow.
 * l2tp_tcpmss_clamp(): ensure there is enough space in the chain.
 * in_l2tp_output(): don't check 'sc' against NULL, it can't be NULL.
 * in_l2tp_input(): no need to call m_pullup since we use m_copydata.
   Just check the space in the chain.
 * in_l2tp_input(): if there is a cookie, make sure the chain has enough
   space.
 * in6_l2tp_input(): same changes as in_l2tp_input().
Ok knakahara@

Use MH_ALIGN instead, ok knakahara@.


To generate a diff of this commit:
cvs rdiff -u -r1.11.2.4 -r1.11.2.5 src/sys/net/if_l2tp.c
cvs rdiff -u -r1.2 -r1.2.2.1 src/sys/net/if_l2tp.h
cvs rdiff -u -r1.2.8.3 -r1.2.8.4 src/sys/netinet/in_l2tp.c
cvs rdiff -u -r1.5.8.3 -r1.5.8.4 src/sys/netinet6/in6_l2tp.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_l2tp.c
diff -u src/sys/net/if_l2tp.c:1.11.2.4 src/sys/net/if_l2tp.c:1.11.2.5
--- src/sys/net/if_l2tp.c:1.11.2.4	Sun Feb 11 21:17:34 2018
+++ src/sys/net/if_l2tp.c	Thu Mar  8 13:41:40 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_l2tp.c,v 1.11.2.4 2018/02/11 21:17:34 snj Exp $	*/
+/*	$NetBSD: if_l2tp.c,v 1.11.2.5 2018/03/08 13:41:40 martin Exp $	*/
 
 /*
  * Copyright (c) 2017 Internet Initiative Japan Inc.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_l2tp.c,v 1.11.2.4 2018/02/11 21:17:34 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_l2tp.c,v 1.11.2.5 2018/03/08 13:41:40 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -228,10 +228,17 @@ l2tp_clone_create(struct if_clone *ifc, 
 {
 	struct l2tp_softc *sc;
 	struct l2tp_variant *var;
+	int rv;
 
 	sc = kmem_zalloc(sizeof(struct l2tp_softc), KM_SLEEP);
-	var = kmem_zalloc(sizeof(struct l2tp_variant), KM_SLEEP);
+	if_initname(&sc->l2tp_ec.ec_if, ifc->ifc_name, unit);
+	rv = l2tpattach0(sc);
+	if (rv != 0) {
+		kmem_free(sc, sizeof(struct l2tp_softc));
+		return rv;
+	}
 
+	var = kmem_zalloc(sizeof(struct l2tp_variant), KM_SLEEP);
 	var->lv_softc = sc;
 	var->lv_state = L2TP_STATE_DOWN;
 	var->lv_use_cookie = L2TP_COOKIE_OFF;
@@ -241,10 +248,6 @@ l2tp_clone_create(struct if_clone *ifc, 
 	mutex_init(&sc->l2tp_lock, MUTEX_DEFAULT, IPL_NONE);
 	PSLIST_ENTRY_INIT(sc, l2tp_hash);
 
-	if_initname(&sc->l2tp_ec.ec_if, ifc->ifc_name, unit);
-
-	l2tpattach0(sc);
-
 	sc->l2tp_ro_percpu = percpu_alloc(sizeof(struct l2tp_ro));
 	percpu_foreach(sc->l2tp_ro_percpu, l2tp_ro_init_pc, NULL);
 
@@ -255,9 +258,10 @@ l2tp_clone_create(struct if_clone *ifc, 
 	return (0);
 }
 
-void
+int
 l2tpattach0(struct l2tp_softc *sc)
 {
+	int rv;
 
 	sc->l2tp_ec.ec_if.if_addrlen = 0;
 	sc->l2tp_ec.ec_if.if_mtu    = L2TP_MTU;
@@ -274,9 +278,19 @@ l2tpattach0(struct l2tp_softc *sc)
 	sc->l2tp_ec.ec_if.if_transmit = l2tp_transmit;
 	sc->l2tp_ec.ec_if._if_input = ether_input;
 	IFQ_SET_READY(&sc->l2tp_ec.ec_if.if_snd);
-	if_attach(&sc->l2tp_ec.ec_if);
+	/* XXX
+	 * It may improve performance to use if_initialize()/if_register()
+	 * so that l2tp_input() calls if_input() instead of
+	 * if_percpuq_enqueue(). However, that causes recursive softnet_lock
+	 * when NET_MPSAFE is not set.
+	 */
+	rv = if_attach(&sc->l2tp_ec.ec_if);
+	if (rv != 0)
+		return rv;
 	if_alloc_sadl(&sc->l2tp_ec.ec_if);
 	bpf_attach(&sc->l2tp_ec.ec_if, DLT_EN10MB, sizeof(struct ether_header));
+
+	return 0;
 }
 
 void
@@ -451,16 +465,23 @@ l2tpintr(struct l2tp_variant *var)
 void
 l2tp_input(struct mbuf *m, struct ifnet *ifp)
 {
+	u_long val;
 
 	KASSERT(ifp != NULL);
 
-	if (0 == (mtod(m, u_long) & 0x03)) {
+	if (m->m_pkthdr.len < sizeof(val)) {
+		m_freem(m);
+		return;
+	}
+
+	m_copydata(m, 0, sizeof(val), &val);
+
+	if ((val & 0x03) == 0) {
 		/* copy and align head of payload */
 		struct mbuf *m_head;
 		int copy_length;
 
 #define L2TP_COPY_LENGTH		60
-#define L2TP_LINK_HDR_ROOM	(MHLEN - L2TP_COPY_LENGTH - 4/*round4(2)*/)
 
 		if (m->m_pkthdr.len < L2TP_COPY_LENGTH) {
 			copy_length = m->m_pkthdr.len;
@@ -481,19 +502,19 @@ l2tp_input(struct mbuf *m, struct ifnet 
 		}
 		M_COPY_PKTHDR(m_head, m);
 
-		m_head->m_data += 2 /* align */ + L2TP_LINK_HDR_ROOM;
-		memcpy(m_head->m_data, m->m_data, copy_length);
+		MH_ALIGN(m_head, L2TP_COPY_LENGTH);
+		memcpy(mtod(m_head, void *), mtod(m, void *), copy_length);
 		m_head->m_len = copy_length;
 		m->m_data += copy_length;
 		m->m_len -= copy_length;
 
 		/* construct chain */
 		if (m->m_len == 0) {
-			m_head->m_next = m_free(m); /* not m_freem */
+			m_head->m_next = m_free(m);
 		} else {
 			/*
-			 * copyed mtag in previous call M_COPY_PKTHDR
-			 * but don't delete mtag in case cutt of M_PKTHDR flag
+			 * Already copied mtag with M_COPY_PKTHDR.
+			 * but don't delete mtag in case cut off M_PKTHDR flag
 			 */
 			m_tag_delete_chain(m, NULL);
 			m->m_flags &= ~M_PKTHDR;
@@ -1352,80 +1373,90 @@ static struct mbuf *l2tp_tcpmss6_clamp(s
 #endif
 
 struct mbuf *
-l2tp_tcpmss_clamp(struct ifnet *ifp, struct mbuf	*m)
+l2tp_tcpmss_clamp(struct ifnet *ifp, struct mbuf *m)
 {
+	struct ether_header *eh;
+	struct ether_vlan_header evh;
 
-	if (l2tp_need_tcpmss_clamp(ifp)) {
-		struct ether_header *eh;
-		struct ether_vlan_header evh;
-
-		/* save ether header */
-		m_copydata(m, 0, sizeof(evh), (void *)&evh);
-		eh = (struct ether_header *)&evh;
-
-		switch (ntohs(eh->ether_type)) {
-		case ETHERTYPE_VLAN: /* Ether + VLAN */
-			if (m->m_pkthdr.len <= sizeof(struct ether_vlan_header))
-				break;
-			m_adj(m, sizeof(struct ether_vlan_header));
-			switch (ntohs(evh.evl_proto)) {
-#ifdef INET
-			case ETHERTYPE_IP: /* Ether + VLAN + IPv4 */
-				m = l2tp_tcpmss4_clamp(ifp, m);
-				if (m == NULL)
-					return NULL;
-				break;
-#endif /* INET */
-#ifdef INET6
-			case ETHERTYPE_IPV6: /* Ether + VLAN + IPv6 */
-				m = l2tp_tcpmss6_clamp(ifp, m);
-				if (m == NULL)
-					return NULL;
-				break;
-#endif /* INET6 */
-			default:
-				break;
-			}
-			/* restore ether header */
-			M_PREPEND(m, sizeof(struct ether_vlan_header),
-			    M_DONTWAIT);
-			if (m == NULL)
-				return NULL;
-			*mtod(m, struct ether_vlan_header *) = evh;
+	if (!l2tp_need_tcpmss_clamp(ifp)) {
+		return m;
+	}
+
+	if (m->m_pkthdr.len < sizeof(evh)) {
+		m_freem(m);
+		return NULL;
+	}
+
+	/* save ether header */
+	m_copydata(m, 0, sizeof(evh), (void *)&evh);
+	eh = (struct ether_header *)&evh;
+
+	switch (ntohs(eh->ether_type)) {
+	case ETHERTYPE_VLAN: /* Ether + VLAN */
+		if (m->m_pkthdr.len <= sizeof(struct ether_vlan_header))
 			break;
+		m_adj(m, sizeof(struct ether_vlan_header));
+		switch (ntohs(evh.evl_proto)) {
 #ifdef INET
-		case ETHERTYPE_IP: /* Ether + IPv4 */
-			if (m->m_pkthdr.len <= sizeof(struct ether_header))
-				break;
-			m_adj(m, sizeof(struct ether_header));
+		case ETHERTYPE_IP: /* Ether + VLAN + IPv4 */
 			m = l2tp_tcpmss4_clamp(ifp, m);
 			if (m == NULL)
 				return NULL;
-			/* restore ether header */
-			M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
-			if (m == NULL)
-				return NULL;
-			*mtod(m, struct ether_header *) = *eh;
 			break;
 #endif /* INET */
 #ifdef INET6
-		case ETHERTYPE_IPV6: /* Ether + IPv6 */
-			if (m->m_pkthdr.len <= sizeof(struct ether_header))
-				break;
-			m_adj(m, sizeof(struct ether_header));
+		case ETHERTYPE_IPV6: /* Ether + VLAN + IPv6 */
 			m = l2tp_tcpmss6_clamp(ifp, m);
 			if (m == NULL)
 				return NULL;
-			/* restore ether header */
-			M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
-			if (m == NULL)
-				return NULL;
-			*mtod(m, struct ether_header *) = *eh;
 			break;
 #endif /* INET6 */
 		default:
 			break;
 		}
+
+		/* restore ether header */
+		M_PREPEND(m, sizeof(struct ether_vlan_header),
+		    M_DONTWAIT);
+		if (m == NULL)
+			return NULL;
+		*mtod(m, struct ether_vlan_header *) = evh;
+		break;
+
+#ifdef INET
+	case ETHERTYPE_IP: /* Ether + IPv4 */
+		if (m->m_pkthdr.len <= sizeof(struct ether_header))
+			break;
+		m_adj(m, sizeof(struct ether_header));
+		m = l2tp_tcpmss4_clamp(ifp, m);
+		if (m == NULL)
+			return NULL;
+		/* restore ether header */
+		M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
+		if (m == NULL)
+			return NULL;
+		*mtod(m, struct ether_header *) = *eh;
+		break;
+#endif /* INET */
+
+#ifdef INET6
+	case ETHERTYPE_IPV6: /* Ether + IPv6 */
+		if (m->m_pkthdr.len <= sizeof(struct ether_header))
+			break;
+		m_adj(m, sizeof(struct ether_header));
+		m = l2tp_tcpmss6_clamp(ifp, m);
+		if (m == NULL)
+			return NULL;
+		/* restore ether header */
+		M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
+		if (m == NULL)
+			return NULL;
+		*mtod(m, struct ether_header *) = *eh;
+		break;
+#endif /* INET6 */
+
+	default:
+		break;
 	}
 
 	return m;
@@ -1439,12 +1470,12 @@ l2tp_need_tcpmss_clamp(struct ifnet *ifp
 #ifdef INET
 	if (ifp->if_tcpmss != 0)
 		ret = 1;
-#endif /* INET */
+#endif
 
 #ifdef INET6
 	if (ifp->if_tcpmss6 != 0)
 		ret = 1;
-#endif /* INET6 */
+#endif
 
 	return ret;
 }

Index: src/sys/net/if_l2tp.h
diff -u src/sys/net/if_l2tp.h:1.2 src/sys/net/if_l2tp.h:1.2.2.1
--- src/sys/net/if_l2tp.h:1.2	Wed May 31 08:19:44 2017
+++ src/sys/net/if_l2tp.h	Thu Mar  8 13:41:40 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_l2tp.h,v 1.2 2017/05/31 08:19:44 knakahara Exp $	*/
+/*	$NetBSD: if_l2tp.h,v 1.2.2.1 2018/03/08 13:41:40 martin Exp $	*/
 
 /*
  * Copyright (c) 2017 Internet Initiative Japan Inc.
@@ -162,7 +162,7 @@ l2tp_heldref_variant(struct l2tp_variant
 
 /* Prototypes */
 void l2tpattach(int);
-void l2tpattach0(struct l2tp_softc *);
+int l2tpattach0(struct l2tp_softc *);
 void l2tp_input(struct mbuf *, struct ifnet *);
 int l2tp_ioctl(struct ifnet *, u_long, void *);
 

Index: src/sys/netinet/in_l2tp.c
diff -u src/sys/netinet/in_l2tp.c:1.2.8.3 src/sys/netinet/in_l2tp.c:1.2.8.4
--- src/sys/netinet/in_l2tp.c:1.2.8.3	Tue Jan  2 10:39:57 2018
+++ src/sys/netinet/in_l2tp.c	Thu Mar  8 13:41:41 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_l2tp.c,v 1.2.8.3 2018/01/02 10:39:57 snj Exp $	*/
+/*	$NetBSD: in_l2tp.c,v 1.2.8.4 2018/03/08 13:41:41 martin Exp $	*/
 
 /*
  * Copyright (c) 2017 Internet Initiative Japan Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in_l2tp.c,v 1.2.8.3 2018/01/02 10:39:57 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_l2tp.c,v 1.2.8.4 2018/03/08 13:41:41 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_l2tp.h"
@@ -103,9 +103,6 @@ in_l2tp_output(struct l2tp_variant *var,
 	    && sin_dst->sin_family == AF_INET);
 
 	sc = var->lv_softc;
-	if (sc == NULL)
-		return ENETUNREACH;
-
 	ifp = &sc->l2tp_ec.ec_if;
 	error = l2tp_check_nesting(ifp, m);
 	if (error) {
@@ -113,7 +110,16 @@ in_l2tp_output(struct l2tp_variant *var,
 		goto looped;
 	}
 
-#ifdef NETYET
+	/* bidirectional configured tunnel mode */
+	if (sin_dst->sin_addr.s_addr == INADDR_ANY) {
+		m_freem(m);
+		if ((ifp->if_flags & IFF_DEBUG) != 0)
+			log(LOG_DEBUG, "%s: ENETUNREACH\n", __func__);
+		error = ENETUNREACH;
+		goto out;
+	}
+
+#ifdef NOTYET
 /* TODO: support ALTQ for innner frame */
 #ifdef ALTQ
 	ALTQ_SAVE_PAYLOAD(m, AF_ETHER);
@@ -122,16 +128,7 @@ in_l2tp_output(struct l2tp_variant *var,
 
 	memset(&iphdr, 0, sizeof(iphdr));
 	iphdr.ip_src = sin_src->sin_addr;
-	/* bidirectional configured tunnel mode */
-	if (sin_dst->sin_addr.s_addr != INADDR_ANY)
-		iphdr.ip_dst = sin_dst->sin_addr;
-	else {
-		m_freem(m);
-		if ((ifp->if_flags & IFF_DEBUG) != 0)
-			log(LOG_DEBUG, "%s: ENETUNREACH\n", __func__);
-		error = ENETUNREACH;
-		goto out;
-	}
+	iphdr.ip_dst = sin_dst->sin_addr;
 	iphdr.ip_p = IPPROTO_L2TP;
 	/* version will be set in ip_output() */
 	iphdr.ip_ttl = ip_l2tp_ttl;
@@ -152,11 +149,12 @@ in_l2tp_output(struct l2tp_variant *var,
 		goto out;
 	}
 #endif
+
 	/*
-	 * payload length
-	 *  NOTE: Payload length may be changed in ip_tcpmss().
-	 *        Typical case is missing of TCP mss option in original
-	 *        TCP header.
+	 * Payload length.
+	 *
+	 * NOTE: payload length may be changed in ip_tcpmss(). Typical case
+	 * is missing of TCP mss option in original TCP header.
 	 */
 	iphdr.ip_len += m->m_pkthdr.len;
 	HTONS(iphdr.ip_len);
@@ -174,12 +172,10 @@ in_l2tp_output(struct l2tp_variant *var,
 		}
 		if (var->lv_peer_cookie_len == 4) {
 			cookie_32 = htonl((uint32_t)var->lv_peer_cookie);
-			memcpy(mtod(m, void *), &cookie_32,
-			    sizeof(uint32_t));
+			memcpy(mtod(m, void *), &cookie_32, sizeof(uint32_t));
 		} else {
 			cookie_64 = htobe64(var->lv_peer_cookie);
-			memcpy(mtod(m, void *), &cookie_64,
-			    sizeof(uint64_t));
+			memcpy(mtod(m, void *), &cookie_64, sizeof(uint64_t));
 		}
 	}
 
@@ -196,11 +192,14 @@ in_l2tp_output(struct l2tp_variant *var,
 
 	/* prepend new IP header */
 	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
+	if (m == NULL) {
+		error = ENOBUFS;
+		goto out;
+	}
 	if (IP_HDR_ALIGNED_P(mtod(m, void *)) == 0) {
-		if (m)
-			m = m_copyup(m, sizeof(struct ip), 0);
+		m = m_copyup(m, sizeof(struct ip), 0);
 	} else {
-		if (m && m->m_len < sizeof(struct ip))
+		if (m->m_len < sizeof(struct ip))
 			m = m_pullup(m, sizeof(struct ip));
 	}
 	if (m == NULL) {
@@ -260,13 +259,12 @@ in_l2tp_input(struct mbuf *m, int off, i
 	struct psref psref;
 	struct l2tp_variant *var;
 
-	if (m->m_len < off + sizeof(uint32_t)) {
-		m = m_pullup(m, off + sizeof(uint32_t));
-		if (!m) {
-			/* if payload length < 4 octets */
-			return;
-		}
-        }
+	KASSERT((m->m_flags & M_PKTHDR) != 0);
+
+	if (m->m_pkthdr.len < off + sizeof(uint32_t)) {
+		m_freem(m);
+		return;
+	}
 
 	/* get L2TP session ID */
 	m_copydata(m, off, sizeof(uint32_t), (void *)&sess_id);
@@ -288,28 +286,28 @@ in_l2tp_input(struct mbuf *m, int off, i
 		m_freem(m);
 		ip_statinc(IP_STAT_NOL2TP);
 		return;
-	} else {
-		sc = var->lv_softc;
-		l2tpp = &(sc->l2tp_ec.ec_if);
+	}
+
+	sc = var->lv_softc;
+	l2tpp = &(sc->l2tp_ec.ec_if);
 
-		if (l2tpp == NULL || (l2tpp->if_flags & IFF_UP) == 0) {
+	if (l2tpp == NULL || (l2tpp->if_flags & IFF_UP) == 0) {
 #ifdef L2TP_DEBUG
-			if (l2tpp == NULL)
-				log(LOG_DEBUG, "%s: l2tpp is NULL\n", __func__);
-			else
-				log(LOG_DEBUG, "%s: l2tpp is down\n", __func__);
+		if (l2tpp == NULL)
+			log(LOG_DEBUG, "%s: l2tpp is NULL\n", __func__);
+		else
+			log(LOG_DEBUG, "%s: l2tpp is down\n", __func__);
 #endif
-			m_freem(m);
-			ip_statinc(IP_STAT_NOL2TP);
-			goto out;
-		}
+		m_freem(m);
+		ip_statinc(IP_STAT_NOL2TP);
+		goto out;
+	}
 
-		/* other CPU do l2tp_delete_tunnel */
-		if (var->lv_psrc == NULL || var->lv_pdst == NULL) {
-			m_freem(m);
-			ip_statinc(IP_STAT_NOL2TP);
-			goto out;
-		}
+	/* other CPU did l2tp_delete_tunnel */
+	if (var->lv_psrc == NULL || var->lv_pdst == NULL) {
+		m_freem(m);
+		ip_statinc(IP_STAT_NOL2TP);
+		goto out;
 	}
 
 	if (var->lv_state != L2TP_STATE_UP) {
@@ -320,6 +318,10 @@ in_l2tp_input(struct mbuf *m, int off, i
 	m_adj(m, off + sizeof(uint32_t));
 
 	if (var->lv_use_cookie == L2TP_COOKIE_ON) {
+		if (m->m_pkthdr.len < var->lv_my_cookie_len) {
+			m_freem(m);
+			goto out;
+		}
 		if (var->lv_my_cookie_len == 4) {
 			m_copydata(m, 0, sizeof(uint32_t), (void *)&cookie_32);
 			NTOHL(cookie_32);

Index: src/sys/netinet6/in6_l2tp.c
diff -u src/sys/netinet6/in6_l2tp.c:1.5.8.3 src/sys/netinet6/in6_l2tp.c:1.5.8.4
--- src/sys/netinet6/in6_l2tp.c:1.5.8.3	Tue Jan  2 10:39:57 2018
+++ src/sys/netinet6/in6_l2tp.c	Thu Mar  8 13:41:41 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_l2tp.c,v 1.5.8.3 2018/01/02 10:39:57 snj Exp $	*/
+/*	$NetBSD: in6_l2tp.c,v 1.5.8.4 2018/03/08 13:41:41 martin Exp $	*/
 
 /*
  * Copyright (c) 2017 Internet Initiative Japan Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_l2tp.c,v 1.5.8.3 2018/01/02 10:39:57 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_l2tp.c,v 1.5.8.4 2018/03/08 13:41:41 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_l2tp.h"
@@ -113,6 +113,14 @@ in6_l2tp_output(struct l2tp_variant *var
 		goto looped;
 	}
 
+	/* bidirectional configured tunnel mode */
+	if (IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) {
+		m_freem(m);
+		if ((ifp->if_flags & IFF_DEBUG) != 0)
+			log(LOG_DEBUG, "%s: ENETUNREACH\n", __func__);
+		return ENETUNREACH;
+	}
+
 #ifdef NOTYET
 /* TODO: support ALTQ for innner frame */
 #ifdef ALTQ
@@ -122,18 +130,10 @@ in6_l2tp_output(struct l2tp_variant *var
 
 	memset(&ip6hdr, 0, sizeof(ip6hdr));
 	ip6hdr.ip6_src = sin6_src->sin6_addr;
-	/* bidirectional configured tunnel mode */
-	if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
-		ip6hdr.ip6_dst = sin6_dst->sin6_addr;
-	else {
-		m_freem(m);
-		if ((ifp->if_flags & IFF_DEBUG) != 0)
-			log(LOG_DEBUG, "%s: ENETUNREACH\n", __func__);
-		return ENETUNREACH;
-	}
+	ip6hdr.ip6_dst = sin6_dst->sin6_addr;
 	/* unlike IPv4, IP version must be filled by caller of ip6_output() */
-	ip6hdr.ip6_vfc  = 0x60;
-	ip6hdr.ip6_nxt  = IPPROTO_L2TP;
+	ip6hdr.ip6_vfc = 0x60;
+	ip6hdr.ip6_nxt = IPPROTO_L2TP;
 	ip6hdr.ip6_hlim = ip6_l2tp_hlim;
 	/* outer IP payload length */
 	ip6hdr.ip6_plen = 0;
@@ -152,10 +152,10 @@ in6_l2tp_output(struct l2tp_variant *var
 #endif
 
 	/*
-	 * payload length
-	 *  NOTE: Payload length may be changed in ip_tcpmss().
-	 *        Typical case is missing of TCP mss option in original
-	 *        TCP header.
+	 * Payload length.
+	 *
+	 * NOTE: payload length may be changed in ip_tcpmss(). Typical case
+	 * is missing of TCP mss option in original TCP header.
 	 */
 	ip6hdr.ip6_plen += m->m_pkthdr.len;
 	HTONS(ip6hdr.ip6_plen);
@@ -171,12 +171,10 @@ in6_l2tp_output(struct l2tp_variant *var
 			return ENOBUFS;
 		if (var->lv_peer_cookie_len == 4) {
 			cookie_32 = htonl((uint32_t)var->lv_peer_cookie);
-			memcpy(mtod(m, void *), &cookie_32,
-			    sizeof(uint32_t));
+			memcpy(mtod(m, void *), &cookie_32, sizeof(uint32_t));
 		} else {
 			cookie_64 = htobe64(var->lv_peer_cookie);
-			memcpy(mtod(m, void *), &cookie_64,
-			    sizeof(uint64_t));
+			memcpy(mtod(m, void *), &cookie_64, sizeof(uint64_t));
 		}
 	}
 
@@ -191,11 +189,12 @@ in6_l2tp_output(struct l2tp_variant *var
 
 	/* prepend new IP header */
 	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
+	if (m == NULL)
+		return ENOBUFS;
 	if (IP_HDR_ALIGNED_P(mtod(m, void *)) == 0) {
-		if (m)
-			m = m_copyup(m, sizeof(struct ip), 0);
+		m = m_copyup(m, sizeof(struct ip), 0);
 	} else {
-		if (m && m->m_len < sizeof(struct ip6_hdr))
+		if (m->m_len < sizeof(struct ip6_hdr))
 			m = m_pullup(m, sizeof(struct ip6_hdr));
 	}
 	if (m == NULL)
@@ -254,14 +253,12 @@ in6_l2tp_input(struct mbuf **mp, int *of
 	uint64_t cookie_64;
 	struct psref psref;
 
-	if (m->m_len < off + sizeof(uint32_t)) {
-		m = m_pullup(m, off + sizeof(uint32_t));
-		if (!m) {
-			/* if payload length < 4 octets */
-			return IPPROTO_DONE;
-		}
-		*mp = m;
-        }
+	KASSERT((m->m_flags & M_PKTHDR) != 0);
+
+	if (m->m_pkthdr.len < off + sizeof(uint32_t)) {
+		m_freem(m);
+		return IPPROTO_DONE;
+	}
 
 	/* get L2TP session ID */
 	m_copydata(m, off, sizeof(uint32_t), (void *)&sess_id);
@@ -282,27 +279,28 @@ in6_l2tp_input(struct mbuf **mp, int *of
 		m_freem(m);
 		IP_STATINC(IP_STAT_NOL2TP);
 		return IPPROTO_DONE;
-	} else {
-		sc = var->lv_softc;
-		l2tpp = &(sc->l2tp_ec.ec_if);
+	}
 
-		if (l2tpp == NULL || (l2tpp->if_flags & IFF_UP) == 0) {
+	sc = var->lv_softc;
+	l2tpp = &(sc->l2tp_ec.ec_if);
+
+	if (l2tpp == NULL || (l2tpp->if_flags & IFF_UP) == 0) {
 #ifdef L2TP_DEBUG
-			if (l2tpp == NULL)
-				log(LOG_DEBUG, "%s: l2tpp is NULL\n", __func__);
-			else
-				log(LOG_DEBUG, "%s: l2tpp is down\n", __func__);
+		if (l2tpp == NULL)
+			log(LOG_DEBUG, "%s: l2tpp is NULL\n", __func__);
+		else
+			log(LOG_DEBUG, "%s: l2tpp is down\n", __func__);
 #endif
-			m_freem(m);
-			IP_STATINC(IP_STAT_NOL2TP);
-			goto out;
-		}
-		/* other CPU do l2tp_delete_tunnel */
-		if (var->lv_psrc == NULL || var->lv_pdst == NULL) {
-			m_freem(m);
-			ip_statinc(IP_STAT_NOL2TP);
-			goto out;
-		}
+		m_freem(m);
+		IP_STATINC(IP_STAT_NOL2TP);
+		goto out;
+	}
+
+	/* other CPU did l2tp_delete_tunnel */
+	if (var->lv_psrc == NULL || var->lv_pdst == NULL) {
+		m_freem(m);
+		ip_statinc(IP_STAT_NOL2TP);
+		goto out;
 	}
 
 	if (var->lv_state != L2TP_STATE_UP) {
@@ -312,6 +310,10 @@ in6_l2tp_input(struct mbuf **mp, int *of
 	m_adj(m, off + sizeof(uint32_t));
 
 	if (var->lv_use_cookie == L2TP_COOKIE_ON) {
+		if (m->m_pkthdr.len < var->lv_my_cookie_len) {
+			m_freem(m);
+			goto out;
+		}
 		if (var->lv_my_cookie_len == 4) {
 			m_copydata(m, 0, sizeof(uint32_t), (void *)&cookie_32);
 			NTOHL(cookie_32);

Reply via email to