ppp_generic.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,701 行 · 第 1/5 页

C
2,701
字号
/* * Generic PPP layer for Linux. * * Copyright 1999-2002 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 20020217== */#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/filter.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 <linux/rwsem.h>#include <linux/stddef.h>#include <linux/device.h>#include <net/slhc_vj.h>#include <asm/atomic.h>#define PPP_VERSION	"2.4.2"/* * 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 NP_MPLS_UC 4		/* MPLS unicast */#define NP_MPLS_MC 5		/* MPLS multicast */#define NUM_NP	6		/* 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 */	int		index;		/* interface unit / channel number */	int		dead;		/* unit/channel has been shut down */};#define PF_TO_X(pf, X)		((X *)((char *)(pf) - offsetof(X, 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 0 */	struct file	*owner;		/* file that owns this unit 48 */	struct list_head channels;	/* list of attached channels 4c */	int		n_channels;	/* how many channels are attached 54 */	spinlock_t	rlock;		/* lock for receive side 58 */	spinlock_t	wlock;		/* lock for transmit side 5c */	int		mru;		/* max receive unit 60 */	unsigned int	flags;		/* control bits 64 */	unsigned int	xstate;		/* transmit state bits 68 */	unsigned int	rstate;		/* receive state bits 6c */	int		debug;		/* debug flags 70 */	struct slcompress *vj;		/* state for VJ header compression */	enum NPmode	npmode[NUM_NP];	/* what to do with each net proto 78 */	struct sk_buff	*xmit_pending;	/* a packet ready to go out 88 */	struct compressor *xcomp;	/* transmit packet compressor 8c */	void		*xc_state;	/* its internal state 90 */	struct compressor *rcomp;	/* receive decompressor 94 */	void		*rc_state;	/* its internal state 98 */	unsigned long	last_xmit;	/* jiffies when last pkt sent 9c */	unsigned long	last_recv;	/* jiffies when last pkt rcvd a0 */	struct net_device *dev;		/* network interface device a4 */#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 */#ifdef CONFIG_PPP_FILTER	struct sock_filter *pass_filter;	/* filter for packets to pass */	struct sock_filter *active_filter;/* filter for pkts to reset idle */	unsigned pass_len, active_len;#endif /* CONFIG_PPP_FILTER */};/* * 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 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' */#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. *//* * A cardmap represents a mapping from unsigned integers to pointers, * and provides a fast "find lowest unused number" operation. * It uses a broad (32-way) tree with a bitmap at each level. * It is designed to be space-efficient for small numbers of entries * and time-efficient for large numbers of entries. */#define CARDMAP_ORDER	5#define CARDMAP_WIDTH	(1U << CARDMAP_ORDER)#define CARDMAP_MASK	(CARDMAP_WIDTH - 1)struct cardmap {	int shift;	unsigned long inuse;	struct cardmap *parent;	void *ptr[CARDMAP_WIDTH];};static void *cardmap_get(struct cardmap *map, unsigned int nr);static void cardmap_set(struct cardmap **map, unsigned int nr, void *ptr);static unsigned int cardmap_find_first_free(struct cardmap *map);static void cardmap_destroy(struct cardmap **map);/* * all_ppp_sem protects the all_ppp_units mapping. * It also ensures that finding a ppp unit in the all_ppp_units map * and updating its file.refcnt field is atomic. */static DECLARE_MUTEX(all_ppp_sem);static struct cardmap *all_ppp_units;static atomic_t ppp_unit_count = ATOMIC_INIT(0);/* * 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 LIST_HEAD(new_channels);static int last_channel_index;static atomic_t channel_count = ATOMIC_INIT(0);/* 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 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_shutdown_interface(struct ppp *ppp);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);static struct class_simple *ppp_class;/* 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;	case PPP_MPLS_UC:		return NP_MPLS_UC;	case PPP_MPLS_MC:		return NP_MPLS_MC;	}	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,	PPP_MPLS_UC,	PPP_MPLS_MC,};	/* 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;	case ETH_P_MPLS_UC:		return NP_MPLS_UC;	case ETH_P_MPLS_MC:		return NP_MPLS_MC;	}	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,	ETH_P_MPLS_UC,	ETH_P_MPLS_MC,};/* * 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 = file->private_data;	struct ppp *ppp;	if (pf != 0) {		file->private_data = NULL;		if (pf->kind == INTERFACE) {			ppp = PF_TO_PPP(pf);			if (file == ppp->owner)				ppp_shutdown_interface(ppp);		}		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;			}		}	}	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;		ret = -EAGAIN;		if (file->f_flags & O_NONBLOCK)			break;		ret = -ERESTARTSYS;		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;	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 = 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 = 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->dead)		mask |= POLLHUP;	return mask;}#ifdef CONFIG_PPP_FILTERstatic int get_filter(void __user *arg, struct sock_filter **p){	struct sock_fprog uprog;	struct sock_filter *code = NULL;	int len, err;	if (copy_from_user(&uprog, arg, sizeof(uprog)))		return -EFAULT;	if (uprog.len > BPF_MAXINSNS)		return -EINVAL;	if (!uprog.len) {		*p = NULL;		return 0;	}	len = uprog.len * sizeof(struct sock_filter);	code = kmalloc(len, GFP_KERNEL);	if (code == NULL)		return -ENOMEM;	if (copy_from_user(code, uprog.filter, len)) {		kfree(code);		return -EFAULT;	}	err = sk_chk_filter(code, uprog.len);	if (err) {		kfree(code);		return err;	}	*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;

⌨️ 快捷键说明

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