⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ppp_generic.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
		if (cmd == PPPIOCGNPMODE) {			err = -EFAULT;			npi.mode = ppp->npmode[i];			if (copy_to_user((void *) arg, &npi, sizeof(npi)))				break;		} else {			ppp->npmode[i] = npi.mode;			/* we may be able to transmit more packets now (??) */			netif_wake_queue(ppp->dev);		}		err = 0;		break;#ifdef CONFIG_PPP_MULTILINK	case PPPIOCSMRRU:		if (get_user(val, (int *) arg))			break;		ppp_recv_lock(ppp);		ppp->mrru = val;		ppp_recv_unlock(ppp);		err = 0;		break;#endif /* CONFIG_PPP_MULTILINK */	default:		err = -ENOTTY;	}	return err;}static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,				unsigned int cmd, unsigned long arg){	int unit, err = -EFAULT;	struct ppp *ppp;	struct channel *chan;	switch (cmd) {	case PPPIOCNEWUNIT:		/* Create a new ppp unit */		if (get_user(unit, (int *) arg))			break;		ppp = ppp_create_interface(unit, &err);		if (ppp == 0)			break;		file->private_data = &ppp->file;		err = -EFAULT;		if (put_user(ppp->file.index, (int *) arg))			break;		err = 0;		break;	case PPPIOCATTACH:		/* Attach to an existing ppp unit */		if (get_user(unit, (int *) arg))			break;		spin_lock(&all_ppp_lock);		ppp = ppp_find_unit(unit);		if (ppp != 0)			atomic_inc(&ppp->file.refcnt);		spin_unlock(&all_ppp_lock);		err = -ENXIO;		if (ppp == 0)			break;		file->private_data = &ppp->file;		err = 0;		break;	case PPPIOCATTCHAN:		if (get_user(unit, (int *) arg))			break;		spin_lock_bh(&all_channels_lock);		chan = ppp_find_channel(unit);		if (chan != 0)			atomic_inc(&chan->file.refcnt);		spin_unlock_bh(&all_channels_lock);		err = -ENXIO;		if (chan == 0)			break;		file->private_data = &chan->file;		err = 0;		break;	default:		err = -ENOTTY;	}	return err;}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	108static devfs_handle_t devfs_handle;/* Called at boot time if ppp is compiled into the kernel,   or at module load time (from init_module) if compiled as a module. */int __init ppp_init(void){	int err;	printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");	err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);	if (err)		printk(KERN_ERR "failed to register PPP device (%d)\n", err);	devfs_handle = devfs_register(NULL, "ppp", DEVFS_FL_DEFAULT,				      PPP_MAJOR, 0,				      S_IFCHR | S_IRUSR | S_IWUSR,				      &ppp_device_fops, NULL);	return 0;}/* * Network interface unit routines. */static intppp_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct ppp *ppp = (struct ppp *) dev->priv;	int npi, proto;	unsigned char *pp;	npi = ethertype_to_npindex(ntohs(skb->protocol));	if (npi < 0)		goto outf;	/* Drop, accept or reject the packet */	switch (ppp->npmode[npi]) {	case NPMODE_PASS:		break;	case NPMODE_QUEUE:		/* it would be nice to have a way to tell the network		   system to queue this one up for later. */		goto outf;	case NPMODE_DROP:	case NPMODE_ERROR:		goto outf;	}	/* Put the 2-byte PPP protocol number on the front,	   making sure there is room for the address and control fields. */	if (skb_headroom(skb) < PPP_HDRLEN) {		struct sk_buff *ns;		ns = alloc_skb(skb->len + dev->hard_header_len, GFP_ATOMIC);		if (ns == 0)			goto outf;		skb_reserve(ns, dev->hard_header_len);		memcpy(skb_put(ns, skb->len), skb->data, skb->len);		kfree_skb(skb);		skb = ns;	}	pp = skb_push(skb, 2);	proto = npindex_to_proto[npi];	pp[0] = proto >> 8;	pp[1] = proto;	netif_stop_queue(dev);	skb_queue_tail(&ppp->file.xq, skb);	ppp_xmit_process(ppp);	return 0; outf:	kfree_skb(skb);	++ppp->stats.tx_dropped;	return 0;}static struct net_device_stats *ppp_net_stats(struct net_device *dev){	struct ppp *ppp = (struct ppp *) dev->priv;	return &ppp->stats;}static intppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){	struct ppp *ppp = dev->priv;	int err = -EFAULT;	void *addr = (void *) ifr->ifr_ifru.ifru_data;	struct ppp_stats stats;	struct ppp_comp_stats cstats;	char *vers;	switch (cmd) {	case SIOCGPPPSTATS:		ppp_get_stats(ppp, &stats);		if (copy_to_user(addr, &stats, sizeof(stats)))			break;		err = 0;		break;	case SIOCGPPPCSTATS:		memset(&cstats, 0, sizeof(cstats));		if (ppp->xc_state != 0)			ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c);		if (ppp->rc_state != 0)			ppp->rcomp->decomp_stat(ppp->rc_state, &cstats.d);		if (copy_to_user(addr, &cstats, sizeof(cstats)))			break;		err = 0;		break;	case SIOCGPPPVER:		vers = PPP_VERSION;		if (copy_to_user(addr, vers, strlen(vers) + 1))			break;		err = 0;		break;	default:		err = -EINVAL;	}	return err;}intppp_net_init(struct net_device *dev){	dev->hard_header_len = PPP_HDRLEN;	dev->mtu = PPP_MTU;	dev->hard_start_xmit = ppp_start_xmit;	dev->get_stats = ppp_net_stats;	dev->do_ioctl = ppp_net_ioctl;	dev->addr_len = 0;	dev->tx_queue_len = 3;	dev->type = ARPHRD_PPP;	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;	dev_init_buffers(dev);	return 0;}/* * Transmit-side routines. *//* * Called to do any work queued up on the transmit side * that can now be done. */static voidppp_xmit_process(struct ppp *ppp){	struct sk_buff *skb;	ppp_xmit_lock(ppp);	ppp_push(ppp);	while (ppp->xmit_pending == 0	       && (skb = skb_dequeue(&ppp->file.xq)) != 0)		ppp_send_frame(ppp, skb);	/* If there's no work left to do, tell the core net	   code that we can accept some more. */	if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0	    && ppp->dev != 0)		netif_wake_queue(ppp->dev);	ppp_xmit_unlock(ppp);}/* * Compress and send a frame. * The caller should have locked the xmit path, * and xmit_pending should be 0. */static voidppp_send_frame(struct ppp *ppp, struct sk_buff *skb){	int proto = PPP_PROTO(skb);	struct sk_buff *new_skb;	int len;	unsigned char *cp;	++ppp->stats.tx_packets;	ppp->stats.tx_bytes += skb->len - 2;	switch (proto) {	case PPP_IP:		if (ppp->vj == 0 || (ppp->flags & SC_COMP_TCP) == 0)			break;		/* try to do VJ TCP header compression */		new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2,				    GFP_ATOMIC);		if (new_skb == 0) {			printk(KERN_ERR "PPP: no memory (VJ comp pkt)\n");			goto drop;		}		skb_reserve(new_skb, ppp->dev->hard_header_len - 2);		cp = skb->data + 2;		len = slhc_compress(ppp->vj, cp, skb->len - 2,				    new_skb->data + 2, &cp,				    !(ppp->flags & SC_NO_TCP_CCID));		if (cp == skb->data + 2) {			/* didn't compress */			kfree_skb(new_skb);		} else {			if (cp[0] & SL_TYPE_COMPRESSED_TCP) {				proto = PPP_VJC_COMP;				cp[0] &= ~SL_TYPE_COMPRESSED_TCP;			} else {				proto = PPP_VJC_UNCOMP;				cp[0] = skb->data[2];			}			kfree_skb(skb);			skb = new_skb;			cp = skb_put(skb, len + 2);			cp[0] = 0;			cp[1] = proto;		}		break;	case PPP_CCP:		/* peek at outbound CCP frames */		ppp_ccp_peek(ppp, skb, 0);		break;	}	/* try to do packet compression */	if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state != 0	    && proto != PPP_LCP && proto != PPP_CCP) {		new_skb = alloc_skb(ppp->dev->mtu + ppp->dev->hard_header_len,				    GFP_ATOMIC);		if (new_skb == 0) {			printk(KERN_ERR "PPP: no memory (comp pkt)\n");			goto drop;		}		if (ppp->dev->hard_header_len > PPP_HDRLEN)			skb_reserve(new_skb,				    ppp->dev->hard_header_len - PPP_HDRLEN);		/* compressor still expects A/C bytes in hdr */		len = ppp->xcomp->compress(ppp->xc_state, skb->data - 2,					   new_skb->data, skb->len + 2,					   ppp->dev->mtu + PPP_HDRLEN);		if (len > 0 && (ppp->flags & SC_CCP_UP)) {			kfree_skb(skb);			skb = new_skb;			skb_put(skb, len);			skb_pull(skb, 2);	/* pull off A/C bytes */		} else {			/* didn't compress, or CCP not up yet */			kfree_skb(new_skb);		}	}	/* for data packets, record the time */	if (proto < 0x8000)		ppp->last_xmit = jiffies;	/*	 * If we are waiting for traffic (demand dialling),	 * queue it up for pppd to receive.	 */	if (ppp->flags & SC_LOOP_TRAFFIC) {		if (ppp->file.rq.qlen > PPP_MAX_RQLEN)			goto drop;		skb_queue_tail(&ppp->file.rq, skb);		wake_up_interruptible(&ppp->file.rwait);		return;	}	ppp->xmit_pending = skb;	ppp_push(ppp);	return; drop:	kfree_skb(skb);	++ppp->stats.tx_errors;}/* * Try to send the frame in xmit_pending. * The caller should have the xmit path locked. */static voidppp_push(struct ppp *ppp){	struct list_head *list;	struct channel *pch;	struct sk_buff *skb = ppp->xmit_pending;	if (skb == 0)		return;	list = &ppp->channels;	if (list_empty(list)) {		/* nowhere to send the packet, just drop it */		ppp->xmit_pending = 0;		kfree_skb(skb);		return;	}	if ((ppp->flags & SC_MULTILINK) == 0) {		/* not doing multilink: send it down the first channel */		list = list->next;		pch = list_entry(list, struct channel, clist);		spin_lock_bh(&pch->downl);		if (pch->chan) {			if (pch->chan->ops->start_xmit(pch->chan, skb))				ppp->xmit_pending = 0;		} else {			/* channel got unregistered */			kfree_skb(skb);			ppp->xmit_pending = 0;		}		spin_unlock_bh(&pch->downl);		return;	}#ifdef CONFIG_PPP_MULTILINK	/* Multilink: fragment the packet over as many links	   as can take the packet at the moment. */	if (!ppp_mp_explode(ppp, skb))		return;#endif /* CONFIG_PPP_MULTILINK */	ppp->xmit_pending = 0;	kfree_skb(skb);}#ifdef CONFIG_PPP_MULTILINK/* * Divide a packet to be transmitted into fragments and * send them out the individual links. */static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb){	int nch, len, fragsize;	int i, bits, hdrlen, mtu;	int flen, fnb;	unsigned char *p, *q;	struct list_head *list;	struct channel *pch;	struct sk_buff *frag;	struct ppp_channel *chan;	nch = 0;	hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;	list = &ppp->channels;	while ((list = list->next) != &ppp->channels) {		pch = list_entry(list, struct channel, clist);		nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0);		/*		 * If a channel hasn't had a fragment yet, it has to get		 * one before we send any fragments on later channels.		 * If it can't take a fragment now, don't give any		 * to subsequent channels.		 */		if (!pch->had_frag && !pch->avail) {			while ((list = list->next) != &ppp->channels) {				pch = list_entry(list, struct channel, clist);				pch->avail = 0;			}			break;		}	}	if (nch == 0)		return 0;	/* can't take now, leave it in xmit_pending */	/* Do protocol field compression (XXX this should be optional) */	p = skb->data;	len = skb->len;	if (*p == 0) {		++p;		--len;	}	/* decide on fragment size */	fragsize = len;	if (nch > 1) {		int maxch = ROUNDUP(len, MIN_FRAG_SIZE);		if (nch > maxch)			nch = maxch;		fragsize = ROUNDUP(fragsize, nch);	}	/* skip to the channel after the one we last used	   and start at that one */	for (i = 0; i < ppp->nxchan; ++i) {		list = list->next;		if (list == &ppp->channels) {			i = 0;			break;		}	}	/* create a fragment for each channel */	bits = B;	do {		list = list->next;		if (list == &ppp->channels) {			i = 0;			continue;		}		pch = list_entry(list, struct channel, clist);		++i;		if (!pch->avail)			continue;		/* check the channel's mtu and whether it is still attached. */		spin_lock_bh(&pch->downl);		if (pch->chan == 0 || (mtu = pch->chan->mtu) < hdrlen) {			/* can't use this channel */			spin_unlock_bh(&pch->downl);			pch->avail = 0;			if (--nch == 0)				break;			continue;		}		/*		 * We have to create multiple fragments for this channel		 * if fragsize is greater than the channel's mtu.		 */		if (fragsize > len)			fragsize = len;		for (flen = fragsize; flen > 0; flen -= fnb) {			fnb = flen;			if (fnb > mtu + 2 - hdrlen)				fnb = mtu + 2 - hdrlen;			if (fnb >= len)				bits |= E;			frag = alloc_skb(fnb + hdrlen, GFP_ATOMIC);			if (frag == 0)				goto noskb;			q = skb_put(frag, fnb + hdrlen);			/* make the MP header */			q[0] = PPP_MP >> 8;			q[1] = PPP_MP;			if (ppp->flags & SC_MP_XSHORTSEQ) {				q[2] = bits + ((ppp->nxseq >> 8) & 0xf);				q[3] = ppp->nxseq;			} else {				q[2] = bits;				q[3] = ppp->nxseq >> 16;				q[4] = ppp->nxseq >> 8;				q[5] = ppp->nxseq;			}			/* copy the data in */			memcpy(q + hdrlen, p, fnb);			/* try to send it down the channel */			chan = pch->chan;			if (!chan->ops->start_xmit(chan, frag))				skb_queue_tail(&pch->file.xq, frag);			pch->had_frag = 1;			p += fnb;			len -= fnb;			++ppp->nxseq;			bits = 0;		}		spin_unlock_bh(&pch->downl);	} while (len > 0);	ppp->nxchan = i;	return 1; noskb:	spin_unlock_bh(&pch->downl);	if (ppp->debug & 1)		printk(KERN_ERR "PPP: no memory (fragment)\n");	++ppp->stats.tx_errors;	++ppp->nxseq;	return 1;	/* abandon the frame */}#endif /* CONFIG_PPP_MULTILINK *//* * Try to send data out on a channel. */static voidppp_channel_push(struct channel *pch){	struct sk_buff *skb;	struct ppp *ppp;	spin_lock_bh(&pch->downl);	if (pch->chan != 0) {		while (skb_queue_len(&pch->file.xq) > 0) {			skb = skb_dequeue(&pch->file.xq);			if (!pch->chan->ops->start_xmit(pch->chan, skb)) {				/* put the packet back and try again later */				skb_queue_head(&pch->file.xq, skb);				break;			}		}	} else {		/* channel got deregistered */		skb_queue_purge(&pch->file.xq);	}	spin_unlock_bh(&pch->downl);	/* see if there is anything from the attached unit to be sent */	if (skb_queue_len(&pch->file.xq) == 0) {		read_lock_bh(&pch->upl);		ppp = pch->ppp;		if (ppp != 0)			ppp_xmit_process(ppp);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -