The Linux Networking Architecture [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

The Linux Networking Architecture [Electronic resources] - نسخه متنی

Klaus Wehrle

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










8.3 PPP Implementation in the Linux Kernel




As mentioned before, the PPP implementation in Linux is divided into four different tasks: three kernel modules and the pppd user space daemon. During design of this division, care was taken to move as little functionality as possible into the Linux kernel. For this reason, the kernel modules are rather simple. pppd includes 13,000 lines of code (2,100 lines alone in main.c), which means that it is four times the size of the three kernel modules (ppp_generic.c, ppp_synctty.c, and ppp_async.c) together. In the following sections, we will first discuss the generic PPP driver and then the driver for the asynchronous PPP TTY line discipline. The driver for the synchronous PPP line discipline is relatively simple, so we will not discuss it here.



8.3.1 Functions and Data Structures of the Generic PPP Driver




Figure 8-4 shows the most important data structures of the generic PPP driver. There is a separate ppp structure with general management information for each PPP device. Some important entries, particularly the transmit and receive queues, xq and rq, are in a substructure of the type ppp_file. This substructure is also found in the channel structure, which is used to manage single channels in multilink PPP, which will not be discussed here, for the sake of simplicity.




Figure 8-4. Important data structures of the generic PPP driver.


Section 8.1.1) over the device /dev/ppp. For this purpose, it must first bind the device /dev/ppp to a specific PPP device by use of an ioctl() call. This binding means that a pointer to the ppp structure is entered into the field private_data of the relevant file structure.





ppp_init()



drivers/net/ppp_generic.c



This function is invoked by init_module() whenever the PPP module is loaded: It uses the function devs_register_chrdev() to register the character-oriented device /dev/ppp (see Section 8.1.2) with the Linux kernel.



ppp_cleanup()



drivers/net/ppp_generic.c



This function is invoked whenever the PPP module is removed. It frees all data structures used and deregisters the device /dev/ppp.


Section 8.1.1.) If it is a control packet, then skb_queue_tail() adds the packet to the receive queue, where it can be read by the PPP daemon over the device /dev/ppp. If it is a data packet, then the payload is packed in an sk_buff structure with the correct protocol identifier and passed to the network layer by calling netif_rx().



8.3.2 Functions and Data Structures of the Asynchronous PPP Driver




The asynchronous PPP module essentially supplies a new TTY line discipline (see Section 7.2.1), by the name of N_PPP, and representing an intermediate layer between the generic PPP driver and the driver of the underlying TTY device.


Section 7.2.3), there is a reference to the tty_struct structure of the underlying TTY device, which contains a tty_ldisc structure for the PPP TTY line discipline.




Figure 8-5. Important data structures of the asynchronous PPP driver.


[View full size image]




A ppp_channel structure (which will not be discussed in detail here) is used to reach both the ppp structure of the relevant generic PPP driver and a structure of the type ppp_channel_ops, which includes function pointers to, among others, the function ppp_async_send() described further below. Inversely, the ppp structure of the generic PPP driver can be used to reach the relevant ppp_channel structure (and thus the function pointers in ppp_channel_ops) over several detours (which will not be described here, to keep things simple). This is important for being able to pass outgoing packets to the function ppp_async_send() in case of asynchronous PPP.





ppp_async_init()



drivers/net/ppp_async.c



This function is invoked whenever the ppp_async.o module loads. It uses tty_register_ldisc() to register the PPP TTY line discipline with the Linux kernel.



ppp_async_cleanup()



drivers/net/ppp_async.c



This function is invoked whenever the ppp_async.o module is removed. It calls tty_register_ldisc(N_PPP, NULL) to deregister the PPP TTY line discipline.



ppp_async_send()



drivers/net/ppp_async.c



This function is invoked by the function ppp_push() of the generic PPP driver over the function pointer start_xmit() in the ppp_channel_ops structure (see Figure 8-5) as soon as a PPP packet is ready to be sent. It forwards the packet to the function ppp_async_push().



ppp_async_push()



drivers/net/ppp_async.c



This function is invoked by the function ppp_async_send() to transmit a PPP packet. The function uses the auxiliary function ppp_async_encode() to prepare the packet for asynchronous transmission and then sends it to the driver of the underlying TTY device by repeatedly calling tty->driver.write().





ppp_async_encode()



drivers/net/ppp_async.c



This function uses the character stuffing described in Section 7.1.1 to transmit a PPP packet over an asynchronous device.



ppp_async_input()



drivers/net/ppp_async.c



This function is invoked by ppp_asynctty_receive() as soon as new data was supplied by the underlying TTY device. As with the SLIP functionality (see Section 7.1.1), it undoes character stuffing and detects the beginning and end of PPP packets. As soon as a packet has been read completely, it is forwarded to the function process_input_packet().



process_input_packet()



drivers/net/ppp_async.c



This function is invoked by ppp_async_input() as soon as a PPP packet has been read completely. First, the packet checksum (Frame Check SequenceFCS) is checked. After a number of additional checks, ppp_input() is called eventually, to forward the packet to the generic PPP driver.



ppp_asynctty_open(), ppp_asynctty_close()



drivers/net/ppp_async.c



These functions are invoked by the function pointers open() and close() of the tty_ldisc structure as soon as a user program switches a TTY device to the PPP line discipline or resets it to another line discipline. Essentially, an asynctty structure is created in tty_asynctty_open() for the state data of the TTY line discipline, then initialized, and finally released in tty_asynctty_close().



ppp_asynctty_read(), ppp_asynctty_write()



drivers/net/ppp_async.c



