FreeBSD/Linux Kernel Cross Reference
sys/net/ppp_tty.c
1 /*
2 * ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous
3 * tty devices.
4 *
5 * Copyright (c) 1989 Carnegie Mellon University.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by Carnegie Mellon University. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * Drew D. Perkins
21 * Carnegie Mellon University
22 * 4910 Forbes Ave.
23 * Pittsburgh, PA 15213
24 * (412) 268-8576
25 * ddp@andrew.cmu.edu
26 *
27 * Based on:
28 * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89
29 *
30 * Copyright (c) 1987 Regents of the University of California.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms are permitted
34 * provided that the above copyright notice and this paragraph are
35 * duplicated in all such forms and that any documentation,
36 * advertising materials, and other materials related to such
37 * distribution and use acknowledge that the software was developed
38 * by the University of California, Berkeley. The name of the
39 * University may not be used to endorse or promote products derived
40 * from this software without specific prior written permission.
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
42 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
43 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
44 *
45 * Serial Line interface
46 *
47 * Rick Adams
48 * Center for Seismic Studies
49 * 1300 N 17th Street, Suite 1450
50 * Arlington, Virginia 22209
51 * (703)276-7900
52 * rick@seismo.ARPA
53 * seismo!rick
54 *
55 * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
56 * Converted to 4.3BSD Beta by Chris Torek.
57 * Other changes made at Berkeley, based in part on code by Kirk Smith.
58 *
59 * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com)
60 * Added VJ tcp header compression; more unified ioctls
61 *
62 * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au).
63 * Cleaned up a lot of the mbuf-related code to fix bugs that
64 * caused system crashes and packet corruption. Changed pppstart
65 * so that it doesn't just give up with a "collision" if the whole
66 * packet doesn't fit in the output ring buffer.
67 *
68 * Added priority queueing for interactive IP packets, following
69 * the model of if_sl.c, plus hooks for bpf.
70 * Paul Mackerras (paulus@cs.anu.edu.au).
71 */
72
73 /* $FreeBSD$ */
74
75 #include "opt_ppp.h" /* XXX for ppp_defs.h */
76
77 #define VJC /* XXX for ppp_defs.h */
78
79 #include <sys/param.h>
80 #include <sys/systm.h>
81 #include <sys/proc.h>
82 #include <sys/mbuf.h>
83 #include <sys/dkstat.h>
84 #include <sys/socket.h>
85 #include <sys/fcntl.h>
86 #include <sys/tty.h>
87 #include <sys/conf.h>
88 #include <sys/uio.h>
89 #include <sys/vnode.h>
90
91 #ifdef __i386__
92 #include <i386/isa/intr_machdep.h>
93 #endif
94
95 #ifdef PPP_FILTER
96 #include <net/bpf.h>
97 #endif
98 #include <net/if_ppp.h>
99 #include <net/if_pppvar.h>
100
101 static int pppopen __P((dev_t dev, struct tty *tp));
102 static int pppclose __P((struct tty *tp, int flag));
103 static int pppread __P((struct tty *tp, struct uio *uio, int flag));
104 static int pppwrite __P((struct tty *tp, struct uio *uio, int flag));
105 static int ppptioctl __P((struct tty *tp, u_long cmd, caddr_t data, int flag,
106 struct proc *));
107 static int pppinput __P((int c, struct tty *tp));
108 static int pppstart __P((struct tty *tp));
109
110 static u_short pppfcs __P((u_short fcs, u_char *cp, int len));
111 static void pppasyncstart __P((struct ppp_softc *));
112 static void pppasyncctlp __P((struct ppp_softc *));
113 static void pppasyncrelinq __P((struct ppp_softc *));
114 static void pppasyncsetmtu __P((struct ppp_softc *));
115 static void ppp_timeout __P((void *));
116 static void pppgetm __P((struct ppp_softc *sc));
117 static void ppplogchar __P((struct ppp_softc *, int));
118
119 /* XXX called from if_ppp.c - layering violation */
120 void pppasyncattach __P((void *));
121
122 /*
123 * Some useful mbuf macros not in mbuf.h.
124 */
125 #define M_IS_CLUSTER(m) ((m)->m_flags & M_EXT)
126
127 #define M_DATASTART(m) \
128 (M_IS_CLUSTER(m) ? (m)->m_ext.ext_buf : \
129 (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat)
130
131 #define M_DATASIZE(m) \
132 (M_IS_CLUSTER(m) ? (m)->m_ext.ext_size : \
133 (m)->m_flags & M_PKTHDR ? MHLEN: MLEN)
134
135 /*
136 * Does c need to be escaped?
137 */
138 #define ESCAPE_P(c) (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F)))
139
140 /*
141 * Procedures for using an async tty interface for PPP.
142 */
143
144 /* This is a FreeBSD-2.X kernel. */
145 #define CCOUNT(q) ((q)->c_cc)
146 #define PPP_LOWAT 100 /* Process more output when < LOWAT on queue */
147 #define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */
148
149 /*
150 * Define the PPP line discipline.
151 */
152
153 static struct linesw pppdisc = {
154 pppopen, pppclose, pppread, pppwrite,
155 ppptioctl, pppinput, pppstart, ttymodem,
156 PPP_FLAG
157 };
158
159 void
160 pppasyncattach(dummy)
161 void *dummy;
162 {
163 #ifdef __i386__
164 int s;
165
166 s = splhigh();
167
168 /*
169 * Make sure that the soft net "engine" cannot run while spltty code is
170 * active. The if_ppp.c code can walk down into b_to_q etc, and it is
171 * bad if the tty system was in the middle of another b_to_q...
172 */
173 tty_imask |= softnet_imask; /* spltty() block spl[soft]net() */
174 net_imask |= softtty_imask; /* splimp() block splsofttty() */
175 net_imask |= tty_imask; /* splimp() block spltty() */
176 update_intr_masks();
177
178 splx(s);
179 if ( bootverbose )
180 printf("new masks: bio %x, tty %x, net %x\n",
181 bio_imask, tty_imask, net_imask);
182 #endif
183
184 /* register line discipline */
185 linesw[PPPDISC] = pppdisc;
186 }
187
188 /*
189 * Line specific open routine for async tty devices.
190 * Attach the given tty to the first available ppp unit.
191 * Called from device open routine or ttioctl() at >= splsofttty()
192 */
193 /* ARGSUSED */
194 static int
195 pppopen(dev, tp)
196 dev_t dev;
197 register struct tty *tp;
198 {
199 struct proc *p = curproc; /* XXX */
200 register struct ppp_softc *sc;
201 int error, s;
202
203 if ((error = suser(p)) != 0)
204 return (error);
205
206 s = spltty();
207
208 if (tp->t_line == PPPDISC) {
209 sc = (struct ppp_softc *) tp->t_sc;
210 if (sc != NULL && sc->sc_devp == (void *) tp) {
211 splx(s);
212 return (0);
213 }
214 }
215
216 if ((sc = pppalloc(p->p_pid)) == NULL) {
217 splx(s);
218 return ENXIO;
219 }
220
221 if (sc->sc_relinq)
222 (*sc->sc_relinq)(sc); /* get previous owner to relinquish the unit */
223
224 sc->sc_ilen = 0;
225 sc->sc_m = NULL;
226 bzero(sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
227 sc->sc_asyncmap[0] = 0xffffffff;
228 sc->sc_asyncmap[3] = 0x60000000;
229 sc->sc_rasyncmap = 0;
230 sc->sc_devp = (void *) tp;
231 sc->sc_start = pppasyncstart;
232 sc->sc_ctlp = pppasyncctlp;
233 sc->sc_relinq = pppasyncrelinq;
234 sc->sc_setmtu = pppasyncsetmtu;
235 sc->sc_outm = NULL;
236 pppgetm(sc);
237 sc->sc_if.if_flags |= IFF_RUNNING;
238 getmicrotime(&sc->sc_if.if_lastchange);
239 sc->sc_if.if_baudrate = tp->t_ospeed;
240
241 tp->t_sc = (caddr_t) sc;
242 ttyflush(tp, FREAD | FWRITE);
243
244 /*
245 * Pre-allocate cblocks to the "just right" amount. The 1 byte t_canq
246 * allocation helps avoid the need for select and/or FIONREAD.
247 * We also pass 1 byte tokens through t_canq...
248 */
249 clist_alloc_cblocks(&tp->t_canq, 1, 1);
250 clist_alloc_cblocks(&tp->t_outq, sc->sc_if.if_mtu + PPP_HIWAT,
251 sc->sc_if.if_mtu + PPP_HIWAT);
252 clist_alloc_cblocks(&tp->t_rawq, 0, 0);
253
254 splx(s);
255
256 return (0);
257 }
258
259 /*
260 * Line specific close routine, called from device close routine
261 * and from ttioctl at >= splsofttty().
262 * Detach the tty from the ppp unit.
263 * Mimics part of ttyclose().
264 */
265 static int
266 pppclose(tp, flag)
267 struct tty *tp;
268 int flag;
269 {
270 register struct ppp_softc *sc;
271 int s;
272
273 s = spltty();
274 ttyflush(tp, FREAD | FWRITE);
275 clist_free_cblocks(&tp->t_canq);
276 clist_free_cblocks(&tp->t_outq);
277 tp->t_line = 0;
278 sc = (struct ppp_softc *) tp->t_sc;
279 if (sc != NULL) {
280 tp->t_sc = NULL;
281 if (tp == (struct tty *) sc->sc_devp) {
282 pppasyncrelinq(sc);
283 pppdealloc(sc);
284 }
285 }
286 splx(s);
287 return 0;
288 }
289
290 /*
291 * Relinquish the interface unit to another device.
292 */
293 static void
294 pppasyncrelinq(sc)
295 struct ppp_softc *sc;
296 {
297 int s;
298
299 s = spltty();
300 if (sc->sc_outm) {
301 m_freem(sc->sc_outm);
302 sc->sc_outm = NULL;
303 }
304 if (sc->sc_m) {
305 m_freem(sc->sc_m);
306 sc->sc_m = NULL;
307 }
308 if (sc->sc_flags & SC_TIMEOUT) {
309 untimeout(ppp_timeout, (void *) sc, sc->sc_ch);
310 sc->sc_flags &= ~SC_TIMEOUT;
311 }
312 splx(s);
313 }
314
315 /*
316 * This gets called from the upper layer to notify a mtu change
317 */
318 static void
319 pppasyncsetmtu(sc)
320 register struct ppp_softc *sc;
321 {
322 register struct tty *tp = (struct tty *) sc->sc_devp;
323 int s;
324
325 s = spltty();
326 if (tp != NULL)
327 clist_alloc_cblocks(&tp->t_outq, sc->sc_if.if_mtu + PPP_HIWAT,
328 sc->sc_if.if_mtu + PPP_HIWAT);
329 splx(s);
330 }
331
332 /*
333 * Line specific (tty) read routine.
334 * called at zero spl from the device driver in the response to user-level
335 * reads on the tty file descriptor (ie: pppd).
336 */
337 static int
338 pppread(tp, uio, flag)
339 register struct tty *tp;
340 struct uio *uio;
341 int flag;
342 {
343 register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
344 struct mbuf *m, *m0;
345 register int s;
346 int error = 0;
347
348 if (sc == NULL)
349 return 0;
350 /*
351 * Loop waiting for input, checking that nothing disasterous
352 * happens in the meantime.
353 */
354 s = spltty();
355 for (;;) {
356 if (tp != (struct tty *) sc->sc_devp || tp->t_line != PPPDISC) {
357 splx(s);
358 return 0;
359 }
360 if (sc->sc_inq.ifq_head != NULL)
361 break;
362 if ((tp->t_state & TS_CONNECTED) == 0) {
363 splx(s);
364 return 0; /* end of file */
365 }
366 if (tp->t_state & TS_ASYNC || flag & IO_NDELAY) {
367 splx(s);
368 return (EWOULDBLOCK);
369 }
370 error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH, "pppin", 0);
371 if (error) {
372 splx(s);
373 return error;
374 }
375 }
376
377 /* Pull place-holder byte out of canonical queue */
378 getc(&tp->t_canq);
379
380 /* Get the packet from the input queue */
381 IF_DEQUEUE(&sc->sc_inq, m0);
382 splx(s);
383
384 for (m = m0; m && uio->uio_resid; m = m->m_next)
385 if ((error = uiomove(mtod(m, u_char *), m->m_len, uio)) != 0)
386 break;
387 m_freem(m0);
388 return (error);
389 }
390
391 /*
392 * Line specific (tty) write routine.
393 * called at zero spl from the device driver in the response to user-level
394 * writes on the tty file descriptor (ie: pppd).
395 */
396 static int
397 pppwrite(tp, uio, flag)
398 register struct tty *tp;
399 struct uio *uio;
400 int flag;
401 {
402 register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
403 struct mbuf *m, *m0, **mp;
404 struct sockaddr dst;
405 int len, error, s;
406
407 if ((tp->t_state & TS_CONNECTED) == 0)
408 return 0; /* wrote 0 bytes */
409 if (tp->t_line != PPPDISC)
410 return (EINVAL);
411 if (sc == NULL || tp != (struct tty *) sc->sc_devp)
412 return EIO;
413 if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HDRLEN ||
414 uio->uio_resid < PPP_HDRLEN)
415 return (EMSGSIZE);
416
417 s = spltty();
418 for (mp = &m0; uio->uio_resid; mp = &m->m_next) {
419 MGET(m, M_WAIT, MT_DATA);
420 if ((*mp = m) == NULL) {
421 m_freem(m0);
422 splx(s);
423 return (ENOBUFS);
424 }
425 m->m_len = 0;
426 if (uio->uio_resid >= MCLBYTES / 2)
427 MCLGET(m, M_DONTWAIT);
428 len = M_TRAILINGSPACE(m);
429 if (len > uio->uio_resid)
430 len = uio->uio_resid;
431 if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) {
432 m_freem(m0);
433 splx(s);
434 return (error);
435 }
436 m->m_len = len;
437 }
438 dst.sa_family = AF_UNSPEC;
439 bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN);
440 m0->m_data += PPP_HDRLEN;
441 m0->m_len -= PPP_HDRLEN;
442
443 /* call the upper layer to "transmit" it... */
444 error = pppoutput(&sc->sc_if, m0, &dst, (struct rtentry *)0);
445 splx(s);
446 return (error);
447 }
448
449 /*
450 * Line specific (tty) ioctl routine.
451 * This discipline requires that tty device drivers call
452 * the line specific l_ioctl routine from their ioctl routines.
453 */
454 /* ARGSUSED */
455 static int
456 ppptioctl(tp, cmd, data, flag, p)
457 struct tty *tp;
458 u_long cmd;
459 caddr_t data;
460 int flag;
461 struct proc *p;
462 {
463 struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
464 int error, s;
465
466 if (sc == NULL || tp != (struct tty *) sc->sc_devp)
467 return (ENOIOCTL);
468
469 error = 0;
470 switch (cmd) {
471 case PPPIOCSASYNCMAP:
472 if ((error = suser(p)) != 0)
473 break;
474 sc->sc_asyncmap[0] = *(u_int *)data;
475 break;
476
477 case PPPIOCGASYNCMAP:
478 *(u_int *)data = sc->sc_asyncmap[0];
479 break;
480
481 case PPPIOCSRASYNCMAP:
482 if ((error = suser(p)) != 0)
483 break;
484 sc->sc_rasyncmap = *(u_int *)data;
485 break;
486
487 case PPPIOCGRASYNCMAP:
488 *(u_int *)data = sc->sc_rasyncmap;
489 break;
490
491 case PPPIOCSXASYNCMAP:
492 if ((error = suser(p)) != 0)
493 break;
494 s = spltty();
495 bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
496 sc->sc_asyncmap[1] = 0; /* mustn't escape 0x20 - 0x3f */
497 sc->sc_asyncmap[2] &= ~0x40000000; /* mustn't escape 0x5e */
498 sc->sc_asyncmap[3] |= 0x60000000; /* must escape 0x7d, 0x7e */
499 splx(s);
500 break;
501
502 case PPPIOCGXASYNCMAP:
503 bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap));
504 break;
505
506 default:
507 error = pppioctl(sc, cmd, data, flag, p);
508 if (error == 0 && cmd == PPPIOCSMRU)
509 pppgetm(sc);
510 }
511
512 return error;
513 }
514
515 /*
516 * FCS lookup table as calculated by genfcstab.
517 */
518 static u_short fcstab[256] = {
519 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
520 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
521 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
522 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
523 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
524 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
525 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
526 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
527 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
528 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
529 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
530 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
531 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
532 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
533 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
534 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
535 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
536 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
537 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
538 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
539 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
540 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
541 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
542 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
543 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
544 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
545 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
546 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
547 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
548 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
549 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
550 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
551 };
552
553 /*
554 * Calculate a new FCS given the current FCS and the new data.
555 */
556 static u_short
557 pppfcs(u_short fcs, u_char *cp, int len)
558 {
559 while (len--)
560 fcs = PPP_FCS(fcs, *cp++);
561 return (fcs);
562 }
563
564 /*
565 * This gets called at splsoftnet from if_ppp.c at various times
566 * when there is data ready to be sent.
567 */
568 static void
569 pppasyncstart(sc)
570 register struct ppp_softc *sc;
571 {
572 register struct tty *tp = (struct tty *) sc->sc_devp;
573 register struct mbuf *m;
574 register int len;
575 register u_char *start, *stop, *cp;
576 int n, ndone, done, idle;
577 int s;
578
579 idle = 0;
580 /* XXX assumes atomic access to *tp although we're not at spltty(). */
581 while (CCOUNT(&tp->t_outq) < PPP_HIWAT) {
582 /*
583 * See if we have an existing packet partly sent.
584 * If not, get a new packet and start sending it.
585 */
586 m = sc->sc_outm;
587 if (m == NULL) {
588 /*
589 * Get another packet to be sent.
590 */
591 m = ppp_dequeue(sc);
592 if (m == NULL) {
593 idle = 1;
594 break;
595 }
596
597 /*
598 * The extra PPP_FLAG will start up a new packet, and thus
599 * will flush any accumulated garbage. We do this whenever
600 * the line may have been idle for some time.
601 */
602 /* XXX as above. */
603 if (CCOUNT(&tp->t_outq) == 0) {
604 ++sc->sc_stats.ppp_obytes;
605 (void) putc(PPP_FLAG, &tp->t_outq);
606 }
607
608 /* Calculate the FCS for the first mbuf's worth. */
609 sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len);
610 getmicrotime(&sc->sc_if.if_lastchange);
611 }
612
613 for (;;) {
614 start = mtod(m, u_char *);
615 len = m->m_len;
616 stop = start + len;
617 while (len > 0) {
618 /*
619 * Find out how many bytes in the string we can
620 * handle without doing something special.
621 */
622 for (cp = start; cp < stop; cp++)
623 if (ESCAPE_P(*cp))
624 break;
625 n = cp - start;
626 if (n) {
627 /* NetBSD (0.9 or later), 4.3-Reno or similar. */
628 ndone = n - b_to_q(start, n, &tp->t_outq);
629 len -= ndone;
630 start += ndone;
631 sc->sc_stats.ppp_obytes += ndone;
632
633 if (ndone < n)
634 break; /* packet doesn't fit */
635 }
636 /*
637 * If there are characters left in the mbuf,
638 * the first one must be special.
639 * Put it out in a different form.
640 */
641 if (len) {
642 s = spltty();
643 if (putc(PPP_ESCAPE, &tp->t_outq)) {
644 splx(s);
645 break;
646 }
647 if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
648 (void) unputc(&tp->t_outq);
649 splx(s);
650 break;
651 }
652 splx(s);
653 sc->sc_stats.ppp_obytes += 2;
654 start++;
655 len--;
656 }
657 }
658
659 /*
660 * If we didn't empty this mbuf, remember where we're up to.
661 * If we emptied the last mbuf, try to add the FCS and closing
662 * flag, and if we can't, leave sc_outm pointing to m, but with
663 * m->m_len == 0, to remind us to output the FCS and flag later.
664 */
665 done = len == 0;
666 if (done && m->m_next == NULL) {
667 u_char *p, *q;
668 int c;
669 u_char endseq[8];
670
671 /*
672 * We may have to escape the bytes in the FCS.
673 */
674 p = endseq;
675 c = ~sc->sc_outfcs & 0xFF;
676 if (ESCAPE_P(c)) {
677 *p++ = PPP_ESCAPE;
678 *p++ = c ^ PPP_TRANS;
679 } else
680 *p++ = c;
681 c = (~sc->sc_outfcs >> 8) & 0xFF;
682 if (ESCAPE_P(c)) {
683 *p++ = PPP_ESCAPE;
684 *p++ = c ^ PPP_TRANS;
685 } else
686 *p++ = c;
687 *p++ = PPP_FLAG;
688
689 /*
690 * Try to output the FCS and flag. If the bytes
691 * don't all fit, back out.
692 */
693 s = spltty();
694 for (q = endseq; q < p; ++q)
695 if (putc(*q, &tp->t_outq)) {
696 done = 0;
697 for (; q > endseq; --q)
698 unputc(&tp->t_outq);
699 break;
700 }
701 splx(s);
702 if (done)
703 sc->sc_stats.ppp_obytes += q - endseq;
704 }
705
706 if (!done) {
707 /* remember where we got to */
708 m->m_data = start;
709 m->m_len = len;
710 break;
711 }
712
713 /* Finished with this mbuf; free it and move on. */
714 m = m_free(m);
715 if (m == NULL) {
716 /* Finished a packet */
717 break;
718 }
719 sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len);
720 }
721
722 /*
723 * If m == NULL, we have finished a packet.
724 * If m != NULL, we've either done as much work this time
725 * as we need to, or else we've filled up the output queue.
726 */
727 sc->sc_outm = m;
728 if (m)
729 break;
730 }
731
732 /* Call pppstart to start output again if necessary. */
733 s = spltty();
734 pppstart(tp);
735
736 /*
737 * This timeout is needed for operation on a pseudo-tty,
738 * because the pty code doesn't call pppstart after it has
739 * drained the t_outq.
740 */
741 if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) {
742 sc->sc_ch = timeout(ppp_timeout, (void *) sc, 1);
743 sc->sc_flags |= SC_TIMEOUT;
744 }
745
746 splx(s);
747 }
748
749 /*
750 * This gets called when a received packet is placed on
751 * the inq, at splsoftnet. The pppd daemon is to be woken up to do a read().
752 */
753 static void
754 pppasyncctlp(sc)
755 struct ppp_softc *sc;
756 {
757 struct tty *tp;
758 int s;
759
760 /* Put a placeholder byte in canq for ttselect()/ttnread(). */
761 s = spltty();
762 tp = (struct tty *) sc->sc_devp;
763 putc(0, &tp->t_canq);
764 ttwakeup(tp);
765 splx(s);
766 }
767
768 /*
769 * Start output on async tty interface. If the transmit queue
770 * has drained sufficiently, arrange for pppasyncstart to be
771 * called later at splsoftnet.
772 * Called at spltty or higher.
773 */
774 int
775 pppstart(tp)
776 register struct tty *tp;
777 {
778 register struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
779
780 /*
781 * Call output process whether or not there is any output.
782 * We are being called in lieu of ttstart and must do what it would.
783 */
784 if (tp->t_oproc != NULL)
785 (*tp->t_oproc)(tp);
786
787 /*
788 * If the transmit queue has drained and the tty has not hung up
789 * or been disconnected from the ppp unit, then tell if_ppp.c that
790 * we need more output.
791 */
792 if (CCOUNT(&tp->t_outq) < PPP_LOWAT
793 && !((tp->t_state & TS_CONNECTED) == 0)
794 && sc != NULL && tp == (struct tty *) sc->sc_devp) {
795 ppp_restart(sc);
796 }
797
798 return 0;
799 }
800
801 /*
802 * Timeout routine - try to start some more output.
803 */
804 static void
805 ppp_timeout(x)
806 void *x;
807 {
808 struct ppp_softc *sc = (struct ppp_softc *) x;
809 struct tty *tp = (struct tty *) sc->sc_devp;
810 int s;
811
812 s = spltty();
813 sc->sc_flags &= ~SC_TIMEOUT;
814 pppstart(tp);
815 splx(s);
816 }
817
818 /*
819 * Allocate enough mbuf to handle current MRU.
820 */
821 static void
822 pppgetm(sc)
823 register struct ppp_softc *sc;
824 {
825 struct mbuf *m, **mp;
826 int len;
827
828 mp = &sc->sc_m;
829 for (len = sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){
830 if ((m = *mp) == NULL) {
831 MGETHDR(m, M_DONTWAIT, MT_DATA);
832 if (m == NULL)
833 break;
834 *mp = m;
835 MCLGET(m, M_DONTWAIT);
836 }
837 len -= M_DATASIZE(m);
838 mp = &m->m_next;
839 }
840 }
841
842 /*
843 * tty interface receiver interrupt.
844 */
845 static unsigned paritytab[8] = {
846 0x96696996, 0x69969669, 0x69969669, 0x96696996,
847 0x69969669, 0x96696996, 0x96696996, 0x69969669
848 };
849
850 /*
851 * Called when character is available from device driver.
852 * Only guaranteed to be at splsofttty() or spltty()
853 * This is safe to be called while the upper half's netisr is preempted.
854 */
855 static int
856 pppinput(c, tp)
857 int c;
858 register struct tty *tp;
859 {
860 register struct ppp_softc *sc;
861 struct mbuf *m;
862 int ilen, s;
863
864 sc = (struct ppp_softc *) tp->t_sc;
865 if (sc == NULL || tp != (struct tty *) sc->sc_devp)
866 return 0;
867
868 ++tk_nin;
869 ++sc->sc_stats.ppp_ibytes;
870
871 if ((tp->t_state & TS_CONNECTED) == 0) {
872 if (sc->sc_flags & SC_DEBUG)
873 printf("ppp%d: no carrier\n", sc->sc_if.if_unit);
874 goto flush;
875 }
876
877 if (c & TTY_ERRORMASK) {
878 /* framing error or overrun on this char - abort packet */
879 if (sc->sc_flags & SC_DEBUG)
880 printf("ppp%d: line error %x\n", sc->sc_if.if_unit,
881 c & TTY_ERRORMASK);
882 goto flush;
883 }
884
885 c &= TTY_CHARMASK;
886
887 /*
888 * Handle software flow control of output.
889 */
890 if (tp->t_iflag & IXON) {
891 if (c == tp->t_cc[VSTOP] && tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
892 if ((tp->t_state & TS_TTSTOP) == 0) {
893 tp->t_state |= TS_TTSTOP;
894 tp->t_stop(tp, 0);
895 }
896 return 0;
897 }
898 if (c == tp->t_cc[VSTART] && tp->t_cc[VSTART] != _POSIX_VDISABLE) {
899 tp->t_state &= ~TS_TTSTOP;
900 if (tp->t_oproc != NULL)
901 (*tp->t_oproc)(tp);
902 return 0;
903 }
904 }
905
906 s = spltty();
907 if (c & 0x80)
908 sc->sc_flags |= SC_RCV_B7_1;
909 else
910 sc->sc_flags |= SC_RCV_B7_0;
911 if (paritytab[c >> 5] & (1 << (c & 0x1F)))
912 sc->sc_flags |= SC_RCV_ODDP;
913 else
914 sc->sc_flags |= SC_RCV_EVNP;
915 splx(s);
916
917 if (sc->sc_flags & SC_LOG_RAWIN)
918 ppplogchar(sc, c);
919
920 if (c == PPP_FLAG) {
921 ilen = sc->sc_ilen;
922 sc->sc_ilen = 0;
923
924 if (sc->sc_rawin_count > 0)
925 ppplogchar(sc, -1);
926
927 /*
928 * If SC_ESCAPED is set, then we've seen the packet
929 * abort sequence "}~".
930 */
931 if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED)
932 || (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) {
933 s = spltty();
934 sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */
935 if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){
936 if (sc->sc_flags & SC_DEBUG)
937 printf("ppp%d: bad fcs %x, pkt len %d\n",
938 sc->sc_if.if_unit, sc->sc_fcs, ilen);
939 sc->sc_if.if_ierrors++;
940 sc->sc_stats.ppp_ierrors++;
941 } else
942 sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED);
943 splx(s);
944 return 0;
945 }
946
947 if (ilen < PPP_HDRLEN + PPP_FCSLEN) {
948 if (ilen) {
949 if (sc->sc_flags & SC_DEBUG)
950 printf("ppp%d: too short (%d)\n", sc->sc_if.if_unit, ilen);
951 s = spltty();
952 sc->sc_if.if_ierrors++;
953 sc->sc_stats.ppp_ierrors++;
954 sc->sc_flags |= SC_PKTLOST;
955 splx(s);
956 }
957 return 0;
958 }
959
960 /*
961 * Remove FCS trailer. Somewhat painful...
962 */
963 ilen -= 2;
964 if (--sc->sc_mc->m_len == 0) {
965 for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next)
966 ;
967 sc->sc_mc = m;
968 }
969 sc->sc_mc->m_len--;
970
971 /* excise this mbuf chain */
972 m = sc->sc_m;
973 sc->sc_m = sc->sc_mc->m_next;
974 sc->sc_mc->m_next = NULL;
975
976 ppppktin(sc, m, sc->sc_flags & SC_PKTLOST);
977 if (sc->sc_flags & SC_PKTLOST) {
978 s = spltty();
979 sc->sc_flags &= ~SC_PKTLOST;
980 splx(s);
981 }
982
983 pppgetm(sc);
984 return 0;
985 }
986
987 if (sc->sc_flags & SC_FLUSH) {
988 if (sc->sc_flags & SC_LOG_FLUSH)
989 ppplogchar(sc, c);
990 return 0;
991 }
992
993 if (c < 0x20 && (sc->sc_rasyncmap & (1 << c)))
994 return 0;
995
996 s = spltty();
997 if (sc->sc_flags & SC_ESCAPED) {
998 sc->sc_flags &= ~SC_ESCAPED;
999 c ^= PPP_TRANS;
1000 } else if (c == PPP_ESCAPE) {
1001 sc->sc_flags |= SC_ESCAPED;
1002 splx(s);
1003 return 0;
1004 }
1005 splx(s);
1006
1007 /*
1008 * Initialize buffer on first octet received.
1009 * First octet could be address or protocol (when compressing
1010 * address/control).
1011 * Second octet is control.
1012 * Third octet is first or second (when compressing protocol)
1013 * octet of protocol.
1014 * Fourth octet is second octet of protocol.
1015 */
1016 if (sc->sc_ilen == 0) {
1017 /* reset the first input mbuf */
1018 if (sc->sc_m == NULL) {
1019 pppgetm(sc);
1020 if (sc->sc_m == NULL) {
1021 if (sc->sc_flags & SC_DEBUG)
1022 printf("ppp%d: no input mbufs!\n", sc->sc_if.if_unit);
1023 goto flush;
1024 }
1025 }
1026 m = sc->sc_m;
1027 m->m_len = 0;
1028 m->m_data = M_DATASTART(sc->sc_m);
1029 sc->sc_mc = m;
1030 sc->sc_mp = mtod(m, char *);
1031 sc->sc_fcs = PPP_INITFCS;
1032 if (c != PPP_ALLSTATIONS) {
1033 if (sc->sc_flags & SC_REJ_COMP_AC) {
1034 if (sc->sc_flags & SC_DEBUG)
1035 printf("ppp%d: garbage received: 0x%x (need 0xFF)\n",
1036 sc->sc_if.if_unit, c);
1037 goto flush;
1038 }
1039 *sc->sc_mp++ = PPP_ALLSTATIONS;
1040 *sc->sc_mp++ = PPP_UI;
1041 sc->sc_ilen += 2;
1042 m->m_len += 2;
1043 }
1044 }
1045 if (sc->sc_ilen == 1 && c != PPP_UI) {
1046 if (sc->sc_flags & SC_DEBUG)
1047 printf("ppp%d: missing UI (0x3), got 0x%x\n",
1048 sc->sc_if.if_unit, c);
1049 goto flush;
1050 }
1051 if (sc->sc_ilen == 2 && (c & 1) == 1) {
1052 /* a compressed protocol */
1053 *sc->sc_mp++ = 0;
1054 sc->sc_ilen++;
1055 sc->sc_mc->m_len++;
1056 }
1057 if (sc->sc_ilen == 3 && (c & 1) == 0) {
1058 if (sc->sc_flags & SC_DEBUG)
1059 printf("ppp%d: bad protocol %x\n", sc->sc_if.if_unit,
1060 (sc->sc_mp[-1] << 8) + c);
1061 goto flush;
1062 }
1063
1064 /* packet beyond configured mru? */
1065 if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) {
1066 if (sc->sc_flags & SC_DEBUG)
1067 printf("ppp%d: packet too big\n", sc->sc_if.if_unit);
1068 goto flush;
1069 }
1070
1071 /* is this mbuf full? */
1072 m = sc->sc_mc;
1073 if (M_TRAILINGSPACE(m) <= 0) {
1074 if (m->m_next == NULL) {
1075 pppgetm(sc);
1076 if (m->m_next == NULL) {
1077 if (sc->sc_flags & SC_DEBUG)
1078 printf("ppp%d: too few input mbufs!\n", sc->sc_if.if_unit);
1079 goto flush;
1080 }
1081 }
1082 sc->sc_mc = m = m->m_next;
1083 m->m_len = 0;
1084 m->m_data = M_DATASTART(m);
1085 sc->sc_mp = mtod(m, char *);
1086 }
1087
1088 ++m->m_len;
1089 *sc->sc_mp++ = c;
1090 sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
1091 return 0;
1092
1093 flush:
1094 if (!(sc->sc_flags & SC_FLUSH)) {
1095 s = spltty();
1096 sc->sc_if.if_ierrors++;
1097 sc->sc_stats.ppp_ierrors++;
1098 sc->sc_flags |= SC_FLUSH;
1099 splx(s);
1100 if (sc->sc_flags & SC_LOG_FLUSH)
1101 ppplogchar(sc, c);
1102 }
1103 return 0;
1104 }
1105
1106 #define MAX_DUMP_BYTES 128
1107
1108 static void
1109 ppplogchar(sc, c)
1110 struct ppp_softc *sc;
1111 int c;
1112 {
1113 if (c >= 0)
1114 sc->sc_rawin[sc->sc_rawin_count++] = c;
1115 if (sc->sc_rawin_count >= sizeof(sc->sc_rawin)
1116 || (c < 0 && sc->sc_rawin_count > 0)) {
1117 printf("ppp%d input: %*D", sc->sc_if.if_unit,
1118 sc->sc_rawin_count, sc->sc_rawin, " ");
1119 sc->sc_rawin_count = 0;
1120 }
1121 }
Cache object: 4945da8e09624ac4977d6aafab519f7c
|