In Chapter 4 we looked at the Ethernet interface. In this chapter we describe the SLIP and loopback interfaces, as well as the ioctl
commands used to configure all network interfaces. The TCP compression algorithm used by the SLIP driver is described in Section 29.13. The loopback driver is straightforward and we discuss it here in its entirety.
Figure 5.1, which also appeared as Figure 4.2, lists the entry points to our three example drivers.
Table 5.1. Interface functions for the example drivers.
| Ethernet | SLIP | Loopback | Description |
---|---|---|---|---|
|
| initialize hardware | ||
|
|
|
| accept and queue packet for transmission |
|
| begin transmission of frame | ||
| output complete (unused) | |||
|
|
|
| handle |
|
| reset the device to a known state | ||
| watch the device for failures or collect statistics |
The files containing code for SLIP and loopback drivers are listed in Figure 5.2.
The SLIP and loopback interface structures are described in this chapter.???
sl_softc
is an array, since there can be many SLIP interfaces. loif
is not an array, since there can be only one loopback interface.
The statistics from the ifnet
structure described in Chapter 4 are also updated by the SLIP and loopback drivers. One other variable (which is not in the ifnet
structure) collects statistics; it is shown in Figure 5.4.
A SLIP interface communicates with a remote system across a standard asynchronous serial line. As with Ethernet, SLIP defines a standard way to frame IP packets as they are transmitted on the serial line. Figure 5.5 shows the encapsulation of an IP packet into a SLIP frame when the IP packet contains SLIP’s reserved characters.
Packets are separated by the SLIP END character 0xc0
. If the END character appears in the IP packet, it is prefixed with the SLIP ESC character 0xdb
and transmitted as 0xdc
instead. When the ESC character appears in the IP packet, it is prefixed with the ESC character 0xdb
and transmitted as 0xdd
.
Since there is no type field in SLIP frames (as there is with Ethernet), SLIP is suitable only for carrying IP packets.
SLIP is described in RFC 1055 [Romkey 1988], where its many weaknesses and nonstandard status are also stated. Volume 1 contains a more detailed description of SLIP encapsulation.
The Point-to-Point Protocol (PPP) was designed to address SLIP’s problems and to provide a standard method for transmitting frames across a serial link. PPP is defined in RFC 1332 [McGregor 1992] and RFC 1548 [Simpson 1993]. Net/3 does not contain an implementation of PPP, so we do not discuss it in this text. See Section 2.6 of Volume 1 for more information regarding PPP. Appendix B describes where to obtain a reference implementation of PPP.
In Net/3 the SLIP interface relies on an asynchronous serial device driver to send and receive the data. Traditionally these device drivers have been called TTYs (teletypes). The Net/3 TTY subsystem includes the notion of a line discipline that acts as a filter between the physical device and I/O system calls such as read
and write
. A line discipline implements features such as line editing, newline and carriage-return processing, tab expansion, and more. The SLIP interface appears as a line discipline to the TTY subsystem, but it does not pass incoming data to a process reading from the device and does not accept outgoing data from a process writing to the device. Instead, the SLIP interface passes incoming packets to the IP input queue and accepts outgoing packets through the if_output
function in SLIP’s ifnet
structure. The kernel identifies line disciplines by an integer constant, which for SLIP is SLIPDISC
.
Figure 5.6 shows a traditional line discipline on the left and the SLIP discipline on the right. We show the process on the right as slattach
since it is the program that initializes a SLIP interface. The details of the TTY subsystem and line disciplines are outside the scope of this text. We present only the information required to understand the workings of the SLIP code. For more information about the TTY subsystem see [Leffler et al. 1989]. Figure 5.7 lists the functions that implement the SLIP driver. The middle columns indicate whether the function implements line discipline features, network interface features, or both.
Table 5.7. The functions in the SLIP device driver.
Function | Network Interface | Line Discipline | Description |
---|---|---|---|
| • | initialize and attach | |
| • | initialize the SLIP data structures | |
| • | queue outgoing packets for transmission on associated TTY device | |
| • | process socket | |
| • | convert a device buffer to an mbuf chain | |
| • | attach | |
| • | detach | |
| • | process TTY | |
| • | • | dequeue packet and begin transmitting data on TTY device |
| • | • | process incoming byte from TTY device, queue incoming packet if an entire frame has been received |
The SLIP driver in Net/3 supports compression of TCP packet headers for better throughput. We discuss header compression in Section 29.13, so Figure 5.7 omits the functions that implement this feature.
The Net/3 SLIP interface also supports an escape sequence. When detected by the receiver, the sequence shuts down SLIP processing and returns the device to the standard line discipline. We omit this processing from our discussion.
Figure 5.8 shows the complex relationship between SLIP as a line discipline and SLIP as a network interface.
In Net/3
sc_ttyp
andt_sc
point to thetty
structure and thesl_softc[0]
structure. Instead of cluttering the figure with two arrows, we use a double-ended arrow positioned at each pointer to illustrated the two links between the structures.
Figure 5.8 contains a lot of information:
The network interface is represented by the sl_softc
structure and the TTY device by the tty
structure.
Incoming bytes are stored in the cluster (shown behind the tty
structure). When a complete SLIP frame is received, the enclosed IP packet is put on the ipintrq
by slinput
.
Outgoing packets are dequeued from if_snd
or sc_fastq
, converted to SLIP frames, and passed to the TTY device by slstart
. The TTY buffers outgoing bytes in the clist
structure. The t_oproc
function drains and transmits the bytes held in the clist
structure.
We discussed in Section 3.7 how slattach
initializes the sl_softc
structures. The interface remains initialized but inoperative until a program (usually slattach
) opens a TTY device (e.g., /dev/tty01
) and issues an ioctl
command to replace the standard line discipline with the SLIP discipline. At this point the TTY subsystem calls the line discipline’s open function (in this case slopen
), which establishes the association between a particular TTY device and a particular SLIP interface. slopen
is shown in Figure 5.9.
Table 5.9. The slopen
function.
-------------------------------------------------------------------------- if_sl.c 181 int 182 slopen(dev, tp) 183 dev_t dev; 184 struct tty *tp; 185 { 186 struct proc *p = curproc; /* XXX */ 187 struct sl_softc *sc; 188 int nsl; 189 int error; 190 if (error = suser(p->p_ucred, &p->p_acflag)) 191 return (error); 192 if (tp->t_line == SLIPDISC) 193 return (0); 194 for (nsl = NSL, sc = sl_softc; --nsl >= 0; sc++) 195 if (sc->sc_ttyp == NULL) { 196 if (slinit(sc) == 0) 197 return (ENOBUFS); 198 tp->t_sc = (caddr_t) sc; 199 sc->sc_ttyp = tp; 200 sc->sc_if.if_baudrate = tp->t_ospeed; 201 ttyflush(tp, FREAD | FWRITE); 202 return (0); 203 } 204 return (ENXIO); 205 } -------------------------------------------------------------------------- if_sl.c |
181-193
Two arguments are passed to slopen:dev
, a kernel device identifier that slopen
does not use; and tp
, a pointer to the tty
structure associated with the TTY device. First some precautions: if the process does not have superuser privileges, or if the TTY’s line discipline is set to SLIPDISC
already, slopen
returns immediately.
194-205
The for
loop searches the array of sl_softc
structures for the first unused entry, calls slinit
(Figure 5.10), joins the tty
and sl_softc
structures by t_sc
and sc_ttyp
, and copies the TTY output speed (t_ospeed
) into the SLIP interface. ttyflush
discards any pending input or output data in the TTY queues. slopen
returns ENXIO
if a SLIP interface structure is not available, or 0 if it was successful.
Table 5.10. The slinit
function.
-------------------------------------------------------------------------- if_sl.c 156 static int 157 slinit(sc) 158 struct sl_softc *sc; 159 { 160 caddr_t p; 161 if (sc->sc_ep == (u_char *) 0) { 162 MCLALLOC(p, M_WAIT); 163 if (p) 164 sc->sc_ep = (u_char *) p + SLBUFSIZE; 165 else { 166 printf("sl%d: can't allocate bufferen", sc - sl_softc); 167 sc->sc_if.if_flags &= ~IFF_UP; 168 return (0); 169 } 170 } 171 sc->sc_buf = sc->sc_ep - SLMAX; 172 sc->sc_mp = sc->sc_buf; 173 sl_compress_init(&sc->sc_comp); 174 return (1); 175 } -------------------------------------------------------------------------- if_sl.c |
Notice that the first available
sl_softc
structure is associated with the TTY device. There need not be a fixed mapping between TTY devices and SLIP interfaces if the system has more than one SLIP line. In fact, the mapping depends on the order in whichslattach
opens and closes the TTY devices.
The slinit
function shown in Figure 5.10 initializes the sl_softc
structure.
156-175
The slinit
function allocates an mbuf cluster and attaches it to the sl_softc
structure with three pointers. Incoming bytes are stored in the cluster until an entire SLIP frame has been received. sc_buf
always points to the start of the packet in the cluster, sc_mp
points to the location of the next byte to be received, and sc_ep
points to the end of the cluster. sl_compress_init
initializes the TCP header compression state for this link (Section 29.13).
In Figure 5.8 we see that sc_buf
does not point to the first byte in the cluster. slinit
leaves room for 148 bytes (BUFOFFSET
), as the incoming packet may have a compressed header that will expand to fill this space. The bytes that have already been received are shaded in the cluster. We see that sc_mp
points to the byte just after the last byte received and sc_ep
points to the end of the cluster. Figure 5.11 shows the relationships between several SLIP constants.
Table 5.11. SLIP constants.
Constant | Value | Description |
---|---|---|
| 2048 | size of an mbuf cluster |
| 2048 | maximum size of an uncompressed SLIP packet—including a BPF header |
| 16 | size of SLIP BPF header |
| 148 | maximum size of an expanded TCP/IP header plus room for a BPF header |
| 1900 | maximum size of a compressed SLIP packet stored in a cluster |
| 296 | optimal size of SLIP packet; results in minimal delay with good bulk throughput |
| 100 | maximum number of bytes to queue in TTY output queue |
|
All that remains to make the interface operational is to assign it an IP address. As with the Ethernet driver, we postpone the discussion of address assignment until Section 6.6.
The TTY device driver delivers incoming characters to the SLIP line discipline one at a time by calling slinput
. Figure 5.12 shows the slinput
function but omits the end-of-frame processing, which is discussed separately.
Table 5.12. slinput
function.
-------------------------------------------------------------------------- if_sl.c 527 void 528 slinput(c, tp) 529 int c; 530 struct tty *tp; 531 { 532 struct sl_softc *sc; 533 struct mbuf *m; 534 int len; 535 int s; 536 u_char chdr[CHDR_LEN]; 537 tk_nin++; 538 sc = (struct sl_softc *) tp->t_sc; 539 if (sc == NULL) 540 return; 541 if (c & TTY_ERRORMASK || ((tp->t_state & TS_CARR_ON) == 0 && 542 (tp->t_cflag & CLOCAL) == 0)) { 543 sc->sc_flags |= SC_ERROR; 544 return; 545 } 546 c &= TTY_CHARMASK; 547 ++sc->sc_if.if_ibytes; 548 switch (c) { 549 case TRANS_FRAME_ESCAPE: 550 if (sc->sc_escape) 551 c = FRAME_ESCAPE; 552 break; 553 case TRANS_FRAME_END: 554 if (sc->sc_escape) 555 c = FRAME_END; 556 break; 557 case FRAME_ESCAPE: 558 sc->sc_escape = 1; 559 return; 560 case FRAME_END: /* FRAME_END code (Figure 5.13) */ 636 } 637 if (sc->sc_mp < sc->sc_ep) { 638 *sc->sc_mp++ = c; 639 sc->sc_escape = 0; 640 return; 641 } 642 /* can't put lower; would miss an extra frame */ 643 sc->sc_flags |= SC_ERROR; 644 error: 645 sc->sc_if.if_ierrors++; 646 newpack: 647 sc->sc_mp = sc->sc_buf = sc->sc_ep - SLMAX; 648 sc->sc_escape = 0; 649 } -------------------------------------------------------------------------- if_sl.c |
527-545
The arguments to slinput
are c
, the next input character; and tp
, a pointer to the device’s tty
structure. The global integer tk_nin
counts the incoming characters for all TTY devices. slinput
converts tp->t_sc
to sc
, a pointer to an sl_softc
structure. If there is no interface associated with the TTY device, slinput
returns immediately.
The first argument to slinput
is an integer. In addition to the received character, c
contains control information sent from the TTY device driver in the high-order bits. If an error is indicated in c
or the modem-control lines are not enabled and should not be ignored, SC_ERROR
is set and slinput
returns. Later, when slinput
processes the END character, the frame is discarded. The CLOCAL
flag indicates that the system should treat the line as a local line (i.e., not a dialup line) and should not expect to see modem-control signals.
546-636
slinput
discards the control bits in c
by masking it with TTY_CHARMASK
, updates the count of bytes received on the interface, and jumps based on the received character:
If c
is an escaped ESC character and the previous character was an ESC, slinput
replaces c
with an ESC character.
If c
is an escaped END character and the previous character was an ESC, slinput
replaces c
with an END character.
If c
is the SLIP ESC character, sc_escape
is set and slinput
returns immediately (i.e., the ESC character is discarded).
If c
is the SLIP END character, the packet is put on the IP input queue. The processing for the SLIP frame end character is shown in Figure 5.13.
Table 5.13. slinput
function: end-of-frame processing.
-------------------------------------------------------------------------- if_sl.c 560 case FRAME_END: 561 if (sc->sc_flags & SC_ERROR) { 562 sc->sc_flags &= ~SC_ERROR; 563 goto newpack; 564 } 565 len = sc->sc_mp - sc->sc_buf; 566 if (len < 3) 567 /* less than min length packet - ignore */ 568 goto newpack; 569 if (sc->sc_bpf) { 570 /* 571 * Save the compressed header, so we 572 * can tack it on later. Note that we 573 * will end up copying garbage in some 574 * cases but this is okay. We remember 575 * where the buffer started so we can 576 * compute the new header length. 577 */ 578 bcopy(sc->sc_buf, chdr, CHDR_LEN); 579 } 580 if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) { 581 if (c & 0x80) 582 c = TYPE_COMPRESSED_TCP; 583 else if (c == TYPE_UNCOMPRESSED_TCP) 584 *sc->sc_buf &= 0x4f; /* XXX */ 585 /* 586 * We've got something that's not an IP packet. 587 * If compression is enabled, try to decompress it. 588 * Otherwise, if auto-enable compression is on and 589 * it's a reasonable packet, decompress it and then 590 * enable compression. Otherwise, drop it. 591 */ 592 if (sc->sc_if.if_flags & SC_COMPRESS) { 593 len = sl_uncompress_tcp(&sc->sc_buf, len, 594 (u_int) c, &sc->sc_comp); 595 if (len <= 0) 596 goto error; 597 } else if ((sc->sc_if.if_flags & SC_AUTOCOMP) && 598 c == TYPE_UNCOMPRESSED_TCP && len >= 40) { 599 len = sl_uncompress_tcp(&sc->sc_buf, len, 600 (u_int) c, &sc->sc_comp); 601 if (len <= 0) 602 goto error; 603 sc->sc_if.if_flags |= SC_COMPRESS; 604 } else 605 goto error; 606 } 607 if (sc->sc_bpf) { 608 /* 609 * Put the SLIP pseudo-"link header" in place. 610 * We couldn't do this any earlier since 611 * decompression probably moved the buffer 612 * pointer. Then, invoke BPF. 613 */ 614 u_char *hp = sc->sc_buf - SLIP_HDRLEN; 615 hp[SLX_DIR] = SLIPDIR_IN; 616 bcopy(chdr, &hp[SLX_CHDR], CHDR_LEN); 617 bpf_tap(sc->sc_bpf, hp, len + SLIP_HDRLEN); 618 } 619 m = sl_btom(sc, len); 620 if (m == NULL) 621 goto error; 622 sc->sc_if.if_ipackets++; 623 sc->sc_if.if_lastchange = time; 624 s = splimp(); 625 if (IF_QFULL(&ipintrq)) { 626 IF_DROP(&ipintrq); 627 sc->sc_if.if_ierrors++; 628 sc->sc_if.if_iqdrops++; 629 m_freem(m); 630 } else { 631 IF_ENQUEUE(&ipintrq, m); 632 schednetisr(NETISR_IP); 633 } 634 splx(s); 635 goto newpack; -------------------------------------------------------------------------- if_sl.c |
The common flow of control through this switch
statement is to fall through (there is no default
case). Most bytes are data and don’t match any of the four cases. Control also falls through the switch
in the first two cases.
637-649
If control falls through the switch
, the received character is part of the IP packet. The character is stored in the cluster (if there is room), the pointers are advanced, sc_escape
is cleared, and slinput
returns.
If the cluster is full, the character is discarded and slinput
sets SC_ERROR
. Control reaches error
when the cluster is full or when an error is detected in the end-of-frame processing. At newpack
the cluster pointers are reset for a new packet, sc_escape
is cleared, and slinput
returns.
Figure 5.13 shows the FRAME_END
code omitted from Figure 5.12.
560-579
slinput
discards an incoming SLIP packet immediately if SC_ERROR
was set while the packet was being received or if the packet is less than 3 bytes in length (remember that the packet may be compressed).
If the SLIP interface is tapped by BPF, slinput
saves a copy of the (possibly compressed) header in the chdr
array.
580-606
By examining the first byte of the packet, slinput
determines if it is an uncompressed IP packet, a compressed TCP segment, or an uncompressed TCP segment. The type is saved in c
and the type information is removed from the first byte of data (Section 29.13). If the packet appears to be compressed and compression is enabled, sl_uncompress_tcp
attempts to uncompress the packet. If compression is not enabled, auto-enable compression is on, and if the packet is large enough sl_uncompress_tcp
is also called. If it is a compressed TCP packet, the compression flag is set.
slinput
discards packets it does not recognize by jumping to error
. Section 29.13 discusses the header compression techniques in more detail. The cluster now contains a complete uncompressed packet.
607-618
After SLIP has decompressed the packet, the header and data are passed to BPF. Figure 5.14 shows the layout of the buffer constructed by slinput
.
The first byte of the BPF header encodes the direction of the packet, in this case incoming (SLIPDIR_IN
). The next 15 bytes contain the compressed header. The entire packet is passed to bpf_tap
.
619-635
sl_btom
converts the cluster to an mbuf chain. If the packet is small enough to fit in a single mbuf, sl_btom
copies the packet from the cluster to a newly allocated mbuf packet header; otherwise sl_btom
attaches the cluster to an mbuf and allocates a new cluster for the interface. This is faster than copying from one cluster to another. We do not show sl_btom
in this text.
Since only IP packets are transmitted on a SLIP interface, slinput
does not have to select a protocol queue (as it does in the Ethernet driver). The packet is queued on ipintrq
, an IP software interrupt is scheduled, and slinput
jumps to newpack
, where it updates the cluster packet pointers and clears sc_escape
.
While the SLIP driver increments
if_ierrors
if the packet cannot be queued onipintrq
, neither the Ethernet nor loopback drivers increment this statistic in the same situation.
Access to the IP input queue must be protected by splimp
even though slinput
is called at spltty
. Recall from Figure 1.14 that an splimp
interrupt can preempt spltty
processing.
As with all network interfaces, output processing begins when a network-level protocol calls the interface’s if_output
function. For the Ethernet driver, the function is ether_output
. For SLIP, the function is sloutput
(Figure 5.15).
Table 5.15. sloutput
function.
-------------------------------------------------------------------------- if_sl.c 259 int 260 sloutput(ifp, m, dst, rtp) 261 struct ifnet *ifp; 262 struct mbuf *m; 263 struct sockaddr *dst; 264 struct rtentry *rtp; 265 { 266 struct sl_softc *sc = &sl_softc[ifp->if_unit]; 267 struct ip *ip; 268 struct ifqueue *ifq; 269 int s; 270 /* 271 * Cannot happen (see slioctl). Someday we will extend 272 * the line protocol to support other address families. 273 */ 274 if (dst->sa_family != AF_INET) { 275 printf("sl%d: af%d not supported ", sc->sc_if.if_unit, 276 dst->sa_family); 277 m_freem(m); 278 sc->sc_if.if_noproto++; 279 return (EAFNOSUPPORT); 280 } 281 if (sc->sc_ttyp == NULL) { 282 m_freem(m); 283 return (ENETDOWN); /* sort of */ 284 } 285 if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0 && 286 (sc->sc_ttyp->t_cflag & CLOCAL) == 0) { 287 m_freem(m); 288 return (EHOSTUNREACH); 289 } 290 ifq = &sc->sc_if.if_snd; 291 ip = mtod(m, struct ip *); 292 if (sc->sc_if.if_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) { 293 m_freem(m); 294 return (ENETRESET); /* XXX ? */ 295 } 296 if (ip->ip_tos & IPTOS_LOWDELAY) 297 ifq = &sc->sc_fastq; 298 s = splimp(); 299 if (IF_QFULL(ifq)) { 300 IF_DROP(ifq); 301 m_freem(m); 302 splx(s); 303 sc->sc_if.if_oerrors++; 304 return (ENOBUFS); 305 } 306 IF_ENQUEUE(ifq, m); 307 sc->sc_if.if_lastchange = time; 308 if (sc->sc_ttyp->t_outq.c_cc == 0) 309 slstart(sc->sc_ttyp); 310 splx(s); 311 return (0); 312 } -------------------------------------------------------------------------- if_sl.c |
259-289
The four arguments to sloutput
are: ifp
, a pointer to the SLIP ifnet
structure (in this case an sl_softc
structure); m
, a pointer to the packet to be queued for output; dst
, the next-hop destination for the packet; and rtp
, a pointer to a route entry. The fourth argument is not used by sloutput
, but it is required since sloutput
must match the prototype for the if_output
function in the ifnet
structure.
sloutput
ensures that dst
is an IP address, that the interface is connected to a TTY device, and that the TTY device is operating (i.e., the carrier is on or should be ignored). An error is returned immediately if any of these tests fail.
290-291
The SLIP interface maintains two queues of outgoing packets. The standard queue, if_snd
, is selected by default.
292-295
If the outgoing packet contains an ICMP message and SC_NOICMP
is set for the interface, the packet is discarded. This prevents a SLIP link from being overwhelmed by extraneous ICMP packets (e.g., ECHO packets) sent by a malicious user (Chapter 11).
The error code ENETRESET
indicates that the packet was discarded because of a policy decision (versus a network failure). We’ll see in Chapter 11 that the error is silently discarded unless the ICMP message was generated locally, in which case an error is returned to the process that tried to send the message.
Net/2 returned a 0 in this case. To a diagnostic tool such as
ping
ortraceroute
it would appear as if the packet disappeared since the output operation would report a successful completion.In general, ICMP messages can be discarded. They are not required for correct operation, but discarding them makes troubleshooting more difficult and may lead to less than optimal routing decisions, poorer performance, and wasted network resources.
296-297
If the TOS field in the outgoing packet specifies low-delay service (IPTOS_LOWDELAY
), the output queue is changed to sc_fastq
.
RFC 1700 and RFC 1349 [Almquist 1992] specify the TOS settings for the standard protocols. Low-delay service is specified for Telnet, Rlogin, FTP (control), TFTP, SMTP (command phase), and DNS (UDP query). See Section 3.2 of Volume 1 for more details.
In previous BSD releases, the
ip_tos
was not set correctly by applications. The SLIP driver implemented TOS queueing by examining the transport headers contained within the IP packet. If it found TCP packets for the FTP (command), Telnet, or Rlogin ports, the packet was queued as ifIPTOS_LOWDELAY
was specified. Many routers continue this practice, since many implementations of these interactive services still do not setip_tos
.
298-312
The packet is now placed on the selected queue, the interface statistics are updated, and (if the TTY output queue is empty) sloutput
calls slstart
to initiate transmission of the packet.
SLIP increments
if_oerrors
if the interface queue is full;ether_output
does not.
Unlike the Ethernet output function (ether_output
), sloutput
does not construct a data-link header for the outgoing packet. Since the only other system on a SLIP network is at the other end of the serial link, there is no need for hardware addresses or a protocol, such as ARP, to convert between IP addresses and hardware addresses. Protocol identifiers (such as the Ethernet type field) are also superfluous, since a SLIP link carries only IP packets.
In addition to the call by sloutput
, the TTY device driver calls slstart
when it drains its output queue and needs more bytes to transmit. The TTY subsystem manages its queues through a clist
structure. In Figure 5.8 the output clist t_outq
is shown below slstart
and above the device’s t_oproc
function. slstart
adds bytes to the queue, while t_oproc
drains the queue and transmits the bytes.
The slstart
function is shown in Figure 5.16.
Table 5.16. slstart
function: packet dequeueing.
-------------------------------------------------------------------------- if_sl.c 318 void 319 slstart(tp) 320 struct tty *tp; 321 { 322 struct sl_softc *sc = (struct sl_softc *) tp->t_sc; 323 struct mbuf *m; 324 u_char *cp; 325 struct ip *ip; 326 int s; 327 struct mbuf *m2; 328 u_char bpfbuf[SLMTU + SLIP_HDRLEN]; 329 int len; 330 extern int cfreecount; 331 for (;;) { 332 /* 333 * If there is more in the output queue, just send it now. 334 * We are being called in lieu of ttstart and must do what 335 * it would. 336 */ 337 if (tp->t_outq.c_cc != 0) { 338 (*tp->t_oproc) (tp); 339 if (tp->t_outq.c_cc > SLIP_HIWAT) 340 return; 341 } 342 /* 343 * This happens briefly when the line shuts down. 344 */ 345 if (sc == NULL) 346 return; 347 /* 348 * Get a packet and send it to the interface. 349 */ 350 s = splimp(); 351 IF_DEQUEUE(&sc->sc_fastq, m); 352 if (m) 353 sc->sc_if.if_omcasts++; /* XXX */ 354 else 355 IF_DEQUEUE(&sc->sc_if.if_snd, m); 356 splx(s); 357 if (m == NULL) 358 return; 359 /* 360 * We do the header compression here rather than in sloutput 361 * because the packets will be out of order if we are using TOS 362 * queueing, and the connection id compression will get 363 * munged when this happens. 364 */ 365 if (sc->sc_bpf) { 366 /* 367 * We need to save the TCP/IP header before it's 368 * compressed. To avoid complicated code, we just 369 * copy the entire packet into a stack buffer (since 370 * this is a serial line, packets should be short 371 * and/or the copy should be negligible cost compared 372 * to the packet transmission time). 373 */ 374 struct mbuf *m1 = m; 375 u_char *cp = bpfbuf + SLIP_HDRLEN; 376 len = 0; 377 do { 378 int mlen = m1->m_len; 379 bcopy(mtod(m1, caddr_t), cp, mlen); 380 cp += mlen; 381 len += mlen; 382 } while (m1 = m1->m_next); 383 } 384 if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) { 385 if (sc->sc_if.if_flags & SC_COMPRESS) 386 *mtod(m, u_char *) |= sl_compress_tcp(m, ip, 387 &sc->sc_comp, 1); 388 } 389 if (sc->sc_bpf) { 390 /* 391 * Put the SLIP pseudo-"link header" in place. The 392 * compressed header is now at the beginning of the 393 * mbuf. 394 */ 395 bpfbuf[SLX_DIR] = SLIPDIR_OUT; 396 bcopy(mtod(m, caddr_t), &bpfbuf[SLX_CHDR], CHDR_LEN); 397 bpf_tap(sc->sc_bpf, bpfbuf, len + SLIP_HDRLEN); 398 } /* packet output code */ 483 } 484 } -------------------------------------------------------------------------- if_sl.c |
318-358
When slstart
is called, tp
points to the device’s tty
structure. The body of slstart
consists of a single for
loop. If the output queue t_outq
is not empty, slstart
calls the output function for the device, t_oproc
, which transmits as many bytes as the device will accept. If more than 100 bytes (SLIP_HIWAT
) remain in the TTY output queue, slstart
returns instead of adding another packet’s worth of bytes to the queue. The output device generates an interrupt when it has transmitted all the bytes, and the TTY subsystem calls slstart
when the output list is empty.
If the TTY output queue is empty, a packet is dequeued from sc_fastq
or, if sc_fastq
is empty, from the if_snd
queue, thus transmitting all interactive packets before any other packets.
There are no standard SNMP variables to count packets queued according to the TOS fields. The
XXX
comment in line 353 indicates that the SLIP driver is counting low-delay packets inif_omcasts
, not multicast packets.
359-383
If the SLIP interface is tapped by BPF, slstart
makes a copy of the output packet before any header compression occurs. The copy is saved on the stack in the bpfbuf
array.
384-388
If compression is enabled and the packet contains a TCP segment, sloutput
calls sl_compress_tcp
, which attempts to compress the packet. The resulting packet type is returned and logically ORed with the first byte in IP header (Section 29.13).
389-398
The compressed header is now copied into the BPF header, and the direction recorded as SLIPDIR_OUT
. The completed BPF packet is passed to bpf_tap
.
483-484
slstart
returns if the for
loop terminates.
The next section of slstart
(Figure 5.17) discards packets if the system is low on memory, and implements a simple technique for discarding data generated by noise on the serial line. This is the code omitted from Figure 5.16.
Table 5.17. slstart
function: resource shortages and line noise.
-------------------------------------------------------------------------- if_sl.c 399 sc->sc_if.if_lastchange = time; 400 /* 401 * If system is getting low on clists, just flush our 402 * output queue (if the stuff was important, it'll get 403 * retransmitted). 404 */ 405 if (cfreecount < CLISTRESERVE + SLMTU) { 406 m_freem(m); 407 sc->sc_if.if_collisions++; 408 continue; 409 } 410 /* 411 * The extra FRAME_END will start up a new packet, and thus 412 * will flush any accumulated garbage. We do this whenever 413 * the line may have been idle for some time. 414 */ 415 if (tp->t_outq.c_cc == 0) { 416 ++sc->sc_if.if_obytes; 417 (void) putc(FRAME_END, &tp->t_outq); 418 } -------------------------------------------------------------------------- if_sl.c |
399-409
If the system is low on clist structures, the packet is discarded and counted as a collision. By continuing the loop instead of returning, slstart
quickly discards all remaining packets queued for output. Each iteration discards a packet, since the device still has too many bytes queued for output. Higher-level protocols must detect the lost packets and retransmit them.
410-418
If the TTY output queue is empty, the communication line may have been idle for a period of time and the receiver at the other end may have received extraneous data created by line noise. slstart
places an extra SLIP END character in the output queue. A 0-length frame or a frame created by noise on the line should be discarded by the SLIP interface or IP protocol at the receiver.
Figure 5.18 illustrates this technique for discarding line noise and is attributed to Phil Karn in RFC 1055. In Figure 5.18, the second end-of-frame (END) is transmitted because the line was idle for a period of time. The invalid frame created by the noise and the END byte is discarded by the receiving system.
In Figure 5.19 there is no noise on the line and the 0-length frame is discarded by the receiving system.
The next section of slstart
(Figure 5.20) transfers the data from an mbuf to the output queue for the TTY device.
Table 5.20. s1start
function: packet transmission.
-------------------------------------------------------------------------- if_sl.c 419 while (m) { 420 u_char *ep; 421 cp = mtod(m, u_char *); 422 ep = cp + m->m_len; 423 while (cp < ep) { 424 /* 425 * Find out how many bytes in the string we can 426 * handle without doing something special. 427 */ 428 u_char *bp = cp; 429 while (cp < ep) { 430 switch (*cp++) { 431 case FRAME_ESCAPE: 432 case FRAME_END: 433 --cp; 434 goto out; 435 } 436 } 437 out: 438 if (cp > bp) { 439 /* 440 * Put n characters at once 441 * into the tty output queue. 442 */ 443 if (b_to_q((char *) bp, cp - bp, 444 &tp->t_outq)) 445 break; 446 sc->sc_if.if_obytes += cp - bp; 447 } 448 /* 449 * If there are characters left in the mbuf, 450 * the first one must be special.. 451 * Put it out in a different form. 452 */ 453 if (cp < ep) { 454 if (putc(FRAME_ESCAPE, &tp->t_outq)) 455 break; 456 if (putc(*cp++ == FRAME_ESCAPE ? 457 TRANS_FRAME_ESCAPE : TRANS_FRAME_END, 458 &tp->t_outq)) { 459 (void) unputc(&tp->t_outq); 460 break; 461 } 462 sc->sc_if.if_obytes += 2; 463 } 464 } 465 MFREE(m, m2); 466 m = m2; 467 } -------------------------------------------------------------------------- if_sl.c |
419-467
The outer while
loop in this section is executed once for each mbuf in the chain. The middle while
loop transfers the data from each mbuf to the output device. The inner while
loop advances cp
until it finds an END or ESC character. b_to_q
transfers the bytes between bp
and cp
. END and ESC characters are escaped and queued with two calls to putc
. This middle loop is repeated until all the bytes in the mbuf are passed to the TTY device’s output queue. Figure 5.21 illustrates this process with an mbuf containing a SLIP END character and a SLIP ESC character.
bp
marks the beginning of the first section of the mbuf to transfer with b_to_q
, and cp
marks the end of the first section. ep
marks the end of the data in the mbuf.
If b_to_q
or putc
fail (i.e., data cannot be queued on the TTY device), the break
causes slstart
to fall out of the middle while
loop. The failure indicates that the kernel has run out of clist resources. After each mbuf is copied to the TTY device, or when an error occurs, the mbuf is released, m
is advanced to the next mbuf in the chain, and the outer while
loop continues until all the mbufs in the chain have been processed.
Figure 5.22 shows the processing done by slstart
to complete the outgoing frame.
Table 5.22. slstart
function: end-of-frame processing.
--------------------------------------------------------------------------- if_sl.c 468 if (putc(FRAME_END, &tp->t_outq)) { 469 /* 470 * Not enough room. Remove a char to make room 471 * and end the packet normally. 472 * If you get many collisions (more than one or two 473 * a day) you probably do not have enough clists 474 * and you should increase "nclist" in param.c. 475 */ 476 (void) unputc(&tp->t_outq); 477 (void) putc(FRAME_END, &tp->t_outq); 478 sc->sc_if.if_collisions++; 479 } else { 480 ++sc->sc_if.if_obytes; 481 sc->sc_if.if_opackets++; 482 } --------------------------------------------------------------------------- if_sl.c |
468-482
Control reaches this code when the outer while
loop has finished queueing the bytes on the output queue. The driver sends a SLIP END character, which terminates the frame.
If an error occurred while queueing the bytes, the outgoing frame is invalid and is detected by the receiving system because of an invalid checksum or length.
Whether or not the frame is terminated because of an error, if the END character does not fit on the output queue, the last character on the queue is discarded and slstart
ends the frame. This guarantees that an END character is transmitted. The invalid frame is discarded at the destination.
The SLIP interface provides a good example of a best-effort service. SLIP discards packets if the TTY is overloaded; it truncates packets if resources are unavailable after the packet transmission has started, and it inserts extraneous null packets to detect and discard line noise. In each of these cases, no error message is generated. SLIP depends on IP and the transport layers to detect damaged and missing packets.
On a router forwarding packets from a fast interface such as Ethernet to a low-speed SLIP line, a large percentage of packets are discarded if the sender does not recognize the bottleneck and respond by throttling back the data rate. In Section 25.11 we’ll see how TCP detects and responds to this condition. Applications using a protocol without flow control, such as UDP, must recognize and respond to this condition on their own (Exercise 5.8).
The MTU of a SLIP frame (SLMTU
), the clist high-water mark (SLIP_HIWAT
), and SLIP’s TOS queueing strategies are all designed to minimize the delay inherent in a slow serial link for interactive traffic.
A small MTU improves the delay for interactive data (such as keystrokes and echoes), but hurts the throughput for bulk data transfer. A large MTU improves bulk data throughput, but increases interactive delays. Another problem with SLIP links is that a single typed character is burdened with 40 bytes of TCP and IP header information, which increases the communication delay.
The solution is to pick an MTU large enough to provide good interactive response time and decent bulk data throughput, and to compress TCP/IP headers to reduce the per-packet overhead. RFC 1144 [Jacobson 1990a] describes a compression scheme and the timing calculations that result in selecting an MTU of 296 for a typical 9600 bits/sec asynchronous SLIP link. We describe Compressed SLIP (CSLIP) in Section 29.13. Sections 2.10 and 7.2 of Volume 1 summarize the timing considerations and illustrate the delay on SLIP links.
If too many bytes are buffered in the clist (because SLIP_HIWAT
is set too high), the TOS queueing will be thwarted as new interactive traffic waits behind the large amount of buffered data. If SLIP passes 1 byte at a time to the TTY driver (because SLIP_HIWAT
is set too low), the device calls slstart
for each byte and the line is idle for a brief period of time after each byte is transferred. Setting SLIP_HIWAT
to 100 minimizes the amount of data queued at the device and reduces the frequency at which the TTY subsystem must call slstart
to approximately once every 100 characters.
As described, the SLIP driver provides TOS queueing by transmitting interactive traffic from the sc_fastq
queue before other traffic on the standard interface queue, if_snd
.
For completeness, we show the slclose
function, which is called when the slattach
program closes SLIP’s TTY device and terminates the connection to the remote system.???
Table 5.23. slclose
function.
-------------------------------------------------------------------------- if_sl.c 210 void 211 slclose(tp) 212 struct tty *tp; 213 { 214 struct sl_softc *sc; 215 int s; 216 ttywflush(tp); 217 s = splimp(); /* actually, max(spltty, splnet) */ 218 tp->t_line = 0; 219 sc = (struct sl_softc *) tp->t_sc; 220 if (sc != NULL) { 221 if_down(&sc->sc_if); 222 sc->sc_ttyp = NULL; 223 tp->t_sc = NULL; 224 MCLFREE((caddr_t) (sc->sc_ep - SLBUFSIZE)); 225 sc->sc_ep = 0; 226 sc->sc_mp = 0; 227 sc->sc_buf = 0; 228 } 229 splx(s); 230 } -------------------------------------------------------------------------- if_sl.c |
210-230
tp
points to the TTY device to be closed. slclose
flushes any remaining data out to the serial device, blocks TTY and network processing, and resets the TTY to the default line discipline. If the TTY device is attached to a SLIP interface, the interface is shut down, the links between the two structures are severed, the mbuf cluster associated with the interface is released, and the pointers into the now-discarded cluster are reset. Finally, splx
reenables the TTY and network interrupts.
Recall that a SLIP interface has two roles to play in the kernel:
as a network interface, and
as a TTY line discipline.
Figure 5.7 indicated that slioctl
processes ioctl
commands issued for a SLIP interface through a socket descriptor. In Section 4.4 we showed how ifioctl
calls slioctl
. We’ll see a similar pattern for ioctl
commands that we cover in later chapters.
Figure 5.7 also indicated that sltioctl
processes ioctl
commands issued for the TTY device associated with a SLIP network interface. The one command recognized by sltioctl
is shown in Figure 5.24.
The sltioctl
function is shown in Figure 5.25.
Table 5.25. sltioctl
function.
-------------------------------------------------------------------------- if_sl.c 236 int 237 sltioctl(tp, cmd, data, flag) 238 struct tty *tp; 239 int cmd; 240 caddr_t data; 241 int flag; 242 { 243 struct sl_softc *sc = (struct sl_softc *) tp->t_sc; 244 switch (cmd) { 245 case SLIOCGUNIT: 246 *(int *) data = sc->sc_if.if_unit; 247 break; 248 default: 249 return (-1); 250 } 251 return (0); 252 } -------------------------------------------------------------------------- if_sl.c |
236-252
The t_sc
pointer in the tty
structure points to the associated sl_softc
structure. The unit number of the SLIP interface is copied from if_unit
to *data
, which is eventually returned to the process (Section 17.5).
if_unit
is initialized by slattach
when the system is initialized, and t_sc
is initialized by slopen
when the slattach
program selects the SLIP line discipline for the TTY device. Since the mapping between a TTY device and a SLIP sl_softc
structure is established at run time, a process can discover the interface structure selected by the SLIOCGUNIT
command.
Any packets sent to the loopback interface (Figure 5.26) are immediately queued for input. The interface is implemented entirely in software.
looutput
, the if_output
function for the loopback interface, places outgoing packets on the input queue for the protocol specified by the packet’s destination address.
We already saw that ether_output
may call looutput
to queue a copy of an outgoing broadcast packet when the device has set IFF_SIMPLEX
. In Chapter 12, we’ll see that multicast packets may be also be looped back in this way. looutput
is shown in Figure 5.27.
Table 5.27. The looutput
function.
-------------------------------------------------------------------------- if_loop.c 57 int 58 looutput(ifp, m, dst, rt) 59 struct ifnet *ifp; 60 struct mbuf *m; 61 struct sockaddr *dst; 62 struct rtentry *rt; 63 { 64 int s, isr; 65 struct ifqueue *ifq = 0; 66 if ((m->m_flags & M_PKTHDR) == 0) 67 panic("looutput no HDR"); 68 ifp->if_lastchange = time; 69 if (loif.if_bpf) { 70 /* 71 * We need to prepend the address family as 72 * a four byte field. Cons up a dummy header 73 * to pacify bpf. This is safe because bpf 74 * will only read from the mbuf (i.e., it won't 75 * try to free it or keep a pointer a to it). 76 */ 77 struct mbuf m0; 78 u_int af = dst->sa_family; 79 m0.m_next = m; 80 m0.m_len = 4; 81 m0.m_data = (char *) ⁡ 82 bpf_mtap(loif.if_bpf, &m0); 83 } 84 m->m_pkthdr.rcvif = ifp; 85 if (rt && rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE)) { 86 m_freem(m); 87 return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 88 rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 89 } 90 ifp->if_opackets++; 91 ifp->if_obytes += m->m_pkthdr.len; 92 switch (dst->sa_family) { 93 case AF_INET: 94 ifq = &ipintrq; 95 isr = NETISR_IP; 96 break; 97 case AF_ISO: 98 ifq = &clnlintrq; 99 isr = NETISR_ISO; 100 break; 101 default: 102 printf("lo%d: can't handle af%d ", ifp->if_unit, 103 dst->sa_family); 104 m_freem(m); 105 return (EAFNOSUPPORT); 106 } 107 s = splimp(); 108 if (IF_QFULL(ifq)) { 109 IF_DROP(ifq); 110 m_freem(m); 111 splx(s); 112 return (ENOBUFS); 113 } 114 IF_ENQUEUE(ifq, m); 115 schednetisr(isr); 116 ifp->if_ipackets++; 117 ifp->if_ibytes += m->m_pkthdr.len; 118 splx(s); 119 return (0); 120 } -------------------------------------------------------------------------- if_loop.c |
57-68
The arguments to looutput
are the same as those to ether_output
since both are called indirectly through the if_output
pointer in their ifnet
structures: ifp
, a pointer to the outgoing interface’s ifnet
structure; m
, the packet to send; dst
, the destination address of the packet; and rt
, routing information. If the first mbuf on the chain does not contain a packet, looutput
calls panic
.
Figure 5.28 shows the logical layout for a BPF loopback packet.
69-83
The driver constructs the BPF loopback packet header in m0
on the stack and connects m0
to the mbuf chain containing the original packet. Note the unusual declaration of m0
. It is an mbuf, not a pointer to an mbuf. m_data
in m0
points to af
, which is also allocated on the stack. Figure 5.29 shows this arrangement.
looutput
copies the destination’s address family into af
and passes the new mbuf chain to bpf_mtap
, which processes the packet. Contrast this to bpf_tap
, which accepts the packet in a single contiguous buffer not in an mbuf chain. As the comment indicates, BPF never releases mbufs in a chain, so it is safe to pass m0
(which points to an mbuf on the stack) to bpf_mtap
.
84-89
The remainder of looutput
contains input processing for the packet. Even though this is an output function, the packet is being looped back to appear as input. First, m->m_pkthdr.rcvif
is set to point to the receiving interface. If the caller provided a routing entry, looutput
checks to see if it indicates that the packet should be rejected (RTF_REJECT)
or silently discarded (RTF_BLACKHOLE)
. A black hole is implemented by discarding the mbuf and returning 0. It appears to the caller as if the packet has been transmitted. To reject a packet, looutput
returns EHOSTUNREACH
if the route is for a host and ENETUNREACH
if the route is for a network.
The various
RTF_
xxx flags are described in Figure 18.25.
90-120
looutput
then selects the appropriate protocol input queue and software interrupt by examining sa_family
in the packet’s destination address. It then queues recognized packets and schedules a software interrupt with schednetisr
.
We described the two remaining interfaces to which we refer throughout the text: sl0
, a SLIP interface, and lo0
, the standard loopback interface.
We showed the relationship between the SLIP interface and the SLIP line discipline, described the SLIP encapsulation method, and discussed TOS processing for interactive traffic and other performance considerations for the SLIP driver.
We showed how the loopback interface demultiplexes outgoing packets based on their destination address family and places the packet on the appropriate input queue.
5.1 | Why does the loopback interface not have an input function? |
5.1 | The loopback interface does not need an input function because all its packets are received directly from |
5.2 | Why do you think |
5.2 | The stack allocation is faster than dynamic memory allocation. Performance is important for BPF processing, since the code is executed for each incoming packet. |
5.3 | Perform an analysis of SLIP characteristics for a 19,200 bps serial line. Should the SLIP MTU be changed for this line? |
5.4 | Derive a formula to select a SLIP MTU based on the speed of the serial line. |
5.5 | What happens if a packet is too large to fit in SLIP’S input buffer? |
5.5 | The first character that overflows the buffer is discarded, |
5.6 | An earlier version of |
5.6 | IP discards the packet when the checksum is found to be invalid or when it notices that the length in the IP header does not match the physical packet size. |
5.7 | In Figure 4.31 |
5.7 | Since sc = (struct le_softc *)ifp; initializes |
5.8 | How can a UDP application recognize when its packets are being discarded because of a bottleneck in the network? |
5.8 | This is very hard to do. Some routers may send ICMP source quench messages when they begin discarding packets but Net/3 discards these messages for UDP sockets (Figure 23.30). An application would have to begin using the same techniques used by TCP: estimation of the available bandwidth and delay on roundtrip times for acknowledged datagrams. |
3.135.216.174