ppp_generic.c

来自「linux 内核源代码」· C语言 代码 · 共 2,709 行 · 第 1/5 页

C
2,709
字号
	*p = code;	return uprog.len;}#endif /* CONFIG_PPP_FILTER */static int ppp_ioctl(struct inode *inode, struct file *file,		     unsigned int cmd, unsigned long arg){	struct ppp_file *pf = file->private_data;	struct ppp *ppp;	int err = -EFAULT, val, val2, i;	struct ppp_idle idle;	struct npioctl npi;	int unit, cflags;	struct slcompress *vj;	void __user *argp = (void __user *)arg;	int __user *p = argp;	if (!pf)		return ppp_unattached_ioctl(pf, file, cmd, arg);	if (cmd == PPPIOCDETACH) {		/*		 * We have to be careful here... if the file descriptor		 * has been dup'd, we could have another process in the		 * middle of a poll using the same file *, so we had		 * better not free the interface data structures -		 * instead we fail the ioctl.  Even in this case, we		 * shut down the interface if we are the owner of it.		 * Actually, we should get rid of PPPIOCDETACH, userland		 * (i.e. pppd) could achieve the same effect by closing		 * this fd and reopening /dev/ppp.		 */		err = -EINVAL;		if (pf->kind == INTERFACE) {			ppp = PF_TO_PPP(pf);			if (file == ppp->owner)				ppp_shutdown_interface(ppp);		}		if (atomic_read(&file->f_count) <= 2) {			ppp_release(inode, file);			err = 0;		} else			printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n",			       atomic_read(&file->f_count));		return err;	}	if (pf->kind == CHANNEL) {		struct channel *pch = PF_TO_CHANNEL(pf);		struct ppp_channel *chan;		switch (cmd) {		case PPPIOCCONNECT:			if (get_user(unit, p))				break;			err = ppp_connect_channel(pch, unit);			break;		case PPPIOCDISCONN:			err = ppp_disconnect_channel(pch);			break;		default:			down_read(&pch->chan_sem);			chan = pch->chan;			err = -ENOTTY;			if (chan && chan->ops->ioctl)				err = chan->ops->ioctl(chan, cmd, arg);			up_read(&pch->chan_sem);		}		return err;	}	if (pf->kind != INTERFACE) {		/* can't happen */		printk(KERN_ERR "PPP: not interface or channel??\n");		return -EINVAL;	}	ppp = PF_TO_PPP(pf);	switch (cmd) {	case PPPIOCSMRU:		if (get_user(val, p))			break;		ppp->mru = val;		err = 0;		break;	case PPPIOCSFLAGS:		if (get_user(val, p))			break;		ppp_lock(ppp);		cflags = ppp->flags & ~val;		ppp->flags = val & SC_FLAG_BITS;		ppp_unlock(ppp);		if (cflags & SC_CCP_OPEN)			ppp_ccp_closed(ppp);		err = 0;		break;	case PPPIOCGFLAGS:		val = ppp->flags | ppp->xstate | ppp->rstate;		if (put_user(val, p))			break;		err = 0;		break;	case PPPIOCSCOMPRESS:		err = ppp_set_compress(ppp, arg);		break;	case PPPIOCGUNIT:		if (put_user(ppp->file.index, p))			break;		err = 0;		break;	case PPPIOCSDEBUG:		if (get_user(val, p))			break;		ppp->debug = val;		err = 0;		break;	case PPPIOCGDEBUG:		if (put_user(ppp->debug, p))			break;		err = 0;		break;	case PPPIOCGIDLE:		idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ;		idle.recv_idle = (jiffies - ppp->last_recv) / HZ;		if (copy_to_user(argp, &idle, sizeof(idle)))			break;		err = 0;		break;	case PPPIOCSMAXCID:		if (get_user(val, p))			break;		val2 = 15;		if ((val >> 16) != 0) {			val2 = val >> 16;			val &= 0xffff;		}		vj = slhc_init(val2+1, val+1);		if (!vj) {			printk(KERN_ERR "PPP: no memory (VJ compressor)\n");			err = -ENOMEM;			break;		}		ppp_lock(ppp);		if (ppp->vj)			slhc_free(ppp->vj);		ppp->vj = vj;		ppp_unlock(ppp);		err = 0;		break;	case PPPIOCGNPMODE:	case PPPIOCSNPMODE:		if (copy_from_user(&npi, argp, sizeof(npi)))			break;		err = proto_to_npindex(npi.protocol);		if (err < 0)			break;		i = err;		if (cmd == PPPIOCGNPMODE) {			err = -EFAULT;			npi.mode = ppp->npmode[i];			if (copy_to_user(argp, &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_FILTER	case PPPIOCSPASS:	{		struct sock_filter *code;		err = get_filter(argp, &code);		if (err >= 0) {			ppp_lock(ppp);			kfree(ppp->pass_filter);			ppp->pass_filter = code;			ppp->pass_len = err;			ppp_unlock(ppp);			err = 0;		}		break;	}	case PPPIOCSACTIVE:	{		struct sock_filter *code;		err = get_filter(argp, &code);		if (err >= 0) {			ppp_lock(ppp);			kfree(ppp->active_filter);			ppp->active_filter = code;			ppp->active_len = err;			ppp_unlock(ppp);			err = 0;		}		break;	}#endif /* CONFIG_PPP_FILTER */#ifdef CONFIG_PPP_MULTILINK	case PPPIOCSMRRU:		if (get_user(val, p))			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;	int __user *p = (int __user *)arg;	switch (cmd) {	case PPPIOCNEWUNIT:		/* Create a new ppp unit */		if (get_user(unit, p))			break;		ppp = ppp_create_interface(unit, &err);		if (!ppp)			break;		file->private_data = &ppp->file;		ppp->owner = file;		err = -EFAULT;		if (put_user(ppp->file.index, p))			break;		err = 0;		break;	case PPPIOCATTACH:		/* Attach to an existing ppp unit */		if (get_user(unit, p))			break;		mutex_lock(&all_ppp_mutex);		err = -ENXIO;		ppp = ppp_find_unit(unit);		if (ppp) {			atomic_inc(&ppp->file.refcnt);			file->private_data = &ppp->file;			err = 0;		}		mutex_unlock(&all_ppp_mutex);		break;	case PPPIOCATTCHAN:		if (get_user(unit, p))			break;		spin_lock_bh(&all_channels_lock);		err = -ENXIO;		chan = ppp_find_channel(unit);		if (chan) {			atomic_inc(&chan->file.refcnt);			file->private_data = &chan->file;			err = 0;		}		spin_unlock_bh(&all_channels_lock);		break;	default:		err = -ENOTTY;	}	return err;}static const 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/* Called at boot time if ppp is compiled into the kernel,   or at module load time (from init_module) if compiled as a module. */static int __init ppp_init(void){	int err;	printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");	err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);	if (!err) {		ppp_class = class_create(THIS_MODULE, "ppp");		if (IS_ERR(ppp_class)) {			err = PTR_ERR(ppp_class);			goto out_chrdev;		}		device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), "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;}/* * 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_cow_head(skb, PPP_HDRLEN))		goto outf;	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 __user *addr = (void __user *) 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)			ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c);		if (ppp->rc_state)			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;}static void ppp_setup(struct net_device *dev){	dev->hard_header_len = PPP_HDRLEN;	dev->mtu = PPP_MTU;	dev->addr_len = 0;	dev->tx_queue_len = 3;	dev->type = ARPHRD_PPP;	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;}/* * 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);	if (ppp->dev) {		ppp_push(ppp);		while (!ppp->xmit_pending		       && (skb = skb_dequeue(&ppp->file.xq)))			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 && !skb_peek(&ppp->file.xq))			netif_wake_queue(ppp->dev);	}	ppp_xmit_unlock(ppp);}static inline struct sk_buff *pad_compress_skb(struct ppp *ppp, struct sk_buff *skb){	struct sk_buff *new_skb;	int len;	int new_skb_size = ppp->dev->mtu +		ppp->xcomp->comp_extra + ppp->dev->hard_header_len;	int compressor_skb_size = ppp->dev->mtu +		ppp->xcomp->comp_extra + PPP_HDRLEN;	new_skb = alloc_skb(new_skb_size, GFP_ATOMIC);	if (!new_skb) {		if (net_ratelimit())			printk(KERN_ERR "PPP: no memory (comp pkt)\n");		return NULL;	}	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,				   compressor_skb_size);	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 if (len == 0) {		/* didn't compress, or CCP not up yet */		kfree_skb(new_skb);		new_skb = skb;	} else {		/*		 * (len < 0)		 * MPPE requires that we do not send unencrypted		 * frames.  The compressor will return -1 if we		 * should drop the frame.  We cannot simply test		 * the compress_proto because MPPE and MPPC share		 * the same number.		 */		if (net_ratelimit())			printk(KERN_ERR "ppp: compressor dropped pkt\n");		kfree_skb(skb);		kfree_skb(new_skb);		new_skb = NULL;	}	return new_skb;}/* * 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;	if (proto < 0x8000) {#ifdef CONFIG_PPP_FILTER		/* check if we should pass this packet */		/* the filter instructions are constructed assuming		   a four-byte PPP header on each packet */		*skb_push(skb, 2) = 1;		if (ppp->pass_filter		    && sk_run_filter(skb, ppp->pass_filter,				     ppp->pass_len) == 0) {			if (ppp->debug & 1)				printk(KERN_DEBUG "PPP: outbound frame not passed\n");			kfree_skb(skb);			return;

⌨️ 快捷键说明

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