Subject: kern/30393: PF/ALTQ does not work on ppp(4) interfaces
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <carton@Ivy.NET>
List: netbsd-bugs
Date: 06/01/2005 21:38:00
>Number:         30393
>Category:       kern
>Synopsis:       PF/ALTQ does not work on ppp(4) interfaces
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Jun 01 21:38:00 +0000 2005
>Originator:     Miles Nordin
>Release:        NetBSD 2.0.2_STABLE, pflkm-20050118
>Organization:
Ivy Ministries
>Environment:
System: NetBSD castrovalva 2.0.2_STABLE NetBSD 2.0.2_STABLE (CASTROVALVA-$Revision: 1.10 $) #0: Wed Apr 27 23:41:50 EDT 2005 carton@castrovalva:/scratch/src/sys/arch/alpha/compile/CASTROVALVA alpha
Architecture: alpha
Machine: alpha
>Description:
When running ALTQ on a PPP interface, I get on the console:

altq: packet for ppp0 does not have pkthdr

and in /var/log/messages:

May 31 21:37:04 ezln pppd[3455]: write: No buffer space available

pppd is unable to bring up a session.  If I enable altq after a session is 
up, the session dies after a few minutes.

>How-To-Repeat:
patch kernel with altq.diff included with pflkm
install security/pflkm and load the module
enable altq on ppp0
try to use pppd to establish a session on ppp0.  It'll fail for being unable 
to send LCP messages.
>Fix:
I tracked it down to net/ppp_tty.c, which is allocating the mbuf chain
that doesn't start with a header.  It's allocated I think when someone
write()s to a tty that is in pppdisc, like for LCP and other control
messages.  This fixes it for me:

Index: ppp_tty.c
===================================================================
RCS file: /scratch/cvsroot/netbsd/src/sys/net/ppp_tty.c,v
retrieving revision 1.1.1.5
diff -u -r1.1.1.5 ppp_tty.c
--- ppp_tty.c	12 Dec 2003 11:38:22 -0000	1.1.1.5
+++ ppp_tty.c	1 Jun 2005 01:42:21 -0000
@@ -373,7 +373,7 @@
     int flag;
 {
     struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
-    struct mbuf *m, *m0, **mp;
+    struct mbuf *m, *m0;
     struct sockaddr dst;
     int len, error;
 
@@ -386,28 +386,34 @@
     if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HDRLEN ||
 	uio->uio_resid < PPP_HDRLEN)
 	return (EMSGSIZE);
-    for (mp = &m0; uio->uio_resid; mp = &m->m_next) {
-	m = m_get(M_WAIT, MT_DATA);
-	if ((*mp = m) == NULL) {
-	    m_freem(m0);
-	    return (ENOBUFS);
-	}
+    MGETHDR(m0, M_WAIT, MT_DATA);
+    m0->m_len = 0;
+    m0->m_pkthdr.len = uio->uio_resid;
+    m0->m_pkthdr.rcvif = (struct ifnet *)0;
+    for (m = m0; uio->uio_resid; m = m->m_next) {
 	m->m_len = 0;
 	if (uio->uio_resid >= MCLBYTES / 2)
 	    MCLGET(m, M_DONTWAIT);
 	len = M_TRAILINGSPACE(m);
-	if (len > uio->uio_resid)
+	if (len >= uio->uio_resid) {
 	    len = uio->uio_resid;
-	if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) {
-	    m_freem(m0);
-	    return (error);
-	}
-	m->m_len = len;
+	    if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) {
+		m_freem(m0);
+		return (error);
+	    }
+	    m->m_len = len;
+	} else {
+	    if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) {
+		m_freem(m0);
+		return (error);
+	    }
+	    m->m_len = len;
+	    MGET(m->m_next, M_WAIT, MT_DATA);
+        }
     }
     dst.sa_family = AF_UNSPEC;
     bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN);
-    m0->m_data += PPP_HDRLEN;
-    m0->m_len -= PPP_HDRLEN;
+    m_adj(m0, PPP_HDRLEN);
     return ((*sc->sc_if.if_output)(&sc->sc_if, m0, &dst, (struct rtentry *)0));
 }