📄 ppp_generic.c
字号:
/* * 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 + -