linux2.6内核ppp分析

1 简介

ppp协议(点到点协议),在拨号网络中应用比较广泛,逐渐在替代slip协议。

ppp数据包格式为:| 协议码 | 载荷   |填充符

ppp主要有四类协议码:

   1 0x0001 - 0x3fff 网络层协议(ipv4,ipv6,ipx,appletalk)
   
   2 0x4001 - 0x7fff 无网络层协议参与的小载荷量传输(低整流量)
   
   3 0x8001 - 0xbfff 用于配置网络层的子协议(网络控制协议,如ipcp)
   
   4 0xc001 - 0xffff 用于建立ppp连接的子协议(链路层控制协议,如lcp,pap,chap)
   
2 ppp协议实现的结构

   网络应用程序
   
   ip/tcp协议栈
   
   ppp网络协议 -|
            |—— ppp内核实现
   ppp连线规程 -|
   
   调制解调器
   
   pppd 是应用进程,通过子符设备和ppp内核通信。
   
3 ppp收发数据过程

   a 发送数据 
   
       ppp 协议发送数据有两种途径:一种是网络协议栈发送 ,另一种是pppd直接发送控制协议数据
       
   b 接收数据
   
       ppp连线规程解收到数据后,根据数据类型:如果是协商协议数据,则放到子符设备队列等待pppd读取;如果是网络层数据,则调用netif_rx入网络栈。
       
4 代码分析

a 初始化ppp设备,ppp字符主设备号为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;

   /* 注册字符设备,设备名称是ppp */
   err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
   if (!err) {
       /* 创建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;
}

class_create用途是做什么的?

从linux内核2.6的某个版本之后,devfs不复存在,udev成为devfs的替代,udev是应用层的东东
加入对udev的支持很简单,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用class_device_create创建对应的设备。大致用法如下:

  struct class *myclass = class_create(THIS_MODULE, "my_device_driver");
  class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, "my_device");

这样的module被加载时,udev daemon就会自动在/dev下创建my_device设备文件

/* 字符设备ppp的file结构:
* ---------------------
* |   ....       |
*----------------------
* |   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 connected to */
   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)
{
   //分配hdlc设备对象
   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;
   
   //如果没有数据,则在该队列上等待 
   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);//获取数据
       if (skb)
           break;
           
       //队列中没有数据
       ret = 0;
       if (pf->dead)
           break;
       if (pf->kind == INTERFACE) { //网络设备
           /*
            * 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;
       
       //设置pending ,挂起当前进程
       if (signal_pending(current))
           break;
           
       //重新调度入栈
       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数据到用户空间
   if (copy_to_user(buf, skb->data, skb->len))
       goto outf;
   ret = skb->len;

outf:
   kfree_skb(skb);
out:
   return ret;
}

//发送数据
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;
   
   //分配skb
   skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
   if (skb == 0)
       goto out;
   skb_reserve(skb, pf->hdrlen);
   ret = -EFAULT;
   
   //从用户空间copy数据
   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)); //通过网络接口发送
       break;
   case CHANNEL:
       ppp_channel_push(PF_TO_CHANNEL(pf));//通过规程接口发送
       break;
   }

   ret = count;

out:
   return ret;
}

<待续>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值