1 Overview
The PPP protocol (Point-to-Point Protocol) is widely used in dial-up networks and is gradually replacing the slip protocol.
PPP data packet format: | Protocol code | load | fill character
There are four types of PPP protocol codes:
1 0x0001-0x3fff Network Layer Protocol (IPv4, IPv6, IPX, appletalk)
2 0x4001-0x7fff no network layer protocol involved in small load transfer (low rectification)
3 0x8001-0 xbfff is used to configure sub-protocols (network control protocols, such as ipcp) at the network layer)
4 0xc001-0 xFFFF sub-protocols used to establish PPP connections (Link Layer Control protocols, such as LCP, pap, chap)
2. Structure of PPP protocol implementation
Network Application
IP/TCP protocol stack
PPP network protocol-|
| -- PPP kernel implementation
PPP connection procedure-|
Modem
Pppd is an application process that communicates with the PPP kernel through sub-devices.
3. PPP data sending and receiving process
A. send data
There are two ways to send data through the PPP protocol: one is to send data through the network protocol stack, and the other is to directly send control protocol data through pppd.
B receives data
After the PPP connection procedure unreceives the data, it puts the data according to the Data Type: If it is negotiation protocol data, it is placed in the sub-device queue to wait for pppd to read; if it is network layer data, it calls netif_rx into the network stack.
4. Code Analysis
A initializes the PPP device. The PPP character master device number is 108.
Static struct file_operations ppp_device_fops = {
. Owner = this_module,
. Read = ppp_read,
. Write = ppp_write,
. Poll = ppp_poll,
. IOCTL = ppp_ioctl,
. Open = ppp_open,
. Release = ppp_release
};
# Define ppp_major 108
Static int _ init ppp_init (void)
{
Int err;
/* Register the character device. The device name is PPP */
Err = register_chrdev (ppp_major, "PPP", & ppp_device_fops );
If (! Err ){
/* Create udev */
Ppp_class = class_create (this_module, "PPP ");
If (is_err (ppp_class )){
Err = ptr_err (ppp_class );
Goto out_chrdev;
}
Class_device_create (ppp_class, null, mkdev (ppp_major, 0), null, "PPP ");
}
Out:
If (ERR)
Printk (kern_err "failed to register PPP device (% d)/n", err );
Return err;
Out_chrdev:
Unregister_chrdev (ppp_major, "PPP ");
Goto out;
}
What is class_create used?
After a Linux kernel version of 2.6, devfs no longer exists. udev becomes a replacement for devfs, and udev is the application layer.
Adding support for udev is simple. In the driver initialization code, call class_create to create a class for the device and call class_device_create to create the corresponding device for each device. The general usage is as follows:
Struct class * myclass = class_create (this_module, "my_device_driver ");
Class_device_create (myclass, null, mkdev (major_num, 0), null, "my_device ");
When such a module is loaded, udev daemon will automatically create the my_device file under/dev.
/* Character device PPP file structure:
*---------------------
* |... |
*----------------------
* | F_op |
*----------------------
* |... |
*----------------------
* | Private_data | ----------> struct ppp_file
*----------------------
Struct ppp_file {
Enum {
Interface = 1, Channel
} Kind;
Struct sk_buff_head XQ;/* pppd transmit queue */
Struct sk_buff_head RQ;/* receive queue for pppd */
Wait_queue_head_t rwait;/* For poll on reading/dev/PPP */
Atomic_t refcnt;/* # refs (incl/dev/PPP attached )*/
Int hdrlen;/* space to leave for headers */
Int index;/* interface unit/Channel Number */
Int dead;/* Unit/channel has been shut down */
};
Struct PPP {
Struct ppp_file file;/* stuff for read/write/poll 0 */
Struct file * owner;/* file that owns this unit 48 */
...
};
Struct channel {
Struct ppp_file file;/* stuff for read/write/poll */
Struct list_head list;/* link in all/new_channels list */
Struct ppp_channel * Chan;/* public channel data structure */
Struct rw_semaphore chan_sem;/* protects 'chan' during Chan IOCTL */
Spinlock_t downl;/* protects 'chan', file. XQ dequeue */
Struct PPP * PPP;/* PPP unit we're re connected */
Struct list_head clist;/* link in List of channels per unit */
Rwlock_t upl;/* protects 'ppp '*/
...
};
Struct ppp_channel {
Void * private;/* channel private data */
Struct ppp_channel_ops * OPS;/* operations for this channel */
Int MTU;/* max transmit packet size */
Int hdrlen;/* Amount of headroom channel needs */
Void * PPP;/* opaque to channel */
/* The following are not used at present */
Int speed;/* Transfer Rate (Bytes/second )*/
Int latency;/* overhead time in milliseconds */
};
Static int ppp_open (struct net_device * Dev)
{
// Allocate the HDLC device object
Hdlc_device * HDLC = dev_to_hdlc (Dev );
Void * old_ioctl;
Int result;
Dev-> priv = & HDLC-> state. PPP. syncppp_ptr;
HDLC-> state. PPP. syncppp_ptr = & HDLC-> state. PPP. pppdev;
HDLC-> state. PPP. pppdev. Dev = dev;
Old_ioctl = Dev-> do_ioctl;
HDLC-> state. PPP. old_change_mtu = Dev-> change_mtu;
Sppp_attach (& HDLC-> state. PPP. pppdev );
/* Sppp_attach nukes them. We don't need syncppp's IOCTL */
Dev-> do_ioctl = old_ioctl;
HDLC-> state. PPP. pppdev. sppp. pp_flags & = ~ Pp_cisco;
Dev-> type = arphrd_ppp;
Result = sppp_open (Dev );
If (result ){
Sppp_detach (Dev );
Return result;
}
Return 0;
}
Static ssize_t ppp_read (struct file * file, char _ User * Buf,
Size_t count, loff_t * PPOs)
{
Struct ppp_file * pF = file-> private_data;
// If no data exists, wait in the queue
Declare_waitqueue (wait, current );
Ssize_t ret;
Struct sk_buff * SKB = NULL;
Ret = count;
If (pF = 0)
Return-enxio;
Add_wait_queue (& PF-> rwait, & wait );
For (;;){
Set_current_state (task_interruptible );
SKB = skb_dequeue (& PF-> rq); // obtain data
If (SKB)
Break;
// No data in the queue
Ret = 0;
If (PF-> dead)
Break;
If (PF-> kind = interface) {// Network Device
/*
* Return 0 (EOF) on an interface that has no
* Channels connected, unless it is looping
* Network traffic (demand mode ).
*/
Struct PPP * PPP = pf_to_ppp (PF );
If (PPP-> n_channels = 0
& (PPP-> flags & SC _loop_traffic) = 0)
Break;
}
Ret =-eagain;
If (file-> f_flags & o_nonblock)
Break;
Ret =-erestartsys;
// Set pending to suspend the current process
If (signal_pending (current ))
Break;
// Reschedule to stack
Schedule ();
}
Set_current_state (task_running );
Remove_wait_queue (& PF-> rwait, & wait );
If (SKB = 0)
Goto out;
Ret =-eoverflow;
If (SKB-> Len> count)
Goto outf;
Ret =-efault;
// Copy data to the user space
If (copy_to_user (BUF, SKB-> data, SKB-> Len ))
Goto outf;
Ret = SKB-> Len;
Outf:
Kfree_skb (SKB );
Out:
Return ret;
}
// Send data
Static ssize_t ppp_write (struct file * file, const char _ User * Buf,
Size_t count, loff_t * PPOs)
{
Struct ppp_file * pF = file-> private_data;
Struct sk_buff * SKB;
Ssize_t ret;
If (pF = 0)
Return-enxio;
Ret =-enomem;
// Allocate SKB
SKB = alloc_skb (count + PF-> hdrlen, gfp_kernel );
If (SKB = 0)
Goto out;
Skb_reserve (SKB, PF-> hdrlen );
Ret =-efault;
// Copy data from the user space
If (copy_from_user (skb_put (SKB, count), Buf, count )){
Kfree_skb (SKB );
Goto out;
}
Skb_queue_tail (& PF-> XQ, SKB );
Switch (PF-> kind ){
Case interface:
Ppp_xmit_process (pf_to_ppp (PF); // sent through the network interface
Break;
Case channel:
Ppp_channel_push (pf_to_channel (PF); // send it through the procedure interface
Break;
}
Ret = count;
Out:
Return ret;
}