7.2 Slip Implementation in the Linux Kernel


Before we describe the SLIP implementation in the Linux kernel, we will first discuss the concept of TTY devices and TTY line disciplines, the better to illustrate how SLIP is implemented in Linux. Subsequently, this section will give an overview of the most important functions of the SLIP implementation before we describe the steps involved in implementing SLIP in detail.

7.2.1 TTY Devices and Line Disciplines

In Linux, all devices that can act as terminals are grouped under the collective term TTY (Teletype or Terminal Type). Table 7-2 shows several examples. A TTY device is a character device offering special functions to control a terminal. This includes, for example, the flag for whether the terminal should produce an echo and commands to position the cursor and change color.

Table 7-2. Examples of TTY devices.

/dev Entry


tty0 tty7

virtual consoles


pseudo-terminals e.g., xterm window


serial interfaces

A TTY device can generally switch between different TTY line disciplines. This means that, in the Linux kernel, each system call to read (read()), write (write()), or control (ioctl()) invokes a routine specific to this line discipline. More specifically, the implementation of a TTY line discipline is inserted between the TTY device driver (low-level driver), which is in charge for the actual input and output, and the user process that wants to access the TTY device. (See Figure 7-3.)

Figure 7-3. Interplay between TTY line disciplines and TTY device drivers.


One possible use for a TTY line discipline is the automatic conversion of all line ends between UNIX and Windows computers (LF versus CR/LF). In addition, TTY line disciplines offer an elegant means whereas serial interfaces can intercept and change all data transmitted over a serial interface without the need for the TTY line discipline driver to open and close the serial interface or to establish a modem dialup connection.

To register a new TTY line discipline with the Linux kernel, the driver has to first create a tty_ldisc structure (declared in <include/linux/tty_ldisc.h>) and set the function pointers contained in it. Subsequently, this data structure is registered by tty_register_ldisc() with the kernel to make the new TTY line discipline available for user programs.

struct tty_ldisc


The tty_ldisc structure includes a number of function pointers, which have to be set by the driver of the TTY line discipline. The following code briefly explains the most important function pointers.

 struct tty_ldisc {         int     magic;         char    *name;         int     num;         int     flags;         /*          * The following routines are called from above.          */         int     (*open)(struct tty_struct *);         void    (*close)(struct tty_struct *);         void    (*flush_buffer)(struct tty_struct *tty);         ssize_t (*chars_in_buffer)(struct tty_struct *tty);         ssize_t (*read)(struct tty_struct * tty, struct file * file,                          unsigned char *buf, size_t nr);         ssize_t (*write)(struct tty_struct * tty, struct file * file,                           const unsigned char * buf, size_t nr);         int     (*ioctl)(struct tty_struct * tty, struct file * file,                           unsigned int cmd, unsigned long arg);         void    (*set_termios)(struct tty_struct *tty, struct termios * old);         unsigned int (*poll)(struct tty_struct *, struct file *,                               struct poll_table_struct *);         /*          * The following routines are called from below.          */         void    (*receive_buf)(struct tty_struct *, const unsigned char *cp,                                 char *fp, int count);         int     (*receive_room)(struct tty_struct *);         void    (*write_wakeup)(struct tty_struct *); }; 

The following functions are called from "above" (i.e., by the program or module accessing the TTY device):

  • The function open() is called as soon as the TTY device switches to this line discipline.

  • The function close() is called when the current TTY line discipline is deactivated. This happens when a TTY device switches from this line discipline into another one (where the device is first reset to the standard line discipline N_TTX by the Linux kernel) and when the TTY device itself is closed.

  • The function read() is called when a program wants to read data from the TTY device.

  • The function write() is called when a program wants to send data to the TTY device.

  • The function ioctl() is called when a program uses the system call ioctl() to change the configuration of the TTY line discipline or of the actual TTY device, but only provided that the higher-layer generic driver for TTY devices was unable to process the ioctl() call (as is the case, for example, when the device switches to another TTY line discipline).

The following functions are called from "below" (i.e., from the actual device driver of the TTY device):

  • The function receive_buf() is called when the device driver has received data and wants to forward this data to the higher-layer program (i.e., to the driver of the TTY line discipline in this case). The parameters passed include the address and length of data.

  • The function receive_room() is called by the device driver to request the maximum number of bytes that the TTY line discipline can accept with receive_buf().

  • The function write_wakeup() optionally can be called by the device driver as soon as it has finished sending a data block and is ready to accept more data. However, this happens only provided that it has been explicitly requested by the flag TTY_DO_WRITE_WAKEUP <linux/tty.h>.

