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 + -
显示快捷键?