PPP Generic Driver and Channel Interface¶
Paul Mackerraspaulus@samba.org
7 Feb 2002
The generic PPP driver in linux-2.4 provides an implementation of thefunctionality which is of use in any PPP implementation, including:
- the network interface unit (ppp0 etc.)
- the interface to the networking code
- PPP multilink: splitting datagrams between multiple links, andordering and combining received fragments
- the interface to pppd, via a /dev/ppp character device
- packet compression and decompression
- TCP/IP header compression and decompression
- detecting network traffic for demand dialling and for idle timeouts
- simple packet filtering
For sending and receiving PPP frames, the generic PPP driver calls onthe services of PPPchannels. A PPP channel encapsulates amechanism for transporting PPP frames from one machine to another. APPP channel implementation can be arbitrarily complex internally buthas a very simple interface with the generic PPP code: it merely hasto be able to send PPP frames, receive PPP frames, and optionallyhandle ioctl requests. Currently there are PPP channelimplementations for asynchronous serial ports, synchronous serialports, and for PPP over ethernet.
This architecture makes it possible to implement PPP multilink in anatural and straightforward way, by allowing more than one channel tobe linked to each ppp network interface unit. The generic layer isresponsible for splitting datagrams on transmit and recombining themon receive.
PPP channel API¶
See include/linux/ppp_channel.h for the declaration of the types andfunctions used to communicate between the generic PPP layer and PPPchannels.
Each channel has to provide two functions to the generic PPP layer,via the ppp_channel.ops pointer:
- start_xmit() is called by the generic layer when it has a frame tosend. The channel has the option of rejecting the frame forflow-control reasons. In this case, start_xmit() should return 0and the channel should call the ppp_output_wakeup() function at alater time when it can accept frames again, and the generic layerwill then attempt to retransmit the rejected frame(s). If the frameis accepted, the start_xmit() function should return 1.
- ioctl() provides an interface which can be used by a user-spaceprogram to control aspects of the channel’s behaviour. Thisprocedure will be called when a user-space program does an ioctlsystem call on an instance of /dev/ppp which is bound to thechannel. (Usually it would only be pppd which would do this.)
The generic PPP layer provides seven functions to channels:
- ppp_register_channel() is called when a channel has been created, tonotify the PPP generic layer of its presence. For example, settinga serial port to the PPPDISC line discipline causes the ppp_asyncchannel code to call this function.
- ppp_unregister_channel() is called when a channel is to bedestroyed. For example, the ppp_async channel code calls this whena hangup is detected on the serial port.
- ppp_output_wakeup() is called by a channel when it has previouslyrejected a call to its start_xmit function, and can now accept morepackets.
- ppp_input() is called by a channel when it has received a completePPP frame.
- ppp_input_error() is called by a channel when it has detected that aframe has been lost or dropped (for example, because of a FCS (framecheck sequence) error).
- ppp_channel_index() returns the channel index assigned by the PPPgeneric layer to this channel. The channel should provide some way(e.g. an ioctl) to transmit this back to user-space, as user-spacewill need it to attach an instance of /dev/ppp to this channel.
- ppp_unit_number() returns the unit number of the ppp networkinterface to which this channel is connected, or -1 if the channelis not connected.
Connecting a channel to the ppp generic layer is initiated from thechannel code, rather than from the generic layer. The channel isexpected to have some way for a user-level process to control itindependently of the ppp generic layer. For example, with theppp_async channel, this is provided by the file descriptor to theserial port.
Generally a user-level process will initialize the underlyingcommunications medium and prepare it to do PPP. For example, with anasync tty, this can involve setting the tty speed and modes, issuingmodem commands, and then going through some sort of dialog with theremote system to invoke PPP service there. We refer to this processasdiscovery. Then the user-level process tells the medium tobecome a PPP channel and register itself with the generic PPP layer.The channel then has to report the channel number assigned to it backto the user-level process. From that point, the PPP negotiation codein the PPP daemon (pppd) can take over and perform the PPPnegotiation, accessing the channel through the /dev/ppp interface.
At the interface to the PPP generic layer, PPP frames are stored inskbuff structures and start with the two-byte PPP protocol number.The frame doesnot include the 0xffaddress byte or the 0x03control byte that are optionally used in async PPP. Nor is thereany escaping of control characters, nor are there any FCS or framingcharacters included. That is all the responsibility of the channelcode, if it is needed for the particular medium. That is, the skbuffspresented to the start_xmit() function contain only the 2-byteprotocol number and the data, and the skbuffs presented to ppp_input()must be in the same format.
The channel must provide an instance of a ppp_channel struct torepresent the channel. The channel is free to use theprivate fieldhowever it wishes. The channel should initialize themtu andhdrlen fields before calling ppp_register_channel() and not changethem until after ppp_unregister_channel() returns. Themtu fieldrepresents the maximum size of the data part of the PPP frames, thatis, it does not include the 2-byte protocol number.
If the channel needs some headroom in the skbuffs presented to it fortransmission (i.e., some space free in the skbuff data area before thestart of the PPP frame), it should set thehdrlen field of theppp_channel struct to the amount of headroom required. The genericPPP layer will attempt to provide that much headroom but the channelshould still check if there is sufficient headroom and copy the skbuffif there isn’t.
On the input side, channels should ideally provide at least 2 bytes ofheadroom in the skbuffs presented to ppp_input(). The generic PPPcode does not require this but will be more efficient if this is done.
Buffering and flow control¶
The generic PPP layer has been designed to minimize the amount of datathat it buffers in the transmit direction. It maintains a queue oftransmit packets for the PPP unit (network interface device) plus aqueue of transmit packets for each attached channel. Normally thetransmit queue for the unit will contain at most one packet; theexceptions are when pppd sends packets by writing to /dev/ppp, andwhen the core networking code calls the generic layer’s start_xmit()function with the queue stopped, i.e. when the generic layer hascallednetif_stop_queue(), which only happens on a transmit timeout.The start_xmit function always accepts and queues the packet which itis asked to transmit.
Transmit packets are dequeued from the PPP unit transmit queue andthen subjected to TCP/IP header compression and packet compression(Deflate or BSD-Compress compression), as appropriate. After thispoint the packets can no longer be reordered, as the decompressionalgorithms rely on receiving compressed packets in the same order thatthey were generated.
If multilink is not in use, this packet is then passed to the attachedchannel’s start_xmit() function. If the channel refuses to takethe packet, the generic layer saves it for later transmission. Thegeneric layer will call the channel’s start_xmit() function againwhen the channel calls ppp_output_wakeup() or when the corenetworking code calls the generic layer’s start_xmit() functionagain. The generic layer contains no timeout and retransmissionlogic; it relies on the core networking code for that.
If multilink is in use, the generic layer divides the packet into oneor more fragments and puts a multilink header on each fragment. Itdecides how many fragments to use based on the length of the packetand the number of channels which are potentially able to accept afragment at the moment. A channel is potentially able to accept afragment if it doesn’t have any fragments currently queued up for itto transmit. The channel may still refuse a fragment; in this casethe fragment is queued up for the channel to transmit later. Thisscheme has the effect that more fragments are given to higher-bandwidth channels. It also means that under light load, the genericlayer will tend to fragment large packets across all the channels,thus reducing latency, while under heavy load, packets will tend to betransmitted as single fragments, thus reducing the overhead offragmentation.
SMP safety¶
The PPP generic layer has been designed to be SMP-safe. Locks areused around accesses to the internal data structures where necessaryto ensure their integrity. As part of this, the generic layerrequires that the channels adhere to certain requirements and in turnprovides certain guarantees to the channels. Essentially the channelsare required to provide the appropriate locking on the ppp_channelstructures that form the basis of the communication between thechannel and the generic layer. This is because the channel providesthe storage for the ppp_channel structure, and so the channel isrequired to provide the guarantee that this storage exists and isvalid at the appropriate times.
The generic layer requires these guarantees from the channel:
- The ppp_channel object must exist from the time thatppp_register_channel() is called until after the call toppp_unregister_channel() returns.
- No thread may be in a call to any of ppp_input(), ppp_input_error(),ppp_output_wakeup(), ppp_channel_index() or ppp_unit_number() for achannel at the time that ppp_unregister_channel() is called for thatchannel.
- ppp_register_channel() and ppp_unregister_channel() must be calledfrom process context, not interrupt or softirq/BH context.
- The remaining generic layer functions may be called at softirq/BHlevel but must not be called from a hardware interrupt handler.
- The generic layer may call the channel start_xmit() function atsoftirq/BH level but will not call it at interrupt level. Thus thestart_xmit() function may not block.
- The generic layer will only call the channel ioctl() function inprocess context.
The generic layer provides these guarantees to the channels:
- The generic layer will not call the start_xmit() function for achannel while any thread is already executing in that function forthat channel.
- The generic layer will not call the ioctl() function for a channelwhile any thread is already executing in that function for thatchannel.
- By the time a call to ppp_unregister_channel() returns, no threadwill be executing in a call from the generic layer to that channel’sstart_xmit() or ioctl() function, and the generic layer will notcall either of those functions subsequently.
Interface to pppd¶
The PPP generic layer exports a character device interface called/dev/ppp. This is used by pppd to control PPP interface units andchannels. Although there is only one /dev/ppp, each open instance of/dev/ppp acts independently and can be attached either to a PPP unitor a PPP channel. This is achieved using the file->private_data fieldto point to a separate object for each open instance of /dev/ppp. Inthis way an effect similar to Solaris’ clone open is obtained,allowing us to control an arbitrary number of PPP interfaces andchannels without having to fill up /dev with hundreds of device names.
When /dev/ppp is opened, a new instance is created which is initiallyunattached. Using an ioctl call, it can then be attached to anexisting unit, attached to a newly-created unit, or attached to anexisting channel. An instance attached to a unit can be used to sendand receive PPP control frames, using the read() and write() systemcalls, along with poll() if necessary. Similarly, an instanceattached to a channel can be used to send and receive PPP frames onthat channel.
In multilink terms, the unit represents the bundle, while the channelsrepresent the individual physical links. Thus, a PPP frame sent by awrite to the unit (i.e., to an instance of /dev/ppp attached to theunit) will be subject to bundle-level compression and to fragmentationacross the individual links (if multilink is in use). In contrast, aPPP frame sent by a write to the channel will be sent as-is on thatchannel, without any multilink header.
A channel is not initially attached to any unit. In this state it canbe used for PPP negotiation but not for the transfer of data packets.It can then be connected to a PPP unit with an ioctl call, whichmakes it available to send and receive data packets for that unit.
The ioctl calls which are available on an instance of /dev/ppp dependon whether it is unattached, attached to a PPP interface, or attachedto a PPP channel. The ioctl calls which are available on anunattached instance are:
- PPPIOCNEWUNIT creates a new PPP interface and makes this /dev/pppinstance the “owner” of the interface. The argument should point toan int which is the desired unit number if >= 0, or -1 to assign thelowest unused unit number. Being the owner of the interface meansthat the interface will be shut down if this instance of /dev/ppp isclosed.
- PPPIOCATTACH attaches this instance to an existing PPP interface.The argument should point to an int containing the unit number.This does not make this instance the owner of the PPP interface.
- PPPIOCATTCHAN attaches this instance to an existing PPP channel.The argument should point to an int containing the channel number.
The ioctl calls available on an instance of /dev/ppp attached to achannel are:
- PPPIOCCONNECT connects this channel to a PPP interface. Theargument should point to an int containing the interface unitnumber. It will return an EINVAL error if the channel is alreadyconnected to an interface, or ENXIO if the requested interface doesnot exist.
- PPPIOCDISCONN disconnects this channel from the PPP interface thatit is connected to. It will return an EINVAL error if the channelis not connected to an interface.
- All other ioctl commands are passed to the channel ioctl() function.
The ioctl calls that are available on an instance that is attached toan interface unit are:
PPPIOCSMRU sets the MRU (maximum receive unit) for the interface.The argument should point to an int containing the new MRU value.
PPPIOCSFLAGS sets flags which control the operation of theinterface. The argument should be a pointer to an int containingthe new flags value. The bits in the flags value that can be setare:
SC_COMP_TCP enable transmit TCP header compression SC_NO_TCP_CCID disable connection-id compression forTCP header compression SC_REJ_COMP_TCP disable receive TCP header decompression SC_CCP_OPEN Compression Control Protocol (CCP) isopen, so inspect CCP packets SC_CCP_UP CCP is up, may (de)compress packets SC_LOOP_TRAFFIC send IP traffic to pppd SC_MULTILINK enable PPP multilink fragmentation ontransmitted packets SC_MP_SHORTSEQ expect short multilink sequencenumbers on received multilink fragments SC_MP_XSHORTSEQ transmit short multilink sequence nos. The values of these flags are defined in <linux/ppp-ioctl.h>. Notethat the values of the SC_MULTILINK, SC_MP_SHORTSEQ andSC_MP_XSHORTSEQ bits are ignored if the CONFIG_PPP_MULTILINK optionis not selected.
PPPIOCGFLAGS returns the value of the status/control flags for theinterface unit. The argument should point to an int where the ioctlwill store the flags value. As well as the values listed above forPPPIOCSFLAGS, the following bits may be set in the returned value:
SC_COMP_RUN CCP compressor is running SC_DECOMP_RUN CCP decompressor is running SC_DC_ERROR CCP decompressor detected non-fatal error SC_DC_FERROR CCP decompressor detected fatal error PPPIOCSCOMPRESS sets the parameters for packet compression ordecompression. The argument should point to a ppp_option_datastructure (defined in <linux/ppp-ioctl.h>), which contains apointer/length pair which should describe a block of memorycontaining a CCP option specifying a compression method and itsparameters. The ppp_option_data struct also contains a
transmitfield. If this is 0, the ioctl will affect the receive path,otherwise the transmit path.PPPIOCGUNIT returns, in the int pointed to by the argument, the unitnumber of this interface unit.
PPPIOCSDEBUG sets the debug flags for the interface to the value inthe int pointed to by the argument. Only the least significant bitis used; if this is 1 the generic layer will print some debugmessages during its operation. This is only intended for debuggingthe generic PPP layer code; it is generally not helpful for workingout why a PPP connection is failing.
PPPIOCGDEBUG returns the debug flags for the interface in the intpointed to by the argument.
PPPIOCGIDLE returns the time, in seconds, since the last datapackets were sent and received. The argument should point to appp_idle structure (defined in <linux/ppp_defs.h>). If theCONFIG_PPP_FILTER option is enabled, the set of packets which resetthe transmit and receive idle timers is restricted to those whichpass the
activepacket filter.Two versions of this command exist, to deal with user spaceexpecting times as either 32-bit or 64-bit time_t seconds.PPPIOCSMAXCID sets the maximum connection-ID parameter (and thus thenumber of connection slots) for the TCP header compressor anddecompressor. The lower 16 bits of the int pointed to by theargument specify the maximum connection-ID for the compressor. Ifthe upper 16 bits of that int are non-zero, they specify the maximumconnection-ID for the decompressor, otherwise the decompressor’smaximum connection-ID is set to 15.
PPPIOCSNPMODE sets the network-protocol mode for a given networkprotocol. The argument should point to an npioctl struct (definedin <linux/ppp-ioctl.h>). The
protocolfield gives the PPP protocolnumber for the protocol to be affected, and themodefieldspecifies what to do with packets for that protocol:NPMODE_PASS normal operation, transmit and receive packets NPMODE_DROP silently drop packets for this protocol NPMODE_ERROR drop packets and return an error on transmit NPMODE_QUEUE queue up packets for transmit, drop receivedpackets At present NPMODE_ERROR and NPMODE_QUEUE have the same effect asNPMODE_DROP.
PPPIOCGNPMODE returns the network-protocol mode for a givenprotocol. The argument should point to an npioctl struct with the
protocolfield set to the PPP protocol number for the protocol ofinterest. On return themodefield will be set to the network-protocol mode for that protocol.PPPIOCSPASS and PPPIOCSACTIVE set the
passandactivepacketfilters. These ioctls are only available if the CONFIG_PPP_FILTERoption is selected. The argument should point to a sock_fprogstructure (defined in <linux/filter.h>) containing the compiled BPFinstructions for the filter. Packets are dropped if they fail thepassfilter; otherwise, if they fail theactivefilter they arepassed but they do not reset the transmit or receive idle timer.PPPIOCSMRRU enables or disables multilink processing for receivedpackets and sets the multilink MRRU (maximum reconstructed receiveunit). The argument should point to an int containing the new MRRUvalue. If the MRRU value is 0, processing of received multilinkfragments is disabled. This ioctl is only available if theCONFIG_PPP_MULTILINK option is selected.
Last modified: 7-feb-2002