if_ppp.c revision 1.1 1 /*
2 * if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver.
3 *
4 * Copyright (c) 1989 Carnegie Mellon University.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by Carnegie Mellon University. The name of the
13 * University may not be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * Drew D. Perkins
20 * Carnegie Mellon University
21 * 4910 Forbes Ave.
22 * Pittsburgh, PA 15213
23 * (412) 268-8576
24 * ddp (at) andrew.cmu.edu
25 *
26 * Based on:
27 * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89
28 *
29 * Copyright (c) 1987 Regents of the University of California.
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms are permitted
33 * provided that the above copyright notice and this paragraph are
34 * duplicated in all such forms and that any documentation,
35 * advertising materials, and other materials related to such
36 * distribution and use acknowledge that the software was developed
37 * by the University of California, Berkeley. The name of the
38 * University may not be used to endorse or promote products derived
39 * from this software without specific prior written permission.
40 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
41 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
42 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
43 *
44 * Serial Line interface
45 *
46 * Rick Adams
47 * Center for Seismic Studies
48 * 1300 N 17th Street, Suite 1450
49 * Arlington, Virginia 22209
50 * (703)276-7900
51 * rick (at) seismo.ARPA
52 * seismo!rick
53 *
54 * Pounded on heavily by Chris Torek (chris (at) mimsy.umd.edu, umcp-cs!chris).
55 * Converted to 4.3BSD Beta by Chris Torek.
56 * Other changes made at Berkeley, based in part on code by Kirk Smith.
57 *
58 * Converted to 4.3BSD+ 386BSD by Brad Parker (brad (at) cayman.com)
59 * Added VJ tcp header compression; more unified ioctls
60 *
61 * Extensively modified by Paul Mackerras (paulus (at) cs.anu.edu.au).
62 * Cleaned up a lot of the mbuf-related code to fix bugs that
63 * caused system crashes and packet corruption. Changed pppstart
64 * so that it doesn't just give up with a collision if the whole
65 * packet doesn't fit in the output ring buffer.
66 *
67 * from: if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp
68 * $Id: if_ppp.c,v 1.1 1993/08/14 06:38:37 deraadt Exp $
69 */
70
71 #include "ppp.h"
72 #if NPPP > 0
73
74 #define VJC
75
76 #include "param.h"
77 #include "proc.h"
78 #include "mbuf.h"
79 #include "buf.h"
80 #include "dkstat.h"
81 #include "socket.h"
82 #include "ioctl.h"
83 #include "file.h"
84 #include "tty.h"
85 #include "kernel.h"
86 #include "conf.h"
87
88 #include "if.h"
89 #include "if_types.h"
90 #include "netisr.h"
91 #include "route.h"
92 #if INET
93 #include "../netinet/in.h"
94 #include "../netinet/in_systm.h"
95 #include "../netinet/in_var.h"
96 #include "../netinet/ip.h"
97 #endif
98
99 /*
100 * Here we try to tell whether we are in a 386BSD kernel, or
101 * in a NetBSD/Net-2/4.3-Reno kernel.
102 */
103 #ifndef RB_LEN
104 /* NetBSD, 4.3-Reno or similar */
105 #define CCOUNT(q) ((q)->c_cc)
106
107 #else
108 /* 386BSD, Jolitz-style ring buffers */
109 #define t_outq t_out
110 #define t_rawq t_raw
111 #define t_canq t_can
112 #define CCOUNT(q) (RB_LEN(q))
113 #endif
114
115 #ifdef VJC
116 #include "slcompress.h"
117 #define HDROFF MAX_HDR
118 /* HDROFF should really be 128, but other parts of the system will
119 panic on TCP+IP headers bigger than MAX_HDR = MHLEN (100). */
120
121 #else
122 #define HDROFF (0)
123 #endif
124
125 #include "if_ppp.h"
126 #include "machine/mtpr.h"
127
128 struct ppp_softc ppp_softc[NPPP];
129 int ppp_async_out_debug = 0;
130 int ppp_async_in_debug = 0;
131 int ppp_debug = 0;
132
133 void pppattach __P((void));
134 int pppopen __P((dev_t dev, struct tty *tp));
135 void pppclose __P((struct tty *tp, int flag));
136 int pppread __P((struct tty *tp, struct uio *uio, int flag));
137 int pppwrite __P((struct tty *tp, struct uio *uio, int flag));
138 int ppptioctl __P((struct tty *tp, int cmd, caddr_t data, int flag));
139 int pppoutput __P((struct ifnet *ifp, struct mbuf *m0,
140 struct sockaddr *dst));
141 void pppstart __P((struct tty *tp));
142 void pppinput __P((int c, struct tty *tp));
143 int pppioctl __P((struct ifnet *ifp, int cmd, caddr_t data));
144
145 static u_short pppfcs __P((u_short fcs, u_char *cp, int len));
146 static int pppinit __P((struct ppp_softc *sc));
147 static struct mbuf *ppp_btom __P((struct ppp_softc *sc));
148 static void pppdumpm __P((struct mbuf *m0, int pktlen));
149 static void pppdumpb __P((u_char *b, int l));
150
151 /*
152 * Some useful mbuf macros not in mbuf.h.
153 */
154 #define M_DATASTART(m) \
155 ((m)->m_flags & M_EXT ? (m)->m_ext.ext_buf : \
156 (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat)
157
158 #define M_DATASIZE(m) \
159 ((m)->m_flags & M_EXT ? (m)->m_ext.ext_size : \
160 (m)->m_flags & M_PKTHDR ? MHLEN: MLEN)
161
162 /*
163 * Called from boot code to establish ppp interfaces.
164 */
165 void
166 pppattach()
167 {
168 register struct ppp_softc *sc;
169 register int i = 0;
170
171 for (sc = ppp_softc; i < NPPP; sc++) {
172 sc->sc_if.if_name = "ppp";
173 sc->sc_if.if_unit = i++;
174 sc->sc_if.if_mtu = PPP_MTU;
175 sc->sc_if.if_flags = IFF_POINTOPOINT;
176 sc->sc_if.if_type = IFT_PPP;
177 sc->sc_if.if_hdrlen = sizeof(struct ppp_header);
178 sc->sc_if.if_ioctl = pppioctl;
179 sc->sc_if.if_output = pppoutput;
180 sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
181 sc->sc_inq.ifq_maxlen = IFQ_MAXLEN;
182 if_attach(&sc->sc_if);
183 }
184 }
185
186 /*
187 * Line specific open routine.
188 * Attach the given tty to the first available ppp unit.
189 */
190 /* ARGSUSED */
191 int
192 pppopen(dev, tp)
193 dev_t dev;
194 register struct tty *tp;
195 {
196 struct proc *p = curproc; /* XXX */
197 register struct ppp_softc *sc;
198 register int nppp;
199 int error, s;
200
201 if (error = suser(p->p_ucred, &p->p_acflag))
202 return (error);
203
204 if (tp->t_line == PPPDISC)
205 return (0);
206
207 for (nppp = 0, sc = ppp_softc; nppp < NPPP; nppp++, sc++)
208 if (sc->sc_ttyp == NULL) {
209 sc->sc_flags = 0;
210 sc->sc_ilen = 0;
211 sc->sc_asyncmap = 0xffffffff;
212 #ifdef VJC
213 sl_compress_init(&sc->sc_comp);
214 #endif
215 if (pppinit(sc) == 0) {
216 sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
217 return (ENOBUFS);
218 }
219 tp->t_sc = (caddr_t)sc;
220 sc->sc_ttyp = tp;
221 sc->sc_outm = NULL;
222 ttyflush(tp, FREAD | FWRITE);
223 sc->sc_if.if_flags |= IFF_RUNNING;
224
225 #ifdef PPP_OUTQ_SIZE
226 /* N.B. this code is designed *only* for use in NetBSD */
227 s = spltty();
228 /* get rid of the default outq clist buffer */
229 clfree(&tp->t_outq);
230 /* and get a new one, without quoting support, much larger */
231 clalloc(&tp->t_outq, PPP_OUTQ_SIZE, 0);
232 splx (s);
233 #endif /* PPP_OUTQ_SIZE */
234
235 return (0);
236 }
237
238 return (ENXIO);
239 }
240
241 /*
242 * Line specific close routine.
243 * Detach the tty from the ppp unit.
244 * Mimics part of ttyclose().
245 */
246 void
247 pppclose(tp, flag)
248 struct tty *tp;
249 int flag;
250 {
251 register struct ppp_softc *sc;
252 struct mbuf *m;
253 int s;
254
255 ttywflush(tp);
256 s = splimp(); /* paranoid; splnet probably ok */
257 tp->t_line = 0;
258 sc = (struct ppp_softc *)tp->t_sc;
259 if (sc != NULL) {
260 if_down(&sc->sc_if);
261 sc->sc_ttyp = NULL;
262 tp->t_sc = NULL;
263 m_freem(sc->sc_outm);
264 sc->sc_outm = NULL;
265 m_freem(sc->sc_m);
266 sc->sc_m = NULL;
267 for (;;) {
268 IF_DEQUEUE(&sc->sc_inq, m);
269 if (m == NULL)
270 break;
271 m_freem(m);
272 }
273 sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
274
275 #ifdef PPP_OUTQ_SIZE
276 /* reinstall default clist-buffer for outq
277 XXXX should really remember old value and restore that!! */
278 clfree(&tp->t_outq);
279 clalloc(&tp->t_outq, 1024, 0);
280 #endif /* PPP_OUTQ_SIZE */
281
282 }
283 splx(s);
284 }
285
286 /*
287 * Line specific (tty) read routine.
288 */
289 int
290 pppread(tp, uio, flag)
291 register struct tty *tp;
292 struct uio *uio;
293 int flag;
294 {
295 register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
296 struct mbuf *m, *m0;
297 register int s;
298 int error;
299
300 if ((tp->t_state & TS_CARR_ON)==0)
301 return (EIO);
302 s = splimp();
303 while (sc->sc_inq.ifq_head == NULL && tp->t_line == PPPDISC) {
304 if (tp->t_state & TS_ASYNC) {
305 splx(s);
306 return (EWOULDBLOCK);
307 }
308 error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin, 0);
309 if (error)
310 return error;
311 }
312 if (tp->t_line != PPPDISC) {
313 splx(s);
314 return (-1);
315 }
316
317 /* Pull place-holder byte out of canonical queue */
318 getc(&tp->t_canq);
319
320 /* Get the packet from the input queue */
321 IF_DEQUEUE(&sc->sc_inq, m0);
322 splx(s);
323
324 for (m = m0; m && uio->uio_resid; m = m->m_next)
325 if (error = uiomove(mtod(m, u_char *), m->m_len, uio))
326 break;
327 m_freem(m0);
328 return (error);
329 }
330
331 /*
332 * Line specific (tty) write routine.
333 */
334 int
335 pppwrite(tp, uio, flag)
336 register struct tty *tp;
337 struct uio *uio;
338 int flag;
339 {
340 register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
341 struct mbuf *m, *m0, **mp;
342 struct sockaddr dst;
343 struct ppp_header *ph1, *ph2;
344 int len, error;
345
346 if ((tp->t_state & TS_CARR_ON)==0)
347 return (EIO);
348 if (tp->t_line != PPPDISC)
349 return (EINVAL);
350 if (uio->uio_resid > sc->sc_if.if_mtu + sizeof (struct ppp_header) ||
351 uio->uio_resid < sizeof (struct ppp_header))
352 return (EMSGSIZE);
353 for (mp = &m0; uio->uio_resid; mp = &m->m_next) {
354 MGET(m, M_WAIT, MT_DATA);
355 if ((*mp = m) == NULL) {
356 m_freem(m0);
357 return (ENOBUFS);
358 }
359 if (uio->uio_resid >= MCLBYTES / 2)
360 MCLGET(m, M_DONTWAIT);
361 len = MIN(M_TRAILINGSPACE(m), uio->uio_resid);
362 if (error = uiomove(mtod(m, u_char *), len, uio)) {
363 m_freem(m0);
364 return (error);
365 }
366 m->m_len = len;
367 }
368 dst.sa_family = AF_UNSPEC;
369 ph1 = (struct ppp_header *) &dst.sa_data;
370 ph2 = mtod(m0, struct ppp_header *);
371 *ph1 = *ph2;
372 m0->m_data += sizeof (struct ppp_header);
373 m0->m_len -= sizeof (struct ppp_header);
374 return (pppoutput(&sc->sc_if, m0, &dst));
375 }
376
377 /*
378 * Line specific (tty) ioctl routine.
379 * Provide a way to get the ppp unit number.
380 * This discipline requires that tty device drivers call
381 * the line specific l_ioctl routine from their ioctl routines.
382 */
383 /* ARGSUSED */
384 int
385 ppptioctl(tp, cmd, data, flag)
386 struct tty *tp;
387 caddr_t data;
388 int cmd, flag;
389 {
390 register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
391 struct proc *p = curproc; /* XXX */
392 int s, error;
393
394 switch (cmd) {
395 #if 0 /* this is handled (properly) by ttioctl */
396 case TIOCGETD:
397 *(int *)data = sc->sc_if.if_unit;
398 break;
399 #endif
400 case FIONREAD:
401 *(int *)data = sc->sc_inq.ifq_len;
402 break;
403
404 case PPPIOCGUNIT:
405 *(int *)data = sc->sc_if.if_unit;
406 break;
407
408 case PPPIOCGFLAGS:
409 *(u_int *)data = sc->sc_flags;
410 break;
411
412 case PPPIOCSFLAGS:
413 if (error = suser(p->p_ucred, &p->p_acflag))
414 return (error);
415 #define SC_MASK 0xffff
416 s = splimp();
417 sc->sc_flags =
418 (sc->sc_flags & ~SC_MASK) | ((*(int *)data) & SC_MASK);
419 splx(s);
420 break;
421
422 case PPPIOCSASYNCMAP:
423 if (error = suser(p->p_ucred, &p->p_acflag))
424 return (error);
425 sc->sc_asyncmap = *(u_int *)data;
426 break;
427
428 case PPPIOCGASYNCMAP:
429 *(u_int *)data = sc->sc_asyncmap;
430 break;
431
432 default:
433 return (-1);
434 }
435 return (0);
436 }
437
438 /*
439 * FCS lookup table as calculated by genfcstab.
440 */
441 static u_short fcstab[256] = {
442 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
443 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
444 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
445 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
446 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
447 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
448 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
449 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
450 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
451 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
452 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
453 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
454 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
455 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
456 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
457 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
458 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
459 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
460 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
461 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
462 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
463 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
464 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
465 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
466 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
467 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
468 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
469 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
470 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
471 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
472 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
473 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
474 };
475
476 /*
477 * Calculate a new FCS given the current FCS and the new data.
478 */
479 static u_short
480 pppfcs(fcs, cp, len)
481 register u_short fcs;
482 register u_char *cp;
483 register int len;
484 {
485 while (len--)
486 fcs = PPP_FCS(fcs, *cp++);
487 return (fcs);
488 }
489
490 /*
491 * Queue a packet. Start transmission if not active.
492 * Packet is placed in Information field of PPP frame.
493 */
494 int
495 pppoutput(ifp, m0, dst)
496 struct ifnet *ifp;
497 struct mbuf *m0;
498 struct sockaddr *dst;
499 {
500 register struct ppp_softc *sc = &ppp_softc[ifp->if_unit];
501 struct mbuf *m, *m1;
502 struct ppp_header *ph;
503 u_short protocol, fcs;
504 u_char address, control, *cp;
505 int s, error, compac, compprot;
506
507 if (sc->sc_ttyp == NULL || (ifp->if_flags & IFF_RUNNING) == 0
508 || (ifp->if_flags & IFF_UP) == 0 && dst->sa_family != AF_UNSPEC) {
509 error = ENETDOWN; /* sort of */
510 goto bad;
511 }
512 if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) {
513 error = EHOSTUNREACH;
514 goto bad;
515 }
516
517 /*
518 * Compute PPP header.
519 */
520 address = PPP_ALLSTATIONS;
521 control = PPP_UI;
522 switch (dst->sa_family) {
523 #ifdef INET
524 case AF_INET:
525 protocol = PPP_IP;
526 #ifdef VJC
527 if (sc->sc_flags & SC_COMP_TCP) {
528 register struct ip *ip;
529
530 if ((ip = mtod(m0, struct ip *))->ip_p == IPPROTO_TCP) {
531 int type = sl_compress_tcp(m0, ip, &sc->sc_comp, 1);
532 switch (type) {
533 case TYPE_UNCOMPRESSED_TCP:
534 protocol = PPP_VJC_UNCOMP;
535 break;
536 case TYPE_COMPRESSED_TCP:
537 protocol = PPP_VJC_COMP;
538 break;
539 }
540 }
541 }
542 #endif
543 break;
544 #endif
545 #ifdef NS
546 case AF_NS:
547 protocol = PPP_XNS;
548 break;
549 #endif
550 case AF_UNSPEC:
551 ph = (struct ppp_header *) dst->sa_data;
552 address = ph->ph_address;
553 control = ph->ph_control;
554 protocol = ntohs(ph->ph_protocol);
555 break;
556 default:
557 printf("ppp%d: af%d not supported\n", ifp->if_unit, dst->sa_family);
558 error = EAFNOSUPPORT;
559 goto bad;
560 }
561 compac = sc->sc_flags & SC_COMP_AC && address == PPP_ALLSTATIONS &&
562 control == PPP_UI && protocol != PPP_ALLSTATIONS &&
563 protocol != PPP_LCP;
564 compprot = sc->sc_flags & SC_COMP_PROT && protocol < 0x100;
565
566 /*
567 * Add PPP header. If no space in first mbuf, allocate another.
568 */
569 if (M_LEADINGSPACE(m0) < sizeof(struct ppp_header)) {
570 m0 = m_prepend(m0, sizeof(struct ppp_header), M_DONTWAIT);
571 if (m0 == 0) {
572 error = ENOBUFS;
573 goto bad;
574 }
575 m0->m_len = 0;
576 } else
577 m0->m_data -= (compac ? 0 : 2) + (compprot ? 1 : 2);
578
579 cp = mtod(m0, u_char *);
580 if (!compac) {
581 *cp++ = address;
582 *cp++ = control;
583 m0->m_len += 2;
584 }
585 if (!compprot) {
586 *cp++ = protocol >> 8;
587 m0->m_len++;
588 }
589 *cp++ = protocol & 0xff;
590 m0->m_len++;
591
592 /*
593 * Add PPP trailer. Compute one's complement of FCS over frame
594 * and attach to mbuf chain least significant byte first.
595 */
596 fcs = PPP_INITFCS;
597 for (m = m0; m; m = m->m_next) {
598 fcs = pppfcs(fcs, mtod(m, u_char *), m->m_len);
599 m1 = m;
600 }
601 fcs ^= 0xffff;
602
603 /*
604 * If the last mbuf is a cluster, we can't just store the
605 * FCS in it (other mbufs might point to the same cluster).
606 */
607 if (M_TRAILINGSPACE(m1) < sizeof(short) || m1->m_flags & M_EXT) {
608 MGET(m, M_DONTWAIT, MT_HEADER);
609 if (m == 0) {
610 error = ENOBUFS;
611 goto bad;
612 }
613 m->m_next = NULL;
614 m->m_len = 0;
615 m1->m_next = m;
616 m1 = m;
617 }
618 cp = mtod(m1, u_char *) + m1->m_len;
619
620 *cp++ = fcs & 0xff;
621 *cp++ = fcs >> 8;
622 m1->m_len += 2;
623
624 if (ppp_async_out_debug) {
625 printf("ppp%d output: ", ifp->if_unit);
626 pppdumpm(m0, -1);
627 }
628
629 s = splimp();
630 if (IF_QFULL(&ifp->if_snd)) {
631 IF_DROP(&ifp->if_snd);
632 splx(s);
633 sc->sc_if.if_oerrors++;
634 error = ENOBUFS;
635 goto bad;
636 }
637 IF_ENQUEUE(&ifp->if_snd, m0);
638 if (CCOUNT(&sc->sc_ttyp->t_outq) == 0)
639 pppstart(sc->sc_ttyp);
640 splx(s);
641 return (0);
642
643 bad:
644 m_freem(m0);
645 return (error);
646 }
647
648 /*
649 * Start output on interface. Get another datagram
650 * to send from the interface queue and map it to
651 * the interface before starting output.
652 */
653 void
654 pppstart(tp)
655 register struct tty *tp;
656 {
657 register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
658 register struct mbuf *m;
659 register int len;
660 register u_char *start, *stop, *cp;
661 int n, s, ndone;
662 struct mbuf *m2;
663
664 for (;;) {
665 /*
666 * If there is more in the output queue, just send it now.
667 * We are being called in lieu of ttstart and must do what
668 * it would.
669 */
670 if (CCOUNT(&tp->t_outq) != 0 && tp->t_oproc != NULL) {
671 (*tp->t_oproc)(tp);
672 if (CCOUNT(&tp->t_outq) > PPP_HIWAT)
673 return;
674 }
675 /*
676 * This happens briefly when the line shuts down.
677 */
678 if (sc == NULL)
679 return;
680
681 /*
682 * See if we have an existing packet partly sent.
683 * If not, get a new packet and start sending it.
684 */
685 m = sc->sc_outm;
686 if (m == NULL) {
687 s = splimp();
688 IF_DEQUEUE(&sc->sc_if.if_snd, m);
689 splx(s);
690 if (m == NULL)
691 return;
692
693 /*
694 * The extra PPP_FLAG will start up a new packet, and thus
695 * will flush any accumulated garbage. We do this whenever
696 * the line may have been idle for some time.
697 */
698 if (CCOUNT(&tp->t_outq) == 0) {
699 ++sc->sc_bytessent;
700 (void) putc(PPP_FLAG, &tp->t_outq);
701 }
702 }
703
704 do {
705 start = mtod(m, u_char *);
706 len = m->m_len;
707 stop = start + len;
708 while (len > 0) {
709 /*
710 * Find out how many bytes in the string we can
711 * handle without doing something special.
712 */
713 for (cp = start; cp < stop; cp++)
714 if ((*cp == PPP_FLAG) || (*cp == PPP_ESCAPE) ||
715 (*cp < 0x20 && (sc->sc_asyncmap & (1 << *cp))))
716 break;
717 n = cp - start;
718 if (n) {
719 #ifndef RB_LEN
720 /* NetBSD, 4.3-Reno or similar. */
721 ndone = n - b_to_q(start, n, &tp->t_outq);
722 #else
723 /* 386BSD */
724 int cc, nleft;
725 for (nleft = n; nleft > 0; nleft -= cc) {
726 if ((cc = RB_CONTIGPUT(&tp->t_out)) == 0)
727 break;
728 cc = min (cc, nleft);
729 bcopy((char *)start, tp->t_out.rb_tl, cc);
730 tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
731 tp->t_out.rb_tl + cc);
732 }
733 ndone = n - nleft;
734 #endif /* RB_LEN */
735 len -= ndone;
736 start += ndone;
737 sc->sc_bytessent += ndone;
738
739 if (ndone < n)
740 break; /* packet doesn't fit */
741 }
742 /*
743 * If there are characters left in the mbuf,
744 * the first one must be special..
745 * Put it out in a different form.
746 */
747 if (len) {
748 if (putc(PPP_ESCAPE, &tp->t_outq))
749 break;
750 if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
751 (void) unputc(&tp->t_outq);
752 break;
753 }
754 sc->sc_bytessent += 2;
755 start++;
756 len--;
757 }
758 }
759 /*
760 * If we didn't empty this mbuf, remember where we're up to.
761 * If we emptied the last mbuf, try to add the closing flag,
762 * and if we can't, leave sc_outm pointing to m, but with
763 * m->m_len == 0, to remind us to output the flag later.
764 */
765 if (len > 0 || m->m_next == NULL && putc(PPP_FLAG, &tp->t_outq)) {
766 m->m_data = start;
767 m->m_len = len;
768 sc->sc_outm = m;
769 if (tp->t_oproc != NULL)
770 (*tp->t_oproc)(tp);
771 return; /* can't do any more at the moment */
772 }
773
774 /* Finished with this mbuf; free it and move on. */
775 MFREE(m, m2);
776 m = m2;
777 } while (m);
778
779 /* Finished a packet */
780 sc->sc_outm = NULL;
781 sc->sc_bytessent++; /* account for closing flag */
782 sc->sc_if.if_opackets++;
783 sc->sc_if.if_obytes = sc->sc_bytessent;
784 }
785 }
786
787 /*
788 * Allocate enough mbuf to handle current MTU.
789 */
790 static int
791 pppinit(sc)
792 register struct ppp_softc *sc;
793 {
794 struct mbuf *m, **mp;
795 int len = HDROFF + MAX(sc->sc_if.if_mtu, PPP_MRU) +
796 sizeof (struct ppp_header) + sizeof (u_short);
797 int s;
798
799 s = splimp();
800 for (mp = &sc->sc_m; (m = *mp) != NULL; mp = &m->m_next)
801 if ((len -= M_DATASIZE(m)) <= 0) {
802 splx(s);
803 return (1);
804 }
805
806 for (;; mp = &m->m_next) {
807 MGETHDR(m, M_DONTWAIT, MT_DATA);
808 if (m == 0) {
809 m_freem(sc->sc_m);
810 sc->sc_m = NULL;
811 splx(s);
812 printf("ppp%d: can't allocate mbuf\n", sc - ppp_softc);
813 return (0);
814 }
815 *mp = m;
816 MCLGET(m, M_DONTWAIT);
817 if ((len -= M_DATASIZE(m)) <= 0) {
818 splx(s);
819 return (1);
820 }
821 }
822 }
823
824 /*
825 * Copy mbuf chain. Would like to use m_copy(), but we need a real copy
826 * of the data, not just copies of pointers to the data.
827 */
828 static struct mbuf *
829 ppp_btom(sc)
830 struct ppp_softc *sc;
831 {
832 register struct mbuf *m, **mp;
833 struct mbuf *top = sc->sc_m;
834
835 /*
836 * First check current mbuf. If we have more than a small mbuf,
837 * return the whole cluster and set beginning of buffer to the
838 * next mbuf.
839 * Else, copy the current bytes into a small mbuf, attach the new
840 * mbuf to the end of the chain and set beginning of buffer to the
841 * current mbuf.
842 */
843
844 if (sc->sc_mc->m_len > MHLEN) {
845 sc->sc_m = sc->sc_mc->m_next;
846 sc->sc_mc->m_next = NULL;
847 }
848 else {
849 /* rather than waste a whole cluster on <= MHLEN bytes,
850 alloc a small mbuf and copy to it */
851 MGETHDR(m, M_DONTWAIT, MT_DATA);
852 if (m == NULL)
853 return (NULL);
854
855 bcopy(mtod(sc->sc_mc, caddr_t), mtod(m, caddr_t),
856 sc->sc_mc->m_len);
857 m->m_len = sc->sc_mc->m_len;
858 for (mp = ⊤ *mp != sc->sc_mc; mp = &(*mp)->m_next)
859 ;
860 *mp = m;
861 sc->sc_m = sc->sc_mc;
862 }
863
864 /*
865 * Try to allocate enough extra mbufs to handle the next packet.
866 */
867 if (pppinit(sc) == 0) {
868 m_freem(top);
869 if (pppinit(sc) == 0)
870 sc->sc_if.if_flags &= ~IFF_UP;
871 return (NULL);
872 }
873
874 return (top);
875 }
876
877 /*
878 * tty interface receiver interrupt.
879 */
880 #define COMPTYPE(proto) ((proto) == PPP_VJC_COMP? TYPE_COMPRESSED_TCP: \
881 TYPE_UNCOMPRESSED_TCP)
882
883 void
884 pppinput(c, tp)
885 int c;
886 register struct tty *tp;
887 {
888 register struct ppp_softc *sc;
889 struct mbuf *m;
890 struct ifqueue *inq;
891 int s, ilen, xlen, proto;
892 char *pkttype;
893
894 tk_nin++;
895 sc = (struct ppp_softc *)tp->t_sc;
896 if (sc == NULL)
897 return;
898
899 ++sc->sc_if.if_ibytes;
900
901 if (c & TTY_FE)
902 /* framing error or overrun on this char - abort packet */
903 goto flush;
904
905 c &= 0xff;
906 if (c == PPP_FLAG) {
907 ilen = sc->sc_ilen;
908 sc->sc_ilen = 0;
909
910 if (sc->sc_flags & SC_FLUSH
911 || ilen > 0 && sc->sc_fcs != PPP_GOODFCS) {
912 #ifdef VJC
913 /*
914 * If we've missed a packet, we must toss subsequent compressed
915 * packets which don't have an explicit connection ID.
916 */
917 sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &sc->sc_comp);
918 #endif
919 if ((sc->sc_flags & SC_FLUSH) == 0){
920 if (ppp_debug)
921 printf("ppp: bad fcs\n");
922 sc->sc_if.if_ierrors++;
923 } else
924 sc->sc_flags &= ~SC_FLUSH;
925 return;
926 }
927
928 if (ilen < sizeof (struct ppp_header) + 2) {
929 if (ilen) {
930 if (ppp_debug)
931 printf("ppp: too short (%d)\n", ilen);
932 sc->sc_if.if_ierrors++;
933 }
934 return;
935 }
936
937 /*
938 * Remove FCS trailer. Somewhat painful...
939 */
940 ilen -= 2;
941 if (--sc->sc_mc->m_len == 0) {
942 for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next)
943 ;
944 sc->sc_mc = m;
945 }
946 sc->sc_mc->m_len--;
947
948 sc->sc_if.if_ipackets++;
949 m = sc->sc_m;
950
951 if (ppp_async_in_debug) {
952 printf("ppp%d: got %d bytes\n", sc - ppp_softc, ilen);
953 pppdumpm(m, ilen);
954 }
955
956 proto = ntohs(mtod(m, struct ppp_header *)->ph_protocol);
957 switch (proto) {
958 #ifdef INET
959 case PPP_IP:
960 ilen -= sizeof (struct ppp_header);
961 m->m_data += sizeof (struct ppp_header);
962 m->m_len -= sizeof (struct ppp_header);
963 break;
964
965 #ifdef VJC
966 case PPP_VJC_COMP:
967 case PPP_VJC_UNCOMP:
968 pkttype = proto == PPP_VJC_COMP? "": "un";
969 if (sc->sc_flags & SC_COMP_TCP) {
970
971 m->m_data += sizeof (struct ppp_header);
972 m->m_len -= sizeof (struct ppp_header);
973 ilen -= sizeof(struct ppp_header);
974
975 xlen = sl_uncompress_tcp_part((u_char **)(&m->m_data),
976 m->m_len, ilen,
977 COMPTYPE(proto), &sc->sc_comp);
978
979 if (xlen) {
980 /* adjust the first mbuf by the decompressed amt */
981 m->m_len += xlen - ilen;
982 ilen = xlen;
983 proto = PPP_IP;
984 break;
985 }
986
987 if (ppp_debug)
988 printf("ppp%d: sl_uncompress failed on type %scomp\n",
989 sc->sc_if.if_unit, pkttype);
990
991 } else {
992 if (ppp_debug)
993 printf("ppp%d: %scomp pkt w/o compression; flags 0x%x\n",
994 sc->sc_if.if_unit, pkttype, sc->sc_flags);
995 }
996 if (ppp_debug)
997 printf("ppp: packet rejected, protocol 0x%x\n", proto);
998 sc->sc_if.if_ierrors++;
999 return;
1000 #endif
1001 #endif
1002 }
1003
1004 /* get this packet as an mbuf chain */
1005 if ((m = ppp_btom(sc)) == NULL) {
1006 sc->sc_if.if_ierrors++;
1007 return;
1008 }
1009 m->m_pkthdr.len = ilen;
1010 m->m_pkthdr.rcvif = &sc->sc_if;
1011
1012 if (proto == PPP_IP) {
1013 /* IP packet - pass it up to IP */
1014 if ((sc->sc_if.if_flags & IFF_UP) == 0) {
1015 /* interface is down - drop the packet. */
1016 m_freem(m);
1017 sc->sc_if.if_ierrors++;
1018 return;
1019 }
1020 schednetisr(NETISR_IP);
1021 inq = &ipintrq;
1022
1023 } else {
1024 /* some other protocol - place on input queue for read() */
1025 /* Put a placeholder byte in canq for ttselect()/ttnread() */
1026 putc(0, &tp->t_canq);
1027 ttwakeup(tp);
1028 inq = &sc->sc_inq;
1029 }
1030
1031 s = splimp();
1032 if (IF_QFULL(inq)) {
1033 IF_DROP(inq);
1034 if (ppp_debug)
1035 printf("ppp: queue full\n");
1036 sc->sc_if.if_ierrors++;
1037 sc->sc_if.if_iqdrops++;
1038 m_freem(m);
1039 } else
1040 IF_ENQUEUE(inq, m);
1041
1042 splx(s);
1043 return;
1044 }
1045 if (sc->sc_flags & SC_FLUSH)
1046 return;
1047 if (c == PPP_ESCAPE) {
1048 sc->sc_flags |= SC_ESCAPED;
1049 return;
1050 }
1051
1052 if (sc->sc_flags & SC_ESCAPED) {
1053 sc->sc_flags &= ~SC_ESCAPED;
1054 c ^= PPP_TRANS;
1055 }
1056
1057 /*
1058 * Initialize buffer on first octet received.
1059 * First octet could be address or protocol (when compressing
1060 * address/control).
1061 * Second octet is control.
1062 * Third octet is first or second (when compressing protocol)
1063 * octet of protocol.
1064 * Fourth octet is second octet of protocol.
1065 */
1066 if (sc->sc_ilen == 0) {
1067 /* reset the first input mbuf */
1068 m = sc->sc_m;
1069 m->m_len = 0;
1070 m->m_data = M_DATASTART(sc->sc_m) + HDROFF;
1071 sc->sc_mc = m;
1072 sc->sc_mp = mtod(m, char *);
1073 sc->sc_fcs = PPP_INITFCS;
1074 if (c != PPP_ALLSTATIONS) {
1075 if ((sc->sc_flags & SC_COMP_AC) == 0) {
1076 if (ppp_debug)
1077 printf("ppp: missing ALLSTATIONS, got 0x%x; flags %x\n",
1078 c, sc->sc_flags);
1079 goto flush;
1080 }
1081 *sc->sc_mp++ = PPP_ALLSTATIONS;
1082 *sc->sc_mp++ = PPP_UI;
1083 sc->sc_ilen += 2;
1084 m->m_len += 2;
1085 }
1086 }
1087 if (sc->sc_ilen == 1 && c != PPP_UI) {
1088 if (ppp_debug)
1089 printf("ppp: missing UI, got 0x%x\n", c);
1090 goto flush;
1091 }
1092 if (sc->sc_ilen == 2 && (c & 1) == 1) {
1093 if ((sc->sc_flags & SC_COMP_PROT) == 0) {
1094 if (ppp_debug)
1095 printf("ppp: compressed protocol %x, but compression off\n",
1096 c);
1097 goto flush;
1098 }
1099 *sc->sc_mp++ = 0;
1100 sc->sc_ilen++;
1101 sc->sc_mc->m_len++;
1102 }
1103 if (sc->sc_ilen == 3 && (c & 1) == 0) {
1104 if (ppp_debug)
1105 printf("ppp: bad protocol %x\n", c);
1106 goto flush;
1107 }
1108
1109 /* packet beyond configured mtu? */
1110 if (++sc->sc_ilen > MAX(sc->sc_if.if_mtu, PPP_MRU) +
1111 sizeof (struct ppp_header) + sizeof (u_short)) {
1112 if (ppp_debug)
1113 printf("ppp: packet too big\n");
1114 goto flush;
1115 }
1116
1117 /* is this mbuf full? */
1118 m = sc->sc_mc;
1119 if (M_TRAILINGSPACE(m) <= 0) {
1120 sc->sc_mc = m = m->m_next;
1121 if (m == NULL) {
1122 printf("ppp%d: too few input mbufs!\n");
1123 goto flush;
1124 }
1125 m->m_len = 0;
1126 m->m_data = M_DATASTART(m);
1127 sc->sc_mp = mtod(m, char *);
1128 }
1129
1130 ++m->m_len;
1131 *sc->sc_mp++ = c;
1132 sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
1133 return;
1134
1135 flush:
1136 sc->sc_if.if_ierrors++;
1137 sc->sc_flags |= SC_FLUSH;
1138 }
1139
1140 /*
1141 * Process an ioctl request to interface.
1142 */
1143 pppioctl(ifp, cmd, data)
1144 register struct ifnet *ifp;
1145 int cmd;
1146 caddr_t data;
1147 {
1148 struct proc *p = curproc; /* XXX */
1149 register struct ppp_softc *sc = &ppp_softc[ifp->if_unit];
1150 register struct ifaddr *ifa = (struct ifaddr *)data;
1151 register struct ifreq *ifr = (struct ifreq *)data;
1152 int s = splimp(), error = 0;
1153
1154
1155 switch (cmd) {
1156 case SIOCSIFFLAGS:
1157 if ((ifp->if_flags & IFF_RUNNING) == 0)
1158 ifp->if_flags &= ~IFF_UP;
1159 break;
1160
1161 case SIOCSIFADDR:
1162 if (ifa->ifa_addr->sa_family != AF_INET)
1163 error = EAFNOSUPPORT;
1164 break;
1165
1166 case SIOCSIFDSTADDR:
1167 if (ifa->ifa_addr->sa_family != AF_INET)
1168 error = EAFNOSUPPORT;
1169 break;
1170
1171 case SIOCSIFMTU:
1172 if (error = suser(p->p_ucred, &p->p_acflag))
1173 return (error);
1174 sc->sc_if.if_mtu = ifr->ifr_mtu;
1175 if (pppinit(sc) == 0)
1176 error = ENOBUFS;
1177 break;
1178
1179 case SIOCGIFMTU:
1180 ifr->ifr_mtu = sc->sc_if.if_mtu;
1181 break;
1182
1183 default:
1184 error = EINVAL;
1185 }
1186 splx(s);
1187 return (error);
1188 }
1189
1190 #define MAX_DUMP_BYTES 128
1191
1192 static void
1193 pppdumpm(m0, pktlen)
1194 struct mbuf *m0;
1195 int pktlen;
1196 {
1197 char buf[2*MAX_DUMP_BYTES+4];
1198 char *bp = buf;
1199 struct mbuf *m;
1200 static char digits[] = "0123456789abcdef";
1201
1202 for (m = m0; m && pktlen; m = m->m_next) {
1203 int l = m->m_len;
1204 u_char *rptr = (u_char *)m->m_data;
1205
1206 if (pktlen > 0) {
1207 l = min(l, pktlen);
1208 pktlen -= l;
1209 }
1210 while (l--) {
1211 if (bp > buf + sizeof(buf) - 4)
1212 goto done;
1213 *bp++ = digits[*rptr >> 4]; /* convert byte to ascii hex */
1214 *bp++ = digits[*rptr++ & 0xf];
1215 }
1216
1217 if (m->m_next) {
1218 if (bp > buf + sizeof(buf) - 3)
1219 goto done;
1220 *bp++ = '|';
1221 }
1222 }
1223 done:
1224 if (m && pktlen)
1225 *bp++ = '>';
1226 *bp = 0;
1227 printf("%s\n", buf);
1228 }
1229
1230 static void
1231 pppdumpb(b, l)
1232 u_char *b;
1233 int l;
1234 {
1235 char buf[2*MAX_DUMP_BYTES+4];
1236 char *bp = buf;
1237 static char digits[] = "0123456789abcdef";
1238
1239 while (l--) {
1240 *bp++ = digits[*b >> 4]; /* convert byte to ascii hex */
1241 *bp++ = digits[*b++ & 0xf];
1242 if (bp >= buf + sizeof(buf) - 2) {
1243 *bp++ = '>';
1244 break;
1245 }
1246 }
1247
1248 *bp = 0;
1249 printf("%s\n", buf);
1250 }
1251
1252
1253 #endif /* NPPP > 0 */
1254