7.2.2 General Procedure

The lifetime of a SLIP connection under Linux consists of the following phases: the sections below will explain how they are implemented in the Linux kernel:

  1. Initialize the SLIP driver: the driver is initialized either when the system boots or when the driver module slip.o is loaded. At the same time, it registers the new TTY line discipline SLIP.

  2. Establish the connection: A user program (e.g., dip or slattach) uses a modem connected to a serial interface (e.g.,/dev/ttyS0) to dial to an Internet provider, and then switches the TTY line discipline of this serial interface to SLIP. At the same time, the SLIP operating mode (e.g., CSLIP or SLIP6) has to be set correctly.

  3. Activate and configure the network device: Once the TTY line discipline has been switched to SLIP, a new network device is available, and the name of this device begins with "sl" (e.g., sl0). ifconfig can then be used to activat and configure this network device (e.g., by assigning valid IP addresses to both ends of the SLIP connection).

  4. Exchange data: As soon as the network device has been configured correctly, the SLIP connection is available for sending IP packets.

  5. Deactivate the network device: Before the SLIP connection is torn down, the network device has to be deactivated by ifconfig.

  6. Tear down the connection: The user program (dip or slattach) separates the underlying modem connection, which causes the network device to be deregistered.

  7. Deinitialize the SLIP driver: As soon as the driver module slip.o has been removed from the Linux kernel, it frees its memory and undoes the registration of the SLIP TTY line discipline.

7.2.3 Functions and Data Structures

The files drivers/net/slip.c and drivers/net/slip.h contain the source code for the SLIP implementation in the Linux kernel. Compression of the IP packet headers by the Van-Jacobson method (CSLIP) is implemented in drivers/net/slhc.c. However, this implementation will not be discussed in detail in what follows.

struct slip


  • The SLIP driver represents each SLIP connection by a slip structure. This structure includes pointers to the net_device structure of the relevant SLIP network device and to the tty_struct structure of the underlying TTY device. (See Figure 7-4.) The slip structure includes buffer pointers and counters to send and receive data. In addition, it stores the SLIP mode (e.g., CSLIP or SLIP6). In total, the slip structure consists of the following fields:

    Figure 7-4. Important data structures of the SLIP implementation.


  • tty points to a structure of the type tty_struct, which represents the TTY device allocated to this SLIP channel. This structure also includes a tty_ldisc structure with the TTY line discipline currently active (in this case, naturally, SLIP).

  • dev points to the net_device structure with the data of the network device allocated to the SLIP connection.

  • mtu is the MTU (Maximum Transmission Unit) of the SLIP connection, which is additionally stored in the net_device structure.

  • mode specifies the active SLIP operating mode (e.g., SL_MODE_SLIP, SL_MODE_CSLIP, or SL_MODE_SLIP6).

  • rbuff points to the receive buffer, which is used to buffer data incoming over the TTY device.

  • rcount is the number of data bytes currently present in the receive buffer.

  • xbuff points to the transmit buffer, which buffers data ready to be output over the TTY device.

  • xhead points to the first character in the transmit buffer that has yet to be sent.

  • xleft specifies the number of bytes still waiting in the transmit buffer (from xhead).

The SLIP functions can be divided into three categories: general management functions, functions to implement the SLIP TTY line discipline, and functions to implement SLIP network devices. The functions used to implement network devices can be recognized by the prefix "sl_"; the other functions have the prefix "slip_". Figure 7-5 gives an overview of the functions discussed below. The following section also explains their interplay with other parts of the Linux kernel.

Figure 7-5. Functions of the SLIP implementation and their integration in the Linux kernel.




This function is called by init_module() when the SLIP module is loaded, and it uses the function tty_register_ldisc() to register the SLIP TTY line discipline with the Linux kernel.



This function is invoked by the function pointer open() in the tty_ldisc structure as soon as a user program wants to switch a TTY device to the SLIP line discipline. It reserves memory for the transmit and receive buffers and registers the new network device for the SLIP connection with the Linux kernel.



This function is invoked by the Linux kernel whenever a new network device is registered, and it does a complete initialization of the network device and the appropriate net_device structure.