These functions are invoked by the function pointers read() and write() of the tty_ldisc structure whenever a program attempts to send data to a TTY device in PPP line discipline or read from it. Its only functionality is that it returns an error message, because all inputs and outputs of the asynchronous PPP driver run over the device /dev/ppp.



ppp_asynctty_room(), ppp_asynctty_receive(),



drivers/net/ppp_async.c



ppp_asynctty_wakeup()



These functions correspond largely to the functions slip_receive_room(), slip_receive_buf(), and slip_write_wakeup() of the SLIP implementation. (See Section 7.2.3). When data arrives, then ppp_asynctty_receive() invokes the function ppp_async_input() (as described earlier).



8.3.3 Initialization




Section 7.2.1) by the name "ppp" for the asynchronous PPP driver (ppp_async.o). This TTY line discipline is an intermediate layer between the device driver of the underlying device and the ppp_async.o module, which facilitates access to all incoming data packets. Once the /dev/tty device and the PPP TTY line discipline have been registered, the first initialization phase is completed.


The second phase is initiated by the calling of pppd. After a brief test of whether its version number matches the kernel driver version, it opens the device /dev/ppp. It then obtains a file descriptor, which is required later to communicate with the generic PPP driver; at first, however, only the reference pointer USAGE_COUNT of the PPP device is incremented.


Next, a user process has to establish a physical connection (e.g., the chat program could dial the number of a dialup server). If this action was successful, then pppd uses the system call ioctl(tty_fd, TIOCSETD, N_PPP) to change the TTY line discipline to N_PPP. It then uses ioctl(ppp_dev_fd,PPPIOCNEWUNIT) to request the generic PPP driver to create a new network device and then uses ioctl(fd,_PPPIOCATTACH) to bind the new network device to the underlying TTY device.


These steps complete the establishment of the actual PPP connection; now the PPP subprotocols, such as LCP, can start authenticating the user and configure higher layers. (See Section 8.4.)



8.3.4 Transmitting IP Packets




The generic PPP driver accepts packets ready for transmission over two different routes: The network layer sends payload packets over the matching network device (pppX), and the PPP daemon sends control packets over the character-oriented device /dev/ppp.


Each data packet to be sent is passed to the function ppp_start_xmit() by the network layer in an sk_buff structure. (See Section 4.1.) This function appends a 2-byte PPP header (see Figure 8-2) to the beginning of the packet and stores the packet in the transmit queue ppp->xq. Virtually the same thing happens in ppp_write(), the function that accepts packets from pppd.


Finally, the function ppp_xmit_process() is invoked in each case. It takes packets from the transmit queue and forwards them to the function ppp_send_frame() for further processing. Depending on the setting, the packet headers might be compressed by the VanJacobson method and the deflate or BSD-Compress method might be used for payload compression. After a forwarding to the function ppp_async_send()_ of the asynchronous PPP driver (or to the corresponding function of the synchronous PPP driver), the generic PPP driver has completed its processing.



8.3.5 Detecting Frame Boundaries




Frame synchronization (framing) is implemented as a TTY line discipline in the asynchronous PPP driver (drivers/net/ppp_async.c) and follows the standard specified in [Simp94b]. Basically, this is an easily modified and streamlined HDLC (High Level Data Link Control; see [ISO93]).




Section 7.1.1 briefly explained why framing is necessary: An asynchronous TTY device (e.g., a modem connection) can process only unstructured byte streams and not full packets, so it is necessary to mark the beginning and end of a packet specially. In PPP, this is done by use of the special control character PPP_FLAG with the binary representation 01111110.


Of course, the remaining data stream should not inadvertently contain such special characters. To prevent special characters from occurring, we use character stuffing. This means that all payload bytes corresponding to a control character, such as PPP_FLAG, are prefixed by the character PPP_ESCAPE (binary 01111101). There are more control characters; see include/linux/ppp.defs.h for a complete list.


Framing and character stuffing are largely implemented in the function ppp_async_encode(). A bit vector in the field xaccm in the struct asyncppp structure is used to detect the characters that should have a PPP_ESCAPE prefix. Each of the 32 X 8 bits in this vector corresponds to one of the 256 available 8-bit characters.


The following program dump from drivers/net/ppp async.c shows how you can convert payload into a data stream:



#define PUT_BYTE(ap, buf, c, islcp)
do { if ((islcp && c < 0x20) || (ap->xaccm
[c >> 5] & (1 << (c & 0x1f)))) {*buf++ = PPP_ESCAPE;
*buf++ = C ^ 0x20; } else
*buf++ = c; } while (0)


In this code, islcp is a flag set only for special LCP commands, which have to work even when the bit vector ap->xaccm has not yet been initialized or has been wrongly initialized.


To protect against transmission errors, a 2-byte CRC checksum (Frame Check SequenceFCS) is appended to the PPP packet before the closing end character (PPP_FLAG). If the packet was fully converted into a data stream, then the driver of the underlying TTY device, which is called by tty->driver.write(), assumes the remaining work.



8.3.6 Receiving IP Packets




Receiving PPP packets over the asynchronous PPP driver works much as does sending packets, just in opposite direction: Incoming data is first sent to the function ppp_asynctty_receive() of the asynchronous PPP driver by the driver of the underlying TTY device. Then the function ppp_async_input()_searches for frame boundaries and undoes character stuffing. The function process_input_packet() tests for whether the checksum (FCS; see Figure 8-2), a decision is made about whether the packet should be passed to the network layer in an sk_buff structure or added to the receive queue ppp->rq, from which it can be read by pppd over the device /dev/ppp.


/ 187