linux 内核不支持ppp,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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值