* Copyright (c) 1987 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)if_sl.c 7.15 (Berkeley) %G%
* Center for Seismic Studies
* 1300 N 17th Street, Suite 1450
* Arlington, Virginia 22209
* Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
* N.B.: this belongs in netinet, not net, the way it stands now.
* Should have a link-layer type designation, but wouldn't be
* Converted to 4.3BSD Beta by Chris Torek.
* Other changes made at Berkeley, based in part on code by Kirk Smith.
* W. Jolitz, added slip abort & time domain window
* also added Van Jacobson's hdr compression code
/* $Header: if_sl.c,v 1.12 85/12/20 21:54:55 chris Exp $ */
/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "machine/mtpr.h"
* N.B.: SLMTU is now a hard limit on input packet size.
* SLMTU must be <= MCLBYTES - sizeof(struct ifnet *).
#define SLIP_HIWAT 1000 /* don't start a new packet if HIWAT on queue */
#define CLISTRESERVE 1000 /* Can't let clists get too low */
* SLIP ABORT ESCAPE MECHANISM:
* (inspired by HAYES modem escape arrangement)
* 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape }
* signals a "soft" exit from slip mode by usermode process
* (hard exit unimplemented -- currently system dependant)
#define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/
#define ABT_WAIT 1 /* in seconds - idle before an escape & after */
#define ABT_RECYCLE (5*2+2) /* in seconds - time window processing abort */
/* a "soft" abort means to pass a suggestion to user code to abort slip */
#define ABT_SOFT 3 /* count of escapes */
/* a "hard" abort means to force abort slip within the kernel -- process jam? */
#define ABT_HARD 5 /* count of escapes */
* Only accept packets with octets that come at least this often.
* With non-reliable but fast modems (FAX, Packet Radio), we assume that
* packets come in groups (time domain), and that fractional groups that
* come erratically are just noise that will foul subsequent packets.
* We reject them on a time filter basis.
* This is a very coarse filter, because error correcting modems like the
* telebit take there own sweet time encoding/decoding packets. If you
* are using an MNP,PEP or other such arrangement, this won't help much.
* If you are using packet radio, use the millisecond time val with
* as small a resolution as possible. In any case, the coarse filter
* saves noisey lines about 50 % of the time.
#define TIME_WINDOW 2 /* max seconds between valid packet chars */
struct sl_softc sl_softc
[NSL
];
#define FRAME_END 0300 /* Frame End */
#define FRAME_ESCAPE 0333 /* Frame Esc */
#define TRANS_FRAME_END 0334 /* transposed frame end */
#define TRANS_FRAME_ESCAPE 0335 /* transposed frame esc */
int sloutput(), slioctl(), ttrstrt();
* Called from boot code to establish sl interfaces.
register struct sl_softc
*sc
;
for (sc
= sl_softc
; i
< NSL
; sc
++) {
sc
->sc_if
.if_name
= "sl";
sc
->sc_if
.if_mtu
= SLMTU
;
sc
->sc_if
.if_flags
= IFF_POINTOPOINT
;
sc
->sc_if
.if_ioctl
= slioctl
;
sc
->sc_if
.if_output
= sloutput
;
sc
->sc_if
.if_snd
.ifq_maxlen
= IFQ_MAXLEN
;
* Line specific open routine.
* Attach the given tty to the first available sl unit.
register struct sl_softc
*sc
;
if (error
= suser(u
.u_cred
, &u
.u_acflag
))
if (tp
->t_line
== SLIPDISC
)
for (nsl
= 0, sc
= sl_softc
; nsl
< NSL
; nsl
++, sc
++)
if (sc
->sc_ttyp
== NULL
) {
sl_compress_init(&sc
->sc_comp
);
ttyflush(tp
, FREAD
| FWRITE
);
* Line specific close routine.
* Detach the tty from the sl unit.
* Mimics part of ttyclose().
register struct sl_softc
*sc
;
s
= splimp(); /* paranoid; splnet probably ok */
sc
= (struct sl_softc
*)tp
->t_sc
;
MCLFREE((struct mbuf
*)sc
->sc_buf
);
sc
->sc_mp
= (char *) 4; /*XXX!?! */
* Line specific (tty) ioctl routine.
* Provide a way to get the sl unit number.
sltioctl(tp
, cmd
, data
, flag
)
struct sl_softc
*sc
= (struct sl_softc
*)tp
->t_sc
;
*(int *)data
= sc
->sc_if
.if_unit
;
if (tp
->t_state
&TS_CARR_ON
)
*(int *)data
= TIOCM_CAR
;
if (sc
->sc_flags
&SC_ABORT
)
*(int *)data
|= TIOCM_DTR
;
*(int *)data
= sc
->sc_flags
;
#define SC_MASK (SC_COMPRESS|SC_NOICMP)
(sc
->sc_flags
&~ SC_MASK
) | ((*(int *)data
) & SC_MASK
);
* Queue a packet. Start transmission if not active.
register struct ifnet
*ifp
;
register struct sl_softc
*sc
;
* `Cannot happen' (see slioctl). Someday we will extend
* the line protocol to support other address families.
if (dst
->sa_family
!= AF_INET
) {
printf("sl%d: af%d not supported\n", ifp
->if_unit
,
sc
= &sl_softc
[ifp
->if_unit
];
if (sc
->sc_ttyp
== NULL
) {
return (ENETDOWN
); /* sort of */
if ((sc
->sc_ttyp
->t_state
& TS_CARR_ON
) == 0) {
if (sc
->sc_flags
& (SC_COMPRESS
|SC_NOICMP
)) {
register struct ip
*ip
= mtod(m
, struct ip
*);
if (ip
->ip_p
== IPPROTO_TCP
) {
/* add stuff to TOS routing */
if (sc
->sc_flags
& SC_COMPRESS
)
(void) sl_compress_tcp(m
, ip
, &sc
->sc_comp
);
} else if ((sc
->sc_flags
& SC_NOICMP
) &&
ip
->ip_p
== IPPROTO_ICMP
) {
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
if (sc
->sc_ttyp
->t_outq
.c_cc
== 0) {
* Start output on interface. Get another datagram
* to send from the interface queue and map it to
* the interface before starting output.
register struct sl_softc
*sc
= (struct sl_softc
*)tp
->t_sc
;
* If there is more in the output queue, just send it now.
* We are being called in lieu of ttstart and must do what
if (tp
->t_outq
.c_cc
> SLIP_HIWAT
)
* This happens briefly when the line shuts down.
* If system is getting low on clists
* and we have something running already, stop here.
if (cfreecount
< CLISTRESERVE
+ SLMTU
&& tp
->t_outq
.c_cc
)
* Get a packet and send it to the interface.
IF_DEQUEUE(&sc
->sc_if
.if_snd
, m
);
* The extra FRAME_END will start up a new packet, and thus
* will flush any accumulated garbage. We do this whenever
* the line may have been idle for some time.
if (tp
->t_outq
.c_cc
== 0)
(void) putc(FRAME_END
, &tp
->t_outq
);
* Find out how many bytes in the string we can
* handle without doing something special.
nd
= locc(FRAME_ESCAPE
, len
, cp
);
np
= locc(FRAME_END
, len
, cp
);
* Put n characters at once
* into the tty output queue.
if (b_to_q((char *)cp
, n
, &tp
->t_outq
))
* If there are characters left in the mbuf,
* the first one must be special..
* Put it out in a different form.
if (putc(FRAME_ESCAPE
, &tp
->t_outq
))
if (putc(*cp
== FRAME_ESCAPE
?
TRANS_FRAME_ESCAPE
: TRANS_FRAME_END
,
(void) unputc(&tp
->t_outq
);
if (putc(FRAME_END
, &tp
->t_outq
)) {
* Not enough room. Remove a char to make room
* and end the packet normally.
* If you get many collisions (more than one or two
* a day) you probably do not have enough clists
* and you should increase "nclist" in param.c.
(void) unputc(&tp
->t_outq
);
(void) putc(FRAME_END
, &tp
->t_outq
);
sc
->sc_if
.if_collisions
++;
register struct sl_softc
*sc
;
if (sc
->sc_buf
== (char *) 0) {
printf("sl%d: can't allocate buffer\n", sc
- sl_softc
);
sc
->sc_if
.if_flags
&= ~IFF_UP
;
* Copy data buffer to mbuf chain; add ifnet pointer ifp.
register struct mbuf
*m
, **mp
;
MGETHDR(m
, M_DONTWAIT
, MT_DATA
);
MGET(m
, M_DONTWAIT
, MT_DATA
);
* If we have at least MINCLSIZE bytes,
* allocate a new page. Swap the current
* buffer page with the new one.
if (m
->m_flags
& M_EXT
) {
count
= MIN(len
, MCLBYTES
);
count
= MIN(len
, m
->m_len
);
bcopy(cp
, mtod(m
, caddr_t
), count
);
* tty interface receiver interrupt.
register struct sl_softc
*sc
;
sc
= (struct sl_softc
*)tp
->t_sc
;
if (!(tp
->t_state
&TS_CARR_ON
)) /*XXX*/
/* if we see an abort after "idle" time, count it */
if ((c
&0x7f) == ABT_ESC
&& time
.tv_sec
>= sc
->sc_lasttime
+ ABT_WAIT
) {
/* record when the first abort escape arrived */
if (sc
->sc_abortcount
== 1) sc
->sc_starttime
= time
.tv_sec
;
/* if we have an abort, see that we have not run out of time, or
that we have an "idle" time after the complete escape sequence */
if (time
.tv_sec
>= sc
->sc_starttime
+ ABT_RECYCLE
)
if (sc
->sc_abortcount
>= ABT_SOFT
&& time
.tv_sec
>= sc
->sc_lasttime
+ ABT_WAIT
)
sc
->sc_flags
|= SC_ABORT
;
if (sc
->sc_ilen
&& time
.tv_sec
>= sc
->sc_lasttime
+ TIME_WINDOW
) {
sc
->sc_flags
&= ~SC_ESCAPED
;
sc
->sc_lasttime
= time
.tv_sec
;
if (sc
->sc_flags
& SC_ESCAPED
) {
sc
->sc_flags
&= ~SC_ESCAPED
;
if (sc
->sc_ilen
== 0) /* ignore */
m
= sl_btom(sc
, sc
->sc_ilen
, &sc
->sc_if
);
{ u_char type
= *mtod(m
, u_char
*);
if (!(m
= sl_uncompress_tcp(m
, type
&0xf0, &sc
->sc_comp
)))
if (IF_QFULL(&ipintrq
)) {
sc
->sc_flags
|= SC_ESCAPED
;
if (++sc
->sc_ilen
> SLMTU
) {
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
int s
= splimp(), error
= 0;
if (ifa
->ifa_addr
->sa_family
== AF_INET
)
if (ifa
->ifa_addr
->sa_family
!= AF_INET
)