This function is invoked by the function pointer open() of the net_device structure as soon as the SLIP network device is activated (e.g., by the command ifconfig up).



This function is invoked by the function pointer stop() of the net_device structure as soon as the SLIP network device is deactivated (e.g., by the command ifconfig down).



This function is invoked by the function pointer close() of the tty_ldisc structure whenever the underlying TTY device is switched from the SLIP line discipline to another line discipline.



This function is invoked whenever the SLIP module is removed. Among other things, it ensures that all buffers are freed and all SLIP network devices are deregistered. Finally, the registration of the SLIP TTY line discipline with the Linux kernel is removed by calling tty_register_ldisc(N_SLIP, NULL).



This function is invoked by the IP layer, which uses the function pointer hard_start_xmit() of the net_device structure to output an IP packet over the SLIP network device. The actual work is delegated to the function sl_encaps().



This function is invoked by sl_xmit(). As explained in section 7.1.1, it converts the IP packet into a byte sequence. Depending on the SLIP operating mode (CSLIP, etc.), other functions may be called in addition (e.g., the function slip_esc()). Subsequently, sl_encaps() invokes the function write() of the underlying TTY device driver, which eventually sends the byte sequence previously created over the TTY device.



This function is invoked by sl_encaps() and does the character stuffing described in Section 7.1.1. A similar function called slip_esc6() exists for the SLIP operating mode SLIP6, which additionally divides the data into 6-bit blocks and converts it into printable characters.



This function is invoked by the underlying TTY device driver over the function pointer write_wakeup() of the tty_ldisc structure (see Section 7.2.1) as soon as the TTY device is ready to accept more data. (See also Section 7.2.7.)



This function is invoked by the underlying TTY device driver over the function pointer receive_room() of the tty_ldisc structure and simply returns the value 65536, because the SLIP implementation can process a maximum of 65536 bytes per call of slip_receive_buf().



This function is invoked by the underlying TTY device driver over the function pointer receive_buf() of the tty_ldisc structure to transfer data from the TTY device to the SLIP driver. The function slip_unesc() (slip_unesc6(), for the SLIP6 operating mode) is invoked to convert the incoming byte stream back into IP packets.



This function is invoked by slip_receive_buf(); it converts the incoming byte stream into IP packets, using the method described in Section 7.1.1. The function sl_bump() is invoked as soon as the END control character specifies that a complete IP packet was received. There is a corresponding function (slip_unesc6()) for the SLIP6 operating mode.



This function is invoked by slip_unesc() (or slip_unesc6()) as soon as a complete IP packet was reconstructed from the byte stream received. It generates a corresponding sk_buff structure and invokes the function netif_rx() to forward the packet to the IP layer.

7.2.4 Initializing the Driver and Establishing a Connection

The initialization function of the SLIP driver (slip_init_ctrl_dev()) is invoked when the SLIP driver is loaded into the Linux kernel (by the command insmod slip, by the kernel daemon, or, if the SLIP driver is permanently integrated into the Linux kernel, during booting). This initialization function registers the SLIP TTY line discipline with the Linux kernel. For this purpose, a tty_ldisc structure, sl_ldisc, is created, which contains pointers to the functions of the SLIP driver, and tty_register_ldisc (N_SLIP, &sl_ldisc) registers the new line discipline.

A modem connection is established by a user program (e.g., dip) regardless of the SLIP driver. To establish a modem connection, the dip program can call a script, which registers with the system at the other end of the line (i.e., with the PoP of the Internet provider) and also starts a SLIP implementation at that end. Subsequently, it uses the system call ioctl (tty, TCIOCSETD, N_SLIP) to switch the corresponding TTY device (e.g., /dev/ttyS0) to SLIP. Alternatively, the user program slattach can be used to switch an existing modem connection to SLIP line discipline.

The above ioctl() call causes the function tty_set_ldisc() to be invoked in the generic TTY driver; that function, in turn, invokes the routine slip_open of the SLIP driver. The latter reserves memory for a slip structure and for the transmit and receive buffers and uses the system call register_netdevice() to register a new network device, by the name of sl#, with the system kernel. A net_device structure is passed to this system call (see Section 5.1.1), and the function pointer init() in this structure points to the function sl_init().

To initialize the new network device, the Linux kernel invokes the function sl_init() immediately after the above actions. The function sl_init() initializes the net_device structure for example, by setting the function pointers remaining in the net_device structure (including pointers to the functions sl_open(), sl_close(), and sl_xmit()).

7.2.5 Activating and Deactivating a Network Device

A user can now use the command ifconfig up to activate the new network device. This activation invokes the function sl_open() in the Linux kernel. The user program passes parameters (e.g., the IP address or the MTU) during that action. Subsequently, packets can be sent over the SLIP network device or received from that device. However, to be able to actually transmit packets, we have to set an appropriate route (either automatically, by ifconfig, or by another user program).

The above steps are done in reverse order to tear down a SLIP connection. More specifically, ifconfig is used to deactivate the network device. Any route registered for this device is now deleted automatically by the Linux kernel, so that no more data can be sent over this network device. The user can then use the command ifconfig down to cause the routine sl_close() to be invoked in the Linux kernel. This routine informs the driver that the network device was deactivated, but it doesn't free the relevant data structures just yet. Subsequently, no more data can be sent or received over this SLIP device.

7.2.6 Tearing Down a Connection and Deinitializing the Driver

The SLIP TTY line discipline might need to be terminated for several reasons: First, it is possible that a user program calls the system call close() or the Linux kernel calls the function tty_hangup() to close a serial connection. In the latter case, the Linux kernel resets the line discipline of the relevant TTY device automatically to the standard value N_TTY. Second, it can happen that the TTY device is switched to another line discipline by the function tty_set_ldisc(). Each of these cases invokes the routine slip_close() in the SLIP driver. This routine does some cleanup work (e.g., it decrements the usage counter of the SLIP module).

When the SLIP module is removed (by the command rmmod slip, or automatically), then the function cleanup_module() is invoked. First of all, this function ensures that all open SLIP connections are closed by the function tty_hangup() and that all SLIP network devices are removed. Subsequently, it deregisters the SLIP TTY line discipline by calling tty_register_ldisc (N_SLIP, NULL). After that, the SLIP TTY line discipline is no longer known in the system, and user programs can no longer use it.

7.2.7 Transmitting IP Packets

To transmit an IP packet, the IP layer invokes the function sl_xmit() and passes an sk_buff structure to this function. This causes sl_encaps() to be invoked, which uses the function slip_esc() to do character stuffing and marks packet boundaries. (See Section 7.1.1.) Subsequently, the converted packet is in the transmit buffer, and the pointer xbuff in the slip structure points to this buffer.

To output data to the TTY device, sl_encaps() invokes the write() routine of the relevant device driver. This routine returns the number of bytes that can actually be transmitted in one shot. Subsequently, the xleft variable is set to the number of bytes still missing, and the xhead pointer is set to the first of these bytes.

Because the function sl_encaps() has set the flag TTY_DO_WRITE_WAKEUP, the TTY device driver invokes the function slip_write_wakeup() as soon as it has transmitted the announced number of bytes. Next, the function slip_write_wakeup() tries to transmit the remaining xleft bytes, starting from the position xhead. The write() routine of the device driver, once more, returns the number of bytes to be actually transmitted, which causes xhead and xleft to be adapted accordingly. This process is repeated until the complete IP packet has been transmitted successfully, so that xleft equals null.

7.2.8 Receiving IP Packets

As soon as data have arrived over the TTY device, the device driver invokes the function slip_receive_buf() of the SLIP driver. The maximum number of bytes passed to slip_receive_buf were previously polled (65536 for the SLIP driver) by the function slip_receive_room().

The function slip_receive_buf() invokes the function slip_unesc() for each single buffer character, to undo the character stuffing described in Section 7.1.1 and detect the boundaries of IP packets. More specifically, for a normal character, slip_unesc() writes the character passed to the receive buffer, rbuff, and increments the counter rcount. If the special character ESC is detected, then it is understood that the next character (ESC_ESC or ESC_END) has to be treated appropriately.

If the END character is found, then slip_unesc() forwards the ready packet to sl_bump() and deletes the rbuff receive buffer by resetting rcount to null. The function sl_bump() reserves memory for an sk_buff structure, copies the readily reconstructed IP packet into this structure, and calls the function netif_rx() to pass the sk_buff structure to the IP layer for further processing.


    Linux Network Architecture
    Linux Network Architecture
    ISBN: 131777203
    EAN: N/A
    Year: 2004
    Pages: 187

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net