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

📄 ppp_generic.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Generic PPP layer for Linux. * * Copyright 1999-2000 Paul Mackerras. * *  This program is free software; you can redistribute it and/or *  modify it under the terms of the GNU General Public License *  as published by the Free Software Foundation; either version *  2 of the License, or (at your option) any later version. * * The generic PPP layer handles the PPP network interfaces, the * /dev/ppp device, packet and VJ compression, and multilink. * It talks to PPP `channels' via the interface defined in * include/linux/ppp_channel.h.  Channels provide the basic means for * sending and receiving PPP frames on some kind of communications * channel. * * Part of the code in this driver was inspired by the old async-only * PPP driver, written by Michael Callahan and Al Longyear, and * subsequently hacked by Paul Mackerras. * * ==FILEVERSION 20000417== */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/kmod.h>#include <linux/init.h>#include <linux/list.h>#include <linux/devfs_fs_kernel.h>#include <linux/netdevice.h>#include <linux/poll.h>#include <linux/ppp_defs.h>#include <linux/if_ppp.h>#include <linux/ppp_channel.h>#include <linux/ppp-comp.h>#include <linux/skbuff.h>#include <linux/rtnetlink.h>#include <linux/if_arp.h>#include <linux/ip.h>#include <linux/tcp.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <net/slhc_vj.h>#include <asm/atomic.h>#define PPP_VERSION	"2.4.1"/* * Network protocols we support. */#define NP_IP	0		/* Internet Protocol V4 */#define NP_IPV6	1		/* Internet Protocol V6 */#define NP_IPX	2		/* IPX protocol */#define NP_AT	3		/* Appletalk protocol */#define NUM_NP	4		/* Number of NPs. */#define MPHDRLEN	6	/* multilink protocol header length */#define MPHDRLEN_SSN	4	/* ditto with short sequence numbers */#define MIN_FRAG_SIZE	64/* * An instance of /dev/ppp can be associated with either a ppp * interface unit or a ppp channel.  In both cases, file->private_data * points to one of these. */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 */	struct list_head list;		/* link in all_* list */	int		index;		/* interface unit / channel number */};#define PF_TO_X(pf, X)	((X *)((char *)(pf)-(unsigned long)(&((X *)0)->file)))#define PF_TO_PPP(pf)		PF_TO_X(pf, struct ppp)#define PF_TO_CHANNEL(pf)	PF_TO_X(pf, struct channel)#define ROUNDUP(n, x)		(((n) + (x) - 1) / (x))/* * Data structure describing one ppp unit. * A ppp unit corresponds to a ppp network interface device * and represents a multilink bundle. * It can have 0 or more ppp channels connected to it. */struct ppp {	struct ppp_file	file;		/* stuff for read/write/poll */	struct list_head channels;	/* list of attached channels */	int		n_channels;	/* how many channels are attached */	spinlock_t	rlock;		/* lock for receive side */	spinlock_t	wlock;		/* lock for transmit side */	int		mru;		/* max receive unit */	unsigned int	flags;		/* control bits */	unsigned int	xstate;		/* transmit state bits */	unsigned int	rstate;		/* receive state bits */	int		debug;		/* debug flags */	struct slcompress *vj;		/* state for VJ header compression */	enum NPmode	npmode[NUM_NP];	/* what to do with each net proto */	struct sk_buff	*xmit_pending;	/* a packet ready to go out */	struct compressor *xcomp;	/* transmit packet compressor */	void		*xc_state;	/* its internal state */	struct compressor *rcomp;	/* receive decompressor */	void		*rc_state;	/* its internal state */	unsigned long	last_xmit;	/* jiffies when last pkt sent */	unsigned long	last_recv;	/* jiffies when last pkt rcvd */	struct net_device *dev;		/* network interface device */#ifdef CONFIG_PPP_MULTILINK	int		nxchan;		/* next channel to send something on */	u32		nxseq;		/* next sequence number to send */	int		mrru;		/* MP: max reconst. receive unit */	u32		nextseq;	/* MP: seq no of next packet */	u32		minseq;		/* MP: min of most recent seqnos */	struct sk_buff_head mrq;	/* MP: receive reconstruction queue */#endif /* CONFIG_PPP_MULTILINK */	struct net_device_stats stats;	/* statistics */};/* * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC, * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP. * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR. * Bits in xstate: SC_COMP_RUN */#define SC_FLAG_BITS	(SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \			 |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ \			 |SC_COMP_TCP|SC_REJ_COMP_TCP)/* * Private data structure for each channel. * This includes the data structure used for multilink. */struct channel {	struct ppp_file	file;		/* stuff for read/write/poll */	struct ppp_channel *chan;	/* public channel data structure */	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' and `ulist' */#ifdef CONFIG_PPP_MULTILINK	u8		avail;		/* flag used in multilink stuff */	u8		had_frag;	/* >= 1 fragments have been sent */	u32		lastseq;	/* MP: last sequence # received */#endif /* CONFIG_PPP_MULTILINK */};/* * SMP locking issues: * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels * list and the ppp.n_channels field, you need to take both locks * before you modify them. * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock -> * channel.downl. *//* * all_ppp_lock protects the all_ppp_units. * It also ensures that finding a ppp unit in the all_ppp_units list * and updating its file.refcnt field is atomic. */static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;static LIST_HEAD(all_ppp_units);/* * all_channels_lock protects all_channels and last_channel_index, * and the atomicity of find a channel and updating its file.refcnt * field. */static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED;static LIST_HEAD(all_channels);static int last_channel_index;/* Get the PPP protocol number from a skb */#define PPP_PROTO(skb)	(((skb)->data[0] << 8) + (skb)->data[1])/* We limit the length of ppp->file.rq to this (arbitrary) value */#define PPP_MAX_RQLEN	32/* * Maximum number of multilink fragments queued up. * This has to be large enough to cope with the maximum latency of * the slowest channel relative to the others.  Strictly it should * depend on the number of channels and their characteristics. */#define PPP_MP_MAX_QLEN	128/* Multilink header bits. */#define B	0x80		/* this fragment begins a packet */#define E	0x40		/* this fragment ends a packet *//* Compare multilink sequence numbers (assumed to be 32 bits wide) */#define seq_before(a, b)	((s32)((a) - (b)) < 0)#define seq_after(a, b)		((s32)((a) - (b)) > 0)/* Prototypes. */static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,			     char *buf, size_t count);static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,			      size_t count);static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,				unsigned int cmd, unsigned long arg);static void ppp_xmit_process(struct ppp *ppp);static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);static void ppp_push(struct ppp *ppp);static void ppp_channel_push(struct channel *pch);static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,			      struct channel *pch);static void ppp_receive_error(struct ppp *ppp);static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,					    struct sk_buff *skb);#ifdef CONFIG_PPP_MULTILINKstatic void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,				struct channel *pch);static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);#endif /* CONFIG_PPP_MULTILINK */static int ppp_set_compress(struct ppp *ppp, unsigned long arg);static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);static void ppp_ccp_closed(struct ppp *ppp);static struct compressor *find_compressor(int type);static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);static struct ppp *ppp_create_interface(int unit, int *retp);static void init_ppp_file(struct ppp_file *pf, int kind);static void ppp_destroy_interface(struct ppp *ppp);static struct ppp *ppp_find_unit(int unit);static struct channel *ppp_find_channel(int unit);static int ppp_connect_channel(struct channel *pch, int unit);static int ppp_disconnect_channel(struct channel *pch);static void ppp_destroy_channel(struct channel *pch);/* Translates a PPP protocol number to a NP index (NP == network protocol) */static inline int proto_to_npindex(int proto){	switch (proto) {	case PPP_IP:		return NP_IP;	case PPP_IPV6:		return NP_IPV6;	case PPP_IPX:		return NP_IPX;	case PPP_AT:		return NP_AT;	}	return -EINVAL;}/* Translates an NP index into a PPP protocol number */static const int npindex_to_proto[NUM_NP] = {	PPP_IP,	PPP_IPV6,	PPP_IPX,	PPP_AT,};	/* Translates an ethertype into an NP index */static inline int ethertype_to_npindex(int ethertype){	switch (ethertype) {	case ETH_P_IP:		return NP_IP;	case ETH_P_IPV6:		return NP_IPV6;	case ETH_P_IPX:		return NP_IPX;	case ETH_P_PPPTALK:	case ETH_P_ATALK:		return NP_AT;	}	return -1;}/* Translates an NP index into an ethertype */static const int npindex_to_ethertype[NUM_NP] = {	ETH_P_IP,	ETH_P_IPV6,	ETH_P_IPX,	ETH_P_PPPTALK,};/* * Locking shorthand. */#define ppp_xmit_lock(ppp)	spin_lock_bh(&(ppp)->wlock)#define ppp_xmit_unlock(ppp)	spin_unlock_bh(&(ppp)->wlock)#define ppp_recv_lock(ppp)	spin_lock_bh(&(ppp)->rlock)#define ppp_recv_unlock(ppp)	spin_unlock_bh(&(ppp)->rlock)#define ppp_lock(ppp)		do { ppp_xmit_lock(ppp); \				     ppp_recv_lock(ppp); } while (0)#define ppp_unlock(ppp)		do { ppp_recv_unlock(ppp); \				     ppp_xmit_unlock(ppp); } while (0)/* * /dev/ppp device routines. * The /dev/ppp device is used by pppd to control the ppp unit. * It supports the read, write, ioctl and poll functions. * Open instances of /dev/ppp can be in one of three states: * unattached, attached to a ppp unit, or attached to a ppp channel. */static int ppp_open(struct inode *inode, struct file *file){	/*	 * This could (should?) be enforced by the permissions on /dev/ppp.	 */	if (!capable(CAP_NET_ADMIN))		return -EPERM;	return 0;}static int ppp_release(struct inode *inode, struct file *file){	struct ppp_file *pf = (struct ppp_file *) file->private_data;	lock_kernel();	if (pf != 0) {		file->private_data = 0;		if (atomic_dec_and_test(&pf->refcnt)) {			switch (pf->kind) {			case INTERFACE:				ppp_destroy_interface(PF_TO_PPP(pf));				break;			case CHANNEL:				ppp_destroy_channel(PF_TO_CHANNEL(pf));				break;			}		}	}	unlock_kernel();	return 0;}static ssize_t ppp_read(struct file *file, char *buf,			size_t count, loff_t *ppos){	struct ppp_file *pf = (struct ppp_file *) file->private_data;	return ppp_file_read(pf, file, buf, count);}static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,			     char *buf, size_t count){	DECLARE_WAITQUEUE(wait, current);	ssize_t ret;	struct sk_buff *skb = 0;	ret = -ENXIO;	if (pf == 0)		goto out;		/* not currently attached */	add_wait_queue(&pf->rwait, &wait);	current->state = TASK_INTERRUPTIBLE;	for (;;) {		skb = skb_dequeue(&pf->rq);		if (skb)			break;		ret = 0;		if (pf->kind == CHANNEL && PF_TO_CHANNEL(pf)->chan == 0)			break;		ret = -EAGAIN;		if (file->f_flags & O_NONBLOCK)			break;		ret = -ERESTARTSYS;		if (signal_pending(current))			break;		schedule();	}	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;	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 *buf,			 size_t count, loff_t *ppos){	struct ppp_file *pf = (struct ppp_file *) file->private_data;	return ppp_file_write(pf, buf, count);}static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,			      size_t count){	struct sk_buff *skb;	ssize_t ret;	ret = -ENXIO;	if (pf == 0)		goto out;	ret = -ENOMEM;	skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);	if (skb == 0)		goto out;	skb_reserve(skb, pf->hdrlen);	ret = -EFAULT;	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;}/* No kernel lock - fine */static unsigned int ppp_poll(struct file *file, poll_table *wait){	struct ppp_file *pf = (struct ppp_file *) file->private_data;	unsigned int mask;	if (pf == 0)		return 0;	poll_wait(file, &pf->rwait, wait);	mask = POLLOUT | POLLWRNORM;	if (skb_peek(&pf->rq) != 0)		mask |= POLLIN | POLLRDNORM;	if (pf->kind == CHANNEL) {		struct channel *pch = PF_TO_CHANNEL(pf);		if (pch->chan == 0)			mask |= POLLHUP;	}	return mask;}static int ppp_ioctl(struct inode *inode, struct file *file,		     unsigned int cmd, unsigned long arg){	struct ppp_file *pf = (struct ppp_file *) file->private_data;	struct ppp *ppp;	int err = -EFAULT, val, val2, i;	struct ppp_idle idle;	struct npioctl npi;	int unit;	struct slcompress *vj;	if (pf == 0)		return ppp_unattached_ioctl(pf, file, cmd, arg);	if (pf->kind == CHANNEL) {		struct channel *pch = PF_TO_CHANNEL(pf);		struct ppp_channel *chan;		switch (cmd) {		case PPPIOCCONNECT:			if (get_user(unit, (int *) arg))				break;			err = ppp_connect_channel(pch, unit);			break;		case PPPIOCDISCONN:			err = ppp_disconnect_channel(pch);			break;		case PPPIOCDETACH:			file->private_data = 0;			if (atomic_dec_and_test(&pf->refcnt))				ppp_destroy_channel(pch);			err = 0;			break;		default:			spin_lock_bh(&pch->downl);			chan = pch->chan;			err = -ENOTTY;			if (chan && chan->ops->ioctl)				err = chan->ops->ioctl(chan, cmd, arg);			spin_unlock_bh(&pch->downl);		}		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 PPPIOCDETACH:		file->private_data = 0;		if (atomic_dec_and_test(&pf->refcnt))			ppp_destroy_interface(ppp);		err = 0;		break;	case PPPIOCSMRU:		if (get_user(val, (int *) arg))			break;		ppp->mru = val;		err = 0;		break;	case PPPIOCSFLAGS:		if (get_user(val, (int *) arg))			break;		ppp_lock(ppp);		if (ppp->flags & ~val & SC_CCP_OPEN)			ppp_ccp_closed(ppp);		ppp->flags = val & SC_FLAG_BITS;		ppp_unlock(ppp);		err = 0;		break;	case PPPIOCGFLAGS:		val = ppp->flags | ppp->xstate | ppp->rstate;		if (put_user(val, (int *) arg))			break;		err = 0;		break;	case PPPIOCSCOMPRESS:		err = ppp_set_compress(ppp, arg);		break;	case PPPIOCGUNIT:		if (put_user(ppp->file.index, (int *) arg))			break;		err = 0;		break;	case PPPIOCSDEBUG:		if (get_user(val, (int *) arg))			break;		ppp->debug = val;		err = 0;		break;	case PPPIOCGDEBUG:		if (put_user(ppp->debug, (int *) arg))			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((void *) arg, &idle, sizeof(idle)))			break;		err = 0;		break;	case PPPIOCSMAXCID:		if (get_user(val, (int *) arg))			break;		val2 = 15;		if ((val >> 16) != 0) {			val2 = val >> 16;			val &= 0xffff;		}		vj = slhc_init(val2+1, val+1);		if (vj == 0) {			printk(KERN_ERR "PPP: no memory (VJ compressor)\n");			err = -ENOMEM;			break;		}		ppp_lock(ppp);		if (ppp->vj != 0)			slhc_free(ppp->vj);		ppp->vj = vj;		ppp_unlock(ppp);		err = 0;		break;	case PPPIOCGNPMODE:	case PPPIOCSNPMODE:		if (copy_from_user(&npi, (void *) arg, sizeof(npi)))			break;		err = proto_to_npindex(npi.protocol);		if (err < 0)			break;		i = err;

⌨️ 快捷键说明

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