As was mentioned repeatedly in previous sections, the largest part of the implementation effort takes place in the PPP daemon, pppd. One of the reasons is that it processes all subprotocols to control the PPP connection. To maintain expandability, utmost care was taken to keep the implementation highly modular, and it has a clearly defined interface for subprotocol implementations. 8.4.1 Managing Subprotocols struct protent | pppd/pppd.h |
The core of the pppd interface for subprotocols is the protent structure, which is defined in the file pppd/pppd.h. It includes mainly entries for callback functions, which are always called whenever pppd receives a packet that it allocates to this subprotocol, given the protocol ID: struct protent { u_short protocol; /* PPP protocol number */ /* Initialization procedure */ void (*init) ___P((int unit)); /* Process a received packet */ void (*input) ___P((int unit, u_char *pkt, int len)); /* Process a received protocol-reject */ void (*protrej) ___P((int unit)); /* Lower layer has come up */ void (*lowerup) ___P((int unit)); /* Lower layer has gone down */ void (*lowerdown) ___P((int unit)); /* Open the protocol */ void (*open) ___P((int unit)); /* Close the protocol */ void (*close) ___P((int unit, char *reason)); /* Print a packet in readable form */ int (*printpkt) ___P((u_char *pkt, int len, void (*printer) ___P((void *, char *, ...)), void *arg)); /* Process a received data packet */ void (*datainput) ___P((int unit, u_char *pkt, int len)); bool enabled_flag; /* 0 iff protocol is disabled */ char *name; /* Text name of protocol */ char *data_name; /* Text name of corresponding data protocol */ option_t *options; /* List of command-line options */ /* Check requested options, assign defaults */ void (*check_options) ___P((void)); /* Configure interface for demand-dial */ int (*demand_conf) ___P((int unit)); /* Say whether to bring up link for this pkt */ int (*active_pkt) ___P((u_char *pkt, int len)); }; Each of the protocols known to pppd has exactly one entry in the global list struct protent protocols[]. Figure 8-6 shows a flow diagram representing a simplified procedure of how a connection is established. The function init() is executed immediately after pppd has started. Shortly after that, the function check_options() is run to handle settings, if applicable, using command-line arguments or options in /etc/ppp/options. Figure 8-6. Procedure involved when pppd establishes a connection. The function lowerup() is invoked for each subprotocol as soon as the lower layers are active. For LCP, the lower layer is the TTY device concerned; all other sub-protocols wait for LCP in turn. Authentication per PAP or CHAP is now triggered in the function link_established() in pppd/auth.c. If the authentication can be completed successfully, then the subprotocols are informed by the function pointer open() in the protent structure, and they all can now start working. As soon as the PPP connection is closed again, all subprotocols are notified accordingly by the function pointer close(). For the authentication protocols PAP and CHAP, the value NULL each is entered for open() and close() as callback function. 8.4.2 States The protocol logic of most subprotocols can be represented elegantly in the form of a finite state machine (FSM). To save cost and avoid errors, the PPP daemon implements a generic FSM, which handles things like state transitions and timers. It is implemented in pppd/fsm.c and primarily takes care that the correct callback functions are invoked at the right time. Examples for subprotocols with an implementation that accesses this generic finite state machine include LCP and IPCP. PPP itself, and thus the PPP daemon, also know different states; however, these states have little to do with the states of subprotocols. These so-called phases are listed in Table 8-3. The PPP daemon behaves differently, depending on the state. For example, it would be fatal to admit configuration protocols for the network layer, such as IPCP, before a successful authentication. Table 8-3. States (phases) of the PPP daemon.State | Meaning |
---|
PHASE_INITIALIZE | Initial state: pppd initialization. | PHASE_DORMANT | Waiting for activity (for dial-on-demand). | PHASE_SERIALCONN | Establish physical connection. | PHASE_ESTABLISH | Physical connection is up and running. | PHASE_AUTHENTICATE | Authentication in progress. | PHASE_CALLBACK | CBCP (see Section 8.2.4) is running. | PHASE_NETWORK | Network protocols are being configured. | PHASE_RUNNING | Higher layers can start working. | PHASE_TERMINATE | LCP requested connection to be torn down. | PHASE_DISCONNECT | Program to tear down connection has started. | PHASE_HOLDOFF | Wait a little before the next connection is established. | PHASE_DEAD | Connection was interrupted. |
A callback function can be invoked upon request in each of these state transitions. To this end, the callback function need only be added to the otherwise unused global variable new_phase_hook. |