阅读:4058回复:1
linux2.6内核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; } <待续> |
|
|
沙发#
发布于:2007-04-19 23:23
好文章
|
|