linux2.6核心ppp分析

來源:互聯網
上載者:User

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;
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.