X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=linux%2Fppp.c;h=66c5e185f1306e46c3d3f492dadb538bffb4e674;hp=f0582e39bd35bf2405115990b951b63bec503669;hb=492bed62999458a3e61f9d46b929ce1e1ca8a72b;hpb=d3cb9cc0a5eda3ff79526e3c0efb5e669ebaa80b diff --git a/linux/ppp.c b/linux/ppp.c index f0582e3..66c5e18 100644 --- a/linux/ppp.c +++ b/linux/ppp.c @@ -1,8 +1,20 @@ -/* - PPP for Linux - - $Id: ppp.c,v 1.2 1994/05/30 02:42:55 paulus Exp $ -*/ +/* PPP for Linux + * + * Michael Callahan + * Al Longyear + * Extensively rewritten by Paul Mackerras + * + * ==FILEVERSION 980704== + * + * NOTE TO MAINTAINERS: + * If you modify this file at all, please set the number above to the + * date of the modification as YYMMDD (year month day). + * ppp.c is shipped with a PPP distribution as well as with the kernel; + * if everyone increases the FILEVERSION number above, then scripts + * can do the right thing when deciding whether to install a new ppp.c + * file. Don't change the format of that line otherwise, so the + * installation script can recognize it. + */ /* Sources: @@ -18,1821 +30,2624 @@ Flags for this module (any combination is acceptable for testing.): - NET02D - Define if using Net-2-Debugged in kernels earler - than v1.1.4. - - NEW_TTY_DRIVERS - Define if using new Ted Ts'o's alpha TTY drivers - from tsx-11.mit.edu. From Ted Ts'o. - - OPTIMIZE_FLAG_TIME - Number of jiffies to force sending of leading flag + OPTIMIZE_FLAG_TIME - Number of jiffies to force sending of leading flag character. This is normally set to ((HZ * 3) / 2). - This is 1.5 seconds. If not defined then the leading - flag is always sent. + This is 1.5 seconds. If zero then the leading + flag is always sent. + + CHECK_CHARACTERS - Enable the checking on all received characters for + 8 data bits, no parity. This adds a small amount of + processing for each received character. */ -/* #define NET02D /* */ -/* #define NEW_TTY_DRIVERS /* */ -#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2) /* */ +#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2) +#define CHECK_CHARACTERS 1 + +#define PPP_MAX_RCV_QLEN 32 /* max # frames we queue up for pppd */ +/* $Id: ppp.c,v 1.19 1998/07/07 04:27:37 paulus Exp $ */ + +#include +#include +#include #include #include #include #include #include #include + +/* a macro to generate linux version number codes */ +#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch)) + +#if LINUX_VERSION_CODE < VERSION(2,1,14) #include +#endif + +#if LINUX_VERSION_CODE >= VERSION(2,1,23) +#include +#endif + #include #include #include #include -#include /* to get the struct task_struct */ -#include /* used in new tty drivers */ -#include /* used in new tty drivers */ +#include /* to get the struct task_struct */ +#include /* used in new tty drivers */ +#include /* used in new tty drivers */ #include #include -#include - -#ifdef NET02D /* v1.1.4 net code and earlier */ -#include -#include -#define skb_queue_head_init(buf) *(buf) = NULL -#else /* v1.1.5 and later */ +#include +#include #include #include -#endif +#include +#include +#include +#include +#include +#include -#include +#define fcstab ppp_crc16_table /* Name of the table in the kernel */ +#include -#include -#include -#include -#include "slhc.h" +#include +#include +#include +#include -#include -#ifndef ARPHRD_PPP -#define ARPHRD_PPP 0 +#ifdef CONFIG_KMOD +#include +#endif +#ifdef CONFIG_KERNELD +#include #endif -#define PRINTK(p) printk p ; -#define ASSERT(p) if (!p) PRINTK ((KERN_CRIT "assertion failed: " # p)) -#define PRINTKN(n,p) {if (ppp_debug >= n) PRINTK (p)} -#define CHECK_PPP(a) if (!ppp->inuse) { PRINTK ((ppp_warning, __LINE__)) return a;} -#define CHECK_PPP_VOID() if (!ppp->inuse) { PRINTK ((ppp_warning, __LINE__)) return;} - -#define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f))) -#define in_rmap(ppp,c) ((((unsigned int) (unsigned char) (c)) < 0x20) && \ - ppp->recv_async_map & (1 << (c))) +#if LINUX_VERSION_CODE >= VERSION(2,1,4) -#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) +#if LINUX_VERSION_CODE >= VERSION(2,1,5) +#include +#else +#include +#endif -int ppp_debug = 2; -int ppp_debug_netpackets = 0; +#define GET_USER get_user +#define PUT_USER put_user +#define COPY_FROM_USER copy_from_user +#define COPY_TO_USER copy_to_user + +#else /* 2.0.x and 2.1.x before 2.1.4 */ + +#define GET_USER(val, src) \ + (verify_area(VERIFY_READ, src, sizeof(*src))? -EFAULT: \ + ((val) = get_user(src), 0)) +#define PUT_USER(val, dst) \ + (verify_area(VERIFY_WRITE, dst, sizeof(*dst))? -EFAULT: \ + (put_user(val, dst), 0)) +#define COPY_FROM_USER(dst, src, size) \ + (verify_area(VERIFY_READ, src, size)? -EFAULT: \ + (memcpy_fromfs(dst, src, size), 0)) +#define COPY_TO_USER(dst, src, size) \ + (verify_area(VERIFY_WRITE, dst, size)? -EFAULT: \ + (memcpy_tofs(dst, src, size), 0)) + +#endif /* < 2.1.4 */ + +#if LINUX_VERSION_CODE < VERSION(2,1,37) +#define test_and_set_bit(nr, addr) set_bit(nr, addr) +#endif -/* Define this string only once for all macro envocations */ -static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n"; +#if LINUX_VERSION_CODE < VERSION(2,1,25) +#define net_device_stats enet_statistics +#endif -int ppp_init(struct device *); -static void ppp_init_ctrl_blk(struct ppp *); -static int ppp_dev_open(struct device *); -static int ppp_dev_close(struct device *); -static void ppp_kick_tty(struct ppp *); +#if LINUX_VERSION_CODE < VERSION(2,1,57) +#define signal_pending(tsk) ((tsk)->pending & ~(tsk)->blocked) +#endif -#ifdef NEW_TTY_DRIVERS -#define ppp_find(tty) ((struct ppp *) tty->disc_data) +#if LINUX_VERSION_CODE < VERSION(2,1,60) +typedef int rw_ret_t; +typedef unsigned int rw_count_t; #else -static void ppp_output_done(void *); -static void ppp_unesc(struct ppp *ppp, unsigned char *c, int n); -static struct ppp *ppp_find(struct tty_struct *); +typedef ssize_t rw_ret_t; +typedef size_t rw_count_t; #endif -static void ppp_doframe(struct ppp *); -static int ppp_do_ip(struct ppp *, unsigned short, unsigned char *, int); -static int ppp_us_queue(struct ppp *, unsigned short, unsigned char *, int); -static int ppp_xmit(struct sk_buff *, struct device *); -static unsigned short ppp_type_trans(struct sk_buff *, struct device *); - -#ifdef NET02D -static int ppp_header(unsigned char *buff, struct device *dev, - unsigned short type, unsigned long daddr, - unsigned long saddr, unsigned len); -static int ppp_rebuild_header(void *buff, struct device *dev); -static void ppp_add_arp(unsigned long addr, struct sk_buff *skb, - struct device *dev); -#else -static int ppp_header(unsigned char *, struct device *, unsigned short, - void *, void *, unsigned, struct sk_buff *); -static int ppp_rebuild_header(void *, struct device *, unsigned long, - struct sk_buff *); +/* + * Local functions + */ + +#ifdef CONFIG_MODULES +static int ppp_register_compressor (struct compressor *cp); +static void ppp_unregister_compressor (struct compressor *cp); #endif -static struct enet_statistics *ppp_get_stats (struct device *); -static struct ppp *ppp_alloc(void); -static int ppp_lock(struct ppp *); -static void ppp_unlock(struct ppp *); -static void ppp_add_fcs(struct ppp *); -static int ppp_check_fcs(struct ppp *); -static void ppp_print_buffer(const char *,char *,int,int); - -static int ppp_read(struct tty_struct *, struct file *, unsigned char *, - unsigned int); -static int ppp_write(struct tty_struct *, struct file *, unsigned char *, - unsigned int); -static int ppp_ioctl(struct tty_struct *, struct file *, unsigned int, - unsigned long); -static int ppp_select(struct tty_struct *tty, struct inode * inode, - struct file * filp, int sel_type, select_table * wait); -static int ppp_open(struct tty_struct *); -static void ppp_close(struct tty_struct *); - -#ifdef NEW_TTY_DRIVERS -static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp, - char *fp, int count); -static void ppp_write_wakeup(struct tty_struct *tty); -#else -static void ppp_tty_input_ready(struct tty_struct *); +static void ppp_async_init(struct ppp *ppp); +static void ppp_async_release(struct ppp *ppp); +static int ppp_tty_push(struct ppp *ppp); +static int ppp_async_encode(struct ppp *ppp); +static int ppp_async_send(struct ppp *, struct sk_buff *); + +static int ppp_ioctl(struct ppp *, unsigned int, unsigned long); +static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp); +static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd); +static void ppp_ccp_closed(struct ppp *ppp); +static int ppp_receive_frame(struct ppp *, struct sk_buff *); +static void ppp_receive_error(struct ppp *ppp); +static void ppp_output_wakeup(struct ppp *ppp); +static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb); +static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb); +static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb); + +static struct ppp *ppp_find (int pid_value); +static struct ppp *ppp_alloc (void); +static void ppp_generic_init(struct ppp *ppp); +static void ppp_release(struct ppp *ppp); +static void ppp_print_buffer (const char *, const __u8 *, int); +static struct compressor *find_compressor (int type); + +#ifndef OPTIMIZE_FLAG_TIME +#define OPTIMIZE_FLAG_TIME 0 #endif -/* FCS table from RFC1331 */ - -static unsigned short fcstab[256] = { - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 - }; - -struct tty_ldisc ppp_ldisc; - -static struct ppp ppp_ctrl[PPP_NRUNIT]; +/* + * Parameters which may be changed via insmod. + */ -/************************************************************* - * INITIALIZATION - *************************************************************/ +static int flag_time = OPTIMIZE_FLAG_TIME; +#if LINUX_VERSION_CODE >= VERSION(2,1,19) +MODULE_PARM(flag_time, "i"); +#endif -static int first_time = 1; +#define CHECK_PPP_MAGIC(ppp) do { \ + if (ppp->magic != PPP_MAGIC) { \ + printk(ppp_magic_warn, ppp, __FILE__, __LINE__); \ + } \ +} while (0) +#define CHECK_PPP(a) do { \ + CHECK_PPP_MAGIC(ppp); \ + if (!ppp->inuse) { \ + printk(ppp_warning, __LINE__); \ + return a; \ + } \ +} while (0) +#define CHECK_PPP_VOID() do { \ + CHECK_PPP_MAGIC(ppp); \ + if (!ppp->inuse) { \ + printk(ppp_warning, __LINE__); \ + return; \ + } \ +} while (0) + +#define tty2ppp(tty) ((struct ppp *) ((tty)->disc_data)) +#define dev2ppp(dev) ((struct ppp *) ((dev)->priv)) +#define ppp2tty(ppp) ((ppp)->tty) +#define ppp2dev(ppp) (&(ppp)->dev) + +static struct ppp *ppp_list = NULL; +static struct ppp *ppp_last = NULL; + +/* Define these strings only once for all macro invocations */ +static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n"; +static char ppp_magic_warn[] = KERN_WARNING "bad magic for ppp %p at %s:%d\n"; -/* called at boot time for each ppp device */ +static char szVersion[] = PPP_VERSION; -int -ppp_init(struct device *dev) -{ - struct ppp *ppp; - int i; +#if LINUX_VERSION_CODE < VERSION(2,1,18) +static struct symbol_table ppp_syms = { +#include + X(ppp_register_compressor), + X(ppp_unregister_compressor), + X(ppp_crc16_table), +#include +}; +#else +EXPORT_SYMBOL(ppp_register_compressor); +EXPORT_SYMBOL(ppp_unregister_compressor); +#endif - ppp = &ppp_ctrl[dev->base_addr]; +/************************************************************* + * LINE DISCIPLINE SUPPORT + * The following code implements the PPP line discipline + * and supports using PPP on an async serial line. + *************************************************************/ - if (first_time) { - first_time = 0; +#define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f))) +#define in_rmap(ppp,c) ((((unsigned int) (__u8) (c)) < 0x20) && \ + ppp->recv_async_map & (1 << (c))) - printk (KERN_INFO "PPP: version %s (%d channels)" -#ifdef NET02D - " NET02D" -#endif -#ifdef NEW_TTY_DRIVERS - " NEW_TTY_DRIVERS" -#endif -#ifdef OPTIMIZE_FLAG_TIME - " OPTIMIZE_FLAGS" -#endif - "\n", PPP_VERSION, PPP_NRUNIT); - - printk (KERN_INFO - "TCP compression code copyright 1989 Regents of the " - "University of California\n"); - - (void) memset(&ppp_ldisc, 0, sizeof(ppp_ldisc)); - ppp_ldisc.open = ppp_open; - ppp_ldisc.close = ppp_close; - ppp_ldisc.read = ppp_read; - ppp_ldisc.write = ppp_write; - ppp_ldisc.ioctl = ppp_ioctl; - ppp_ldisc.select = ppp_select; - -#ifdef NEW_TTY_DRIVERS - ppp_ldisc.magic = TTY_LDISC_MAGIC; - ppp_ldisc.receive_buf = ppp_receive_buf; - ppp_ldisc.write_wakeup = ppp_write_wakeup; +/* + * TTY callbacks + */ + +static rw_ret_t ppp_tty_read(struct tty_struct *, struct file *, __u8 *, + rw_count_t); +static rw_ret_t ppp_tty_write(struct tty_struct *, struct file *, const __u8 *, + rw_count_t); +static int ppp_tty_ioctl(struct tty_struct *, struct file *, unsigned int, + unsigned long); +#if LINUX_VERSION_CODE < VERSION(2,1,23) +static int ppp_tty_select(struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait); #else - ppp_ldisc.handler = ppp_tty_input_ready; +static unsigned int ppp_tty_poll(struct tty_struct *tty, struct file *filp, + poll_table * wait); #endif +static int ppp_tty_open (struct tty_struct *); +static void ppp_tty_close (struct tty_struct *); +static int ppp_tty_room (struct tty_struct *tty); +static void ppp_tty_receive (struct tty_struct *tty, const __u8 * cp, + char *fp, int count); +static void ppp_tty_wakeup (struct tty_struct *tty); + +__u16 ppp_crc16_table[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; +EXPORT_SYMBOL(ppp_crc16_table); - if ((i = tty_register_ldisc(N_PPP, &ppp_ldisc)) == 0) - printk(KERN_INFO "PPP line discipline registered.\n"); - else - printk(KERN_ERR "error registering line discipline: %d\n", i); - } - - /* initialize PPP control block */ - ppp_init_ctrl_blk (ppp); - ppp->inuse = 0; - ppp->line = dev->base_addr; - ppp->tty = NULL; - ppp->dev = dev; - - /* clear statistics */ - memset (&ppp->stats, '\0', sizeof (struct ppp_stats)); - - /* device INFO */ - dev->mtu = PPP_MTU; - dev->hard_start_xmit = ppp_xmit; - dev->open = ppp_dev_open; - dev->stop = ppp_dev_close; - dev->get_stats = ppp_get_stats; - dev->hard_header = ppp_header; - dev->type_trans = ppp_type_trans; - dev->rebuild_header = ppp_rebuild_header; - dev->hard_header_len = 0; - dev->addr_len = 0; - dev->type = ARPHRD_PPP; - -#ifdef NET02D - dev->add_arp = ppp_add_arp; - dev->queue_xmit = dev_queue_xmit; +#ifdef CHECK_CHARACTERS +static __u32 paritytab[8] = +{ + 0x96696996, 0x69969669, 0x69969669, 0x96696996, + 0x69969669, 0x96696996, 0x96696996, 0x69969669 +}; #endif - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); /* = NULL if NET02D */ +/* + * This procedure is called at initialization time to register + * the PPP line discipline. + */ +static int +ppp_first_time(void) +{ + static struct tty_ldisc ppp_ldisc; + int status; - /* New-style flags */ - dev->flags = IFF_POINTOPOINT; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = sizeof(unsigned long); + printk(KERN_INFO + "PPP: version %s (demand dialling)" + "\n", szVersion); - return 0; -} +#ifndef MODULE /* slhc module logic has its own copyright announcement */ + printk(KERN_INFO + "TCP compression code copyright 1989 Regents of the " + "University of California\n"); +#endif -static void -ppp_init_ctrl_blk(struct ppp *ppp) -{ - ppp->magic = PPP_MAGIC; - ppp->sending = 0; - ppp->toss = 0xFE; - ppp->escape = 0; - - ppp->flags = 0; - ppp->mtu = PPP_MTU; - ppp->mru = PPP_MRU; - ppp->fcs = 0; - - memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map)); - ppp->xmit_async_map[0] = 0xffffffff; - ppp->xmit_async_map[3] = 0x60000000; - ppp->recv_async_map = 0x00000000; - - ppp->slcomp = NULL; - ppp->rbuff = NULL; - ppp->xbuff = NULL; - ppp->cbuff = NULL; - - ppp->rhead = NULL; - ppp->rend = NULL; - ppp->rcount = 0; - ppp->xhead = NULL; - ppp->xtail = NULL; - - ppp->us_rbuff = NULL; - ppp->us_rbuff_end = NULL; - ppp->us_rbuff_head = NULL; - ppp->us_rbuff_tail = NULL; - ppp->read_wait = NULL; - ppp->write_wait = NULL; - ppp->us_rbuff_lock = 0; - ppp->inp_sig = 0; - ppp->inp_sig_pid = 0; - -#ifdef OPTIMIZE_FLAG_TIME /* ensure flag will always be sent first time */ - ppp->last_xmit = jiffies - OPTIMIZE_FLAG_TIME; + /* + * Register the tty discipline + */ + (void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc)); + ppp_ldisc.magic = TTY_LDISC_MAGIC; + ppp_ldisc.name = "ppp"; + ppp_ldisc.open = ppp_tty_open; + ppp_ldisc.close = ppp_tty_close; + ppp_ldisc.read = ppp_tty_read; + ppp_ldisc.write = ppp_tty_write; + ppp_ldisc.ioctl = ppp_tty_ioctl; +#if LINUX_VERSION_CODE < VERSION(2,1,23) + ppp_ldisc.select = ppp_tty_select; #else - ppp->last_xmit = 0; + ppp_ldisc.poll = ppp_tty_poll; #endif + ppp_ldisc.receive_room = ppp_tty_room; + ppp_ldisc.receive_buf = ppp_tty_receive; + ppp_ldisc.write_wakeup = ppp_tty_wakeup; + + status = tty_register_ldisc (N_PPP, &ppp_ldisc); + if (status == 0) + printk(KERN_INFO "PPP line discipline registered.\n"); + else + printk(KERN_ERR "error registering line discipline: %d\n", + status); + return status; +} - /* clear statistics */ - memset (&ppp->stats, '\0', sizeof (struct ppp_stats)); - /* Reset the demand dial information */ - ppp->ddinfo.ip_sjiffies = - ppp->ddinfo.ip_rjiffies = - ppp->ddinfo.nip_sjiffies = - ppp->ddinfo.nip_rjiffies = jiffies; +#ifndef MODULE +/* + * Called at boot time if the PPP driver is compiled into the kernel. + */ +int +ppp_init(struct device *dev) +{ + static int first_time = 1; + int answer = 0; + + if (first_time) { + first_time = 0; + answer = ppp_first_time(); +#if LINUX_VERSION_CODE < VERSION(2,1,18) + if (answer == 0) + (void) register_symtab(&ppp_syms); +#endif + } + if (answer == 0) + answer = -ENODEV; + return answer; } +#endif /* - * MTU has been changed by the IP layer. Unfortunately we are not told - * about this, but we spot it ourselves and fix things up. We could be - * in an upcall from the tty driver, or in an ip packet queue. + * Initialize the async-specific parts of the ppp structure. */ - static void -ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru) +ppp_async_init(struct ppp *ppp) { - struct device *dev; - unsigned char *new_rbuff, *new_xbuff, *new_cbuff; - unsigned char *old_rbuff, *old_xbuff, *old_cbuff; - int mtu, mru; -/* - * Allocate the buffer from the kernel for the data - */ - dev = ppp->dev; - mru = new_mru; - mtu = new_mtu; - - /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */ - if (mru < PPP_MRU) - mru = PPP_MRU; - - mtu = (mtu * 2) + 20; - mru = (mru * 2) + 20; - - PRINTKN (2,(KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n", - dev->name, new_mtu, new_mru)); - - new_xbuff = (unsigned char *) kmalloc(mtu + 4, GFP_ATOMIC); - new_rbuff = (unsigned char *) kmalloc(mru + 4, GFP_ATOMIC); - new_cbuff = (unsigned char *) kmalloc(mru + 4, GFP_ATOMIC); + ppp->escape = 0; + ppp->toss = 0xE0; + ppp->tty_pushing = 0; + + memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map)); + ppp->xmit_async_map[0] = 0xffffffff; + ppp->xmit_async_map[3] = 0x60000000; + ppp->recv_async_map = 0xffffffff; + + ppp->tpkt = NULL; + ppp->tfcs = PPP_INITFCS; + ppp->optr = ppp->obuf; + ppp->olim = ppp->obuf; + + ppp->rpkt = NULL; + ppp->rfcs = PPP_INITFCS; + + ppp->tty = NULL; + ppp->backup_tty = NULL; + + ppp->bytes_sent = 0; + ppp->bytes_rcvd = 0; +} + /* - * If the buffers failed to allocate then complain. + * Clean up the async-specific parts of the ppp structure. */ - if (new_xbuff == NULL || new_rbuff == NULL || new_cbuff == NULL) - { - PRINTKN (2,(KERN_ERR "ppp: failed to allocate new buffers\n")); +static void +ppp_async_release(struct ppp *ppp) +{ + struct sk_buff *skb; + + if ((skb = ppp->rpkt) != NULL) + kfree_skb(skb); + ppp->rpkt = NULL; + if ((skb = ppp->tpkt) != NULL) + kfree_skb(skb); + ppp->tpkt = NULL; +} + /* - * Release new buffer pointers if the updates were not performed + * TTY callback. + * + * Called when the tty discipline is switched to PPP. */ - if (new_rbuff != NULL) - kfree (new_rbuff); - if (new_xbuff != NULL) - kfree (new_xbuff); +static int +ppp_tty_open (struct tty_struct *tty) +{ + struct ppp *ppp; + + /* + * Allocate a ppp structure to use. + */ + tty->disc_data = NULL; + ppp = ppp_find(current->pid); + if (ppp != NULL) { + /* + * If we are taking over a ppp unit which is currently + * connected to a loopback pty, there's not much to do. + */ + CHECK_PPP(-EINVAL); - if (new_cbuff != NULL) - kfree (new_cbuff); - } -/* - * Update the pointers to the new buffer structures. - */ - else - { - cli(); - old_xbuff = ppp->xbuff; - old_rbuff = ppp->rbuff; - old_cbuff = ppp->cbuff; + } else { + ppp = ppp_alloc(); + if (ppp == NULL) { + printk(KERN_ERR "ppp_alloc failed\n"); + return -ENFILE; + } + + /* + * Initialize the control block + */ + ppp_generic_init(ppp); + ppp_async_init(ppp); + + MOD_INC_USE_COUNT; + } - ppp->xbuff = new_xbuff; - ppp->rbuff = new_rbuff; - ppp->cbuff = new_cbuff; + tty->disc_data = ppp; + ppp->tty = tty; - dev->mem_start = (unsigned long) new_xbuff; - dev->mem_end = (unsigned long) (dev->mem_start + mtu); + /* + * Flush any pending characters in the driver + */ + if (tty->driver.flush_buffer) + tty->driver.flush_buffer (tty); - dev->rmem_start = (unsigned long) new_rbuff; - ppp->rend = (unsigned char *) - dev->rmem_end = (unsigned long) (dev->rmem_start + mru); + return ppp->line; +} - ppp->rhead = new_rbuff; /* - * Update the parameters for the new buffer sizes + * TTY callback. + * + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. */ - ppp->toss = 0xFE; - ppp->escape = 0; - ppp->sending = 0; - ppp->rcount = 0; - ppp->mru = new_mru; +static void +ppp_tty_close (struct tty_struct *tty) +{ + struct ppp *ppp = tty2ppp(tty); - ppp->mtu = - dev->mtu = new_mtu; + if (ppp == NULL) + return; + tty->disc_data = NULL; + if (ppp->magic != PPP_MAGIC) { + printk(KERN_WARNING "ppp_tty_close: bogus\n"); + return; + } + if (!ppp->inuse) { + printk(KERN_WARNING "ppp_tty_close: not inuse\n"); + ppp->tty = ppp->backup_tty = 0; + return; + } + if (tty == ppp->backup_tty) + ppp->backup_tty = 0; + if (tty != ppp->tty) + return; + if (ppp->backup_tty) { + ppp->tty = ppp->backup_tty; + } else { + ppp->tty = 0; + ppp->sc_xfer = 0; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "ppp: channel %s closing.\n", + ppp2dev(ppp)->name); + + ppp_async_release(ppp); + ppp_release(ppp); + ppp->inuse = 0; + MOD_DEC_USE_COUNT; + } +} - sti(); /* - * Release old buffer pointers + * Read a PPP frame from the rcv_q list, + * waiting if necessary */ - if (old_rbuff != NULL) - kfree (old_rbuff); +static rw_ret_t +ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf, + rw_count_t nr) +{ + struct ppp *ppp = tty2ppp (tty); + struct sk_buff *skb; + rw_ret_t len, err; + + /* + * Validate the pointers + */ + if (!ppp) + return -EIO; + CHECK_PPP(-ENXIO); + + /* + * Before we attempt to write the frame to the user, ensure that the + * user has access to the pages for the total buffer length. + */ + err = verify_area(VERIFY_WRITE, buf, nr); + if (err != 0) + return (err); + + /* + * Wait for a frame to arrive if necessary. + * We increment the module use count so that the module + * can't go away while we're sleeping. + */ + MOD_INC_USE_COUNT; + skb = NULL; + for (;;) { + ppp = tty2ppp(tty); + err = 0; + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse + || tty != ppp->tty) + break; + + skb = skb_dequeue(&ppp->rcv_q); + if (skb != 0) + break; + + /* + * If no frame is available, return -EAGAIN or wait. + */ + err = -EAGAIN; + if (file->f_flags & O_NONBLOCK) + break; + + current->timeout = 0; + interruptible_sleep_on(&ppp->read_wait); + err = -EINTR; + if (signal_pending(current)) + break; + } + MOD_DEC_USE_COUNT; + if (skb == 0) + return err; + + /* + * Ensure that the frame will fit within the caller's buffer. + * If not, just discard the frame. + */ + len = skb->len; + if (len > nr) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG + "ppp: read of %lu bytes too small for %ld " + "frame\n", (unsigned long) nr, (long) len); + ppp->stats.ppp_ierrors++; + err = -EOVERFLOW; + goto out; + } - if (old_xbuff != NULL) - kfree (old_xbuff); + /* + * Copy the received data from the buffer to the caller's area. + */ + err = COPY_TO_USER(buf, skb->data, len); + if (err == 0) + err = len; - if (old_cbuff != NULL) - kfree (old_cbuff); - } +out: + kfree_skb(skb); + return err; } -/* called when we abandon the PPP line discipline */ - -static void -ppp_release(struct ppp *ppp) +/* + * Writing to a tty in ppp line discipline sends a PPP frame. + * Used by pppd to send control packets (LCP, etc.). + */ +static rw_ret_t +ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data, + rw_count_t count) { -#ifdef NEW_TTY_DRIVERS - if (ppp->tty != NULL && ppp->tty->disc_data == ppp) - ppp->tty->disc_data = NULL; /* Break the tty->ppp link */ -#endif + struct ppp *ppp = tty2ppp (tty); + __u8 *new_data; + struct sk_buff *skb; + + /* + * Verify the pointers. + */ + if (!ppp) + return -EIO; + + if (ppp->magic != PPP_MAGIC) + return -EIO; + + CHECK_PPP(-ENXIO); + + /* + * Ensure that the caller does not wish to send too much. + */ + if (count > PPP_MTU + PPP_HDRLEN) { + if (ppp->flags & SC_DEBUG) + printk(KERN_WARNING + "ppp_tty_write: truncating user packet " + "from %lu to mtu %d\n", (unsigned long) count, + PPP_MTU + PPP_HDRLEN); + count = PPP_MTU + PPP_HDRLEN; + } - if (ppp->dev) { - ppp->dev->flags &= ~IFF_UP; /* down the device */ - ppp->dev->flags |= IFF_POINTOPOINT; - } + /* + * Allocate a buffer for the data and fetch it from the user space. + */ + skb = alloc_skb(count, GFP_KERNEL); + if (skb == NULL) { + printk(KERN_ERR "ppp_tty_write: no memory\n"); + return 0; + } + new_data = skb_put(skb, count); + + /* + * Retrieve the user's buffer + */ + if (COPY_FROM_USER(new_data, data, count)) { + kfree_skb(skb); + return -EFAULT; + } - kfree (ppp->xbuff); - kfree (ppp->cbuff); - kfree (ppp->rbuff); - kfree (ppp->us_rbuff); + /* + * Send the frame + */ + ppp_send_ctrl(ppp, skb); + + return (rw_ret_t) count; +} - ppp->xbuff = - ppp->cbuff = - ppp->rbuff = - ppp->us_rbuff = NULL; +/* + * Process the IOCTL call for the tty device. + * Only the ioctls that relate to using ppp on async serial lines + * are processed here; the rest are handled by ppp_ioctl. + */ +static int +ppp_tty_ioctl (struct tty_struct *tty, struct file * file, + unsigned int param2, unsigned long param3) +{ + struct ppp *ppp = tty2ppp (tty); + register int temp_i = 0; + int error = -EFAULT; + + /* + * Verify the status of the PPP device. + */ + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) + return -ENXIO; + + /* + * The user must have an euid of root to do these requests. + */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (param2) { + case PPPIOCGASYNCMAP: + /* + * Retrieve the transmit async map + */ + if (PUT_USER(ppp->xmit_async_map[0], (int *) param3)) + break; + error = 0; + break; + + case PPPIOCSASYNCMAP: + /* + * Set the transmit async map + */ + if (GET_USER(temp_i, (int *) param3)) + break; + ppp->xmit_async_map[0] = temp_i; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_tty_ioctl: set xmit asyncmap %x\n", + ppp->xmit_async_map[0]); + error = 0; + break; + + case PPPIOCSRASYNCMAP: + /* + * Set the receive async map + */ + if (GET_USER(temp_i, (int *) param3)) + break; + ppp->recv_async_map = temp_i; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_tty_ioctl: set rcv asyncmap %x\n", + ppp->recv_async_map); + error = 0; + break; + + case PPPIOCGXASYNCMAP: + /* + * Get the map of characters to be escaped on transmission. + */ + if (COPY_TO_USER((void *) param3, ppp->xmit_async_map, + sizeof (ppp->xmit_async_map))) + break; + error = 0; + break; + + case PPPIOCSXASYNCMAP: + /* + * Set the map of characters to be escaped on transmission. + */ + { + __u32 temp_tbl[8]; + + if (COPY_FROM_USER(temp_tbl, (void *) param3, + sizeof (temp_tbl))) + break; + + temp_tbl[1] = 0x00000000; + temp_tbl[2] &= ~0x40000000; + temp_tbl[3] |= 0x60000000; + + memcpy(ppp->xmit_async_map, temp_tbl, + sizeof (ppp->xmit_async_map)); + + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_tty_ioctl: set xasyncmap\n"); + error = 0; + } + break; + + case PPPIOCXFERUNIT: + /* + * Set up this PPP unit to be used next time this + * process sets a tty to PPP line discipline. + */ + ppp->backup_tty = tty; + ppp->sc_xfer = current->pid; + error = 0; + break; + + case TCGETS: + case TCGETA: + /* + * Allow users to read, but not set, the serial port parameters + */ + error = n_tty_ioctl (tty, file, param2, param3); + break; + + case FIONREAD: + /* + * Returns how many bytes are available for a read(). + */ + { + unsigned long flags; + struct sk_buff *skb; + int count = 0; + + save_flags(flags); + cli(); + skb = skb_peek(&ppp->rcv_q); + if (skb != 0) + count = skb->len; + restore_flags(flags); + if (PUT_USER(count, (int *) param3)) + break; + error = 0; + } + break; + + default: + /* + * All other ioctl() events will come here. + */ + error = ppp_ioctl(ppp, param2, param3); + break; + } + return error; +} - if (ppp->slcomp) { - slhc_free(ppp->slcomp); - ppp->slcomp = NULL; - } +/* + * TTY callback. + * + * Process the select() or poll() statement for the PPP device. + */ - ppp->inuse = 0; - ppp->tty = NULL; +#if LINUX_VERSION_CODE < VERSION(2,1,23) +static int +ppp_tty_select(struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait) +{ + struct ppp *ppp = tty2ppp(tty); + int result = 1; + + /* + * Verify the status of the PPP device. + */ + if (!ppp || tty != ppp->tty) + return -EBADF; + + CHECK_PPP(-EBADF); + + switch (sel_type) { + case SEL_IN: + /* The fd is readable if the receive queue isn't empty. */ + if (skb_peek(&ppp->rcv_q) != NULL) + break; + /* fall through */ + case SEL_EX: + /* Check for exceptions or read errors. */ + /* Is this a pty link and the remote disconnected? */ + if (tty->flags & (1 << TTY_OTHER_CLOSED)) + break; + + /* Is this a local link and the modem disconnected? */ + if (tty_hung_up_p (filp)) + break; + + select_wait(&ppp->read_wait, wait); + result = 0; + break; + + case SEL_OUT: + /* The fd is always writable. */ + break; + } + return result; } -static void -ppp_close(struct tty_struct *tty) +#else /* 2.1.23 or later */ + +static unsigned int +ppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait) { - struct ppp *ppp = ppp_find(tty); + struct ppp *ppp = tty2ppp(tty); + unsigned int mask = 0; + + if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) { + CHECK_PPP(0); - if (ppp == NULL || ppp->magic != PPP_MAGIC) { - PRINTKN (1,(KERN_WARNING "ppp: trying to close unopened tty!\n")); - } else { - CHECK_PPP_VOID(); - ppp_release (ppp); + poll_wait(filp, &ppp->read_wait, wait); - PRINTKN (2,(KERN_INFO "ppp: channel %s closing.\n", ppp->dev->name)); - } + if (skb_peek(&ppp->rcv_q) != NULL) + mask |= POLLIN | POLLRDNORM; + if (tty->flags & (1 << TTY_OTHER_CLOSED) + || tty_hung_up_p(filp)) + mask |= POLLHUP; + mask |= POLLOUT | POLLWRNORM; + } + return mask; } +#endif /* >= 2.1.23 */ -/* called when PPP line discipline is selected on a tty */ -static int -ppp_open(struct tty_struct *tty) +/* + * This function is called by the tty driver when the transmit buffer has + * additional space. It is used by the ppp code to continue to transmit + * the current buffer should the buffer have been partially sent. + */ +static void +ppp_tty_wakeup (struct tty_struct *tty) { - struct ppp *ppp = ppp_find(tty); - - if (ppp) { - PRINTKN (1,(KERN_ERR "ppp_open: gack! tty already associated to %s!\n", - ppp->magic == PPP_MAGIC ? ppp->dev->name : "unknown")); - return -EEXIST; - } - - ppp = ppp_alloc(); - if (ppp == NULL) { - PRINTKN (1,(KERN_ERR "ppp_open: couldn't allocate ppp channel\n")); - return -ENFILE; - } - - /* make sure the channel is actually open */ - ppp_init_ctrl_blk (ppp); - - ppp->tty = tty; - -#ifdef NEW_TTY_DRIVERS - tty->disc_data = ppp; - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); -#else - tty_read_flush (tty); - tty_write_flush (tty); -#endif + struct ppp *ppp = tty2ppp (tty); + + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + if (!ppp) + return; + CHECK_PPP_VOID(); + if (tty != ppp->tty) + return; - if ((ppp->slcomp = slhc_init(16, 16)) == NULL) { - PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n")); - ppp_release (ppp); - return -ENOMEM; - } - - /* Define the buffers for operation */ - ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru); - if (ppp->rbuff == NULL) { - ppp_release (ppp); - return -ENOMEM; - } - - /* Allocate a user-level receive buffer */ - ppp->us_rbuff = kmalloc (RBUFSIZE, GFP_KERNEL); - if (ppp->us_rbuff == NULL) { - PRINTKN (1,(KERN_ERR "ppp: no space for user receive buffer\n")); - ppp_release (ppp); - return -ENOMEM; - } - - ppp->us_rbuff_head = - ppp->us_rbuff_tail = ppp->us_rbuff; - ppp->us_rbuff_end = ppp->us_rbuff + RBUFSIZE; - - PRINTKN (2,(KERN_INFO "ppp: channel %s open\n", ppp->dev->name)); - - return (ppp->line); + if (ppp_tty_push(ppp)) + ppp_output_wakeup(ppp); } -/* called when ppp interface goes "up". here this just means we start - passing IP packets */ +/* + * Send a packet to the peer over an async tty line. + * Returns -1 iff the packet could not be accepted at present, + * 0 if the packet was accepted but we can't accept another yet, or + * 1 if we can accept another packet immediately. + * If this procedure returns 0, ppp_output_wakeup will be called + * exactly once. + */ static int -ppp_dev_open(struct device *dev) +ppp_async_send(struct ppp *ppp, struct sk_buff *skb) { - struct ppp *ppp = &ppp_ctrl[dev->base_addr]; - - /* reset POINTOPOINT every time, since dev_close zaps it! */ - dev->flags |= IFF_POINTOPOINT; + CHECK_PPP(0); - if (ppp->tty == NULL) { - PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go open!\n", - dev->name)); - return -ENXIO; - } + ppp_tty_push(ppp); - PRINTKN (2,(KERN_INFO "ppp: channel %s going up for IP packets!\n", - dev->name)); + if (ppp->tpkt != NULL) + return -1; + ppp->tpkt = skb; + ppp->tpkt_pos = 0; - CHECK_PPP(-ENXIO); - return 0; + return ppp_tty_push(ppp); } +/* + * Push as much data as possible out to the tty. + * Returns 1 if we finished encoding the current frame, 0 otherwise. + */ static int -ppp_dev_close(struct device *dev) +ppp_tty_push(struct ppp *ppp) { - struct ppp *ppp = &ppp_ctrl[dev->base_addr]; - - if (ppp->tty == NULL) { - PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go down!\n", - dev->name)); - return -ENXIO; - } - - PRINTKN (2,(KERN_INFO "ppp: channel %s going down for IP packets!\n", - dev->name)); - CHECK_PPP(-ENXIO); - return 0; + int avail, sent, done = 0; + struct tty_struct *tty = ppp2tty(ppp); + + CHECK_PPP(0); + if (ppp->tty_pushing) + return 0; + if (tty == NULL || tty->disc_data != (void *) ppp) + goto flush; + while (ppp->optr < ppp->olim || ppp->tpkt != 0) { + ppp->tty_pushing = 1; + avail = ppp->olim - ppp->optr; + if (avail > 0) { + tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + sent = tty->driver.write(tty, 0, ppp->optr, avail); + if (sent < 0) + goto flush; /* error, e.g. loss of CD */ + ppp->stats.ppp_obytes += sent; + ppp->optr += sent; + if (sent < avail) { + ppp->tty_pushing = 0; + return done; + } + } + if (ppp->tpkt != 0) + done = ppp_async_encode(ppp); + ppp->tty_pushing = 0; + } + return done; + +flush: + ppp->tty_pushing = 1; + ppp->stats.ppp_oerrors++; + if (ppp->tpkt != 0) { + kfree_skb(ppp->tpkt); + ppp->tpkt = 0; + done = 1; + } + ppp->optr = ppp->olim; + ppp->tty_pushing = 0; + return done; } -/************************************************************* - * TTY OUTPUT - * The following function delivers a fully-formed PPP - * frame in ppp->xbuff to the TTY for output. - *************************************************************/ - -#ifdef NEW_TTY_DRIVERS -static inline void -#else -static void -#endif -ppp_output_done (void *ppp) +/* + * Procedure to encode the data for async serial transmission. + * Does octet stuffing (escaping) and address/control + * and protocol compression. + * Assumes ppp->opkt != 0 on entry. + * Returns 1 if we finished the current frame, 0 otherwise. + */ +static int +ppp_async_encode(struct ppp *ppp) { - /* unlock the transmitter queue */ - ppp_unlock ((struct ppp *) ppp); + int fcs, i, count, c; + unsigned char *buf, *buflim; + unsigned char *data; + int islcp; + + CHECK_PPP(0); + + buf = ppp->obuf; + ppp->olim = buf; + ppp->optr = buf; + i = ppp->tpkt_pos; + data = ppp->tpkt->data; + count = ppp->tpkt->len; + fcs = ppp->tfcs; + + /* + * LCP packets with code values between 1 (configure-reqest) + * and 7 (code-reject) must be sent as though no options + * had been negotiated. + */ + islcp = PPP_PROTOCOL(data) == PPP_LCP + && 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7; + + if (i == 0) { + /* + * Start of a new packet - insert the leading FLAG + * character if necessary. + */ + if (islcp || flag_time == 0 + || jiffies - ppp->last_xmit >= flag_time) + *buf++ = PPP_FLAG; + /* only reset idle time for data packets */ + if (PPP_PROTOCOL(data) < 0x8000) + ppp->last_xmit = jiffies; + fcs = PPP_INITFCS; + ++ppp->stats.ppp_opackets; + ppp->stats.ppp_ooctects += count; + + /* + * Do address/control compression + */ + if ((ppp->flags & SC_COMP_AC) != 0 && !islcp + && PPP_ADDRESS(data) == PPP_ALLSTATIONS + && PPP_CONTROL(data) == PPP_UI) + i += 2; + } - /* If the device is still up then enable the transmitter of the - next frame. */ - if (((struct ppp *) ppp)->dev->flags & IFF_UP) - dev_tint (((struct ppp *) ppp)->dev); + /* + * Once we put in the last byte, we need to put in the FCS + * and closing flag, so make sure there is at least 7 bytes + * of free space in the output buffer. + */ + buflim = buf + OBUFSIZE - 6; + while (i < count && buf < buflim) { + c = data[i++]; + if (i == 3 && c == 0 && (ppp->flags & SC_COMP_PROT)) + continue; /* compress protocol field */ + fcs = PPP_FCS(fcs, c); + if (in_xmap(ppp, c) || (islcp && c < 0x20)) { + *buf++ = PPP_ESCAPE; + c ^= 0x20; + } + *buf++ = c; + } - /* enable any blocked process pending transmission */ - wake_up_interruptible (&((struct ppp *) ppp)->write_wait); -} + if (i == count) { + /* + * We have finished the packet. Add the FCS and flag. + */ + fcs = ~fcs; + c = fcs & 0xff; + if (in_xmap(ppp, c) || (islcp && c < 0x20)) { + *buf++ = PPP_ESCAPE; + c ^= 0x20; + } + *buf++ = c; + c = (fcs >> 8) & 0xff; + if (in_xmap(ppp, c) || (islcp && c < 0x20)) { + *buf++ = PPP_ESCAPE; + c ^= 0x20; + } + *buf++ = c; + *buf++ = PPP_FLAG; + ppp->olim = buf; + + kfree_skb(ppp->tpkt); + ppp->tpkt = 0; + return 1; + } -#ifndef NEW_TTY_DRIVERS -static void -ppp_kick_tty (struct ppp *ppp) -{ - register int count = ppp->xhead - ppp->xbuff; - register int answer; - - ppp->stats.sbytes += count; - - answer = tty_write_data (ppp->tty, - ppp->xbuff, - count, - ppp_output_done, - (void *) ppp); - - if (answer == 0) - ppp_output_done (ppp); /* Should not happen */ - else - if (answer < 0) { - ppp->stats.serrors++; - ppp_output_done (ppp); /* unlock the transmitter */ - } + /* + * Remember where we are up to in this packet. + */ + ppp->olim = buf; + ppp->tpkt_pos = i; + ppp->tfcs = fcs; + return 0; } -#else - -static void -ppp_kick_tty (struct ppp *ppp) +/* + * Callback function from tty driver. Return the amount of space left + * in the receiver's buffer to decide if remote transmitter is to be + * throttled. + */ +static int +ppp_tty_room (struct tty_struct *tty) { - register int count, actual; - - count = ppp->xhead - ppp->xbuff; - - actual = ppp->tty->driver.write(ppp->tty, 0, ppp->xbuff, count); - ppp->stats.sbytes += actual; - if (actual == count) { - ppp_output_done(ppp); - } else { - ppp->xtail = ppp->xbuff + actual; - ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - } + return 65536; /* We can handle an infinite amount of data. :-) */ } -static void ppp_write_wakeup(struct tty_struct *tty) +/* + * Callback function when data is available at the tty driver. + */ +static void +ppp_tty_receive (struct tty_struct *tty, const __u8 * data, + char *flags, int count) { - register int count, actual; - struct ppp *ppp = ppp_find(tty); - - if (!ppp || ppp->magic != PPP_MAGIC) { - PRINTKN (1, - (KERN_ERR "PPP: write_wakeup called but couldn't " - "find PPP struct.\n")); + register struct ppp *ppp = tty2ppp (tty); + struct sk_buff *skb; + int chr, flg; + unsigned char *p; + + if (ppp != 0) + CHECK_PPP_VOID(); + /* + * This can happen if stuff comes in on the backup tty. + */ + if (ppp == 0 || tty != ppp->tty) + return; + /* + * Verify the table pointer and ensure that the line is + * still in PPP discipline. + */ + if (ppp->magic != PPP_MAGIC) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG + "PPP: tty_receive called but couldn't find " + "PPP struct.\n"); return; } + /* + * Print the buffer if desired + */ + if (ppp->flags & SC_LOG_RAWIN) + ppp_print_buffer ("receive buffer", data, count); + + ppp->stats.ppp_ibytes += count; + skb = ppp->rpkt; + while (count-- > 0) { + /* + * Collect the character and error condition for the character. + * Set the toss flag for the first character error. + */ + chr = *data++; + if (flags) { + flg = *flags++; + if (flg) { + if (ppp->toss == 0) + ppp->toss = flg; + switch (flg) { + case TTY_OVERRUN: + ++ppp->estats.rx_fifo_errors; + break; + case TTY_FRAME: + case TTY_BREAK: + ++ppp->estats.rx_frame_errors; + break; + } + continue; + } + } + + /* + * Set the flags for d7 being 0/1 and parity being + * even/odd so that the normal processing would have + * all flags set at the end of the session. A + * missing flag bit indicates an error condition. + */ - if (!ppp->xtail || (ppp->flags & SC_XMIT_BUSY)) - return; +#ifdef CHECK_CHARACTERS + if (chr & 0x80) + ppp->flags |= SC_RCV_B7_1; + else + ppp->flags |= SC_RCV_B7_0; + + if (paritytab[chr >> 5] & (1 << (chr & 0x1F))) + ppp->flags |= SC_RCV_ODDP; + else + ppp->flags |= SC_RCV_EVNP; +#endif - cli(); - if (ppp->flags & SC_XMIT_BUSY) - return; - ppp->flags |= SC_XMIT_BUSY; - sti(); - - count = ppp->xhead - ppp->xtail; - - actual = tty->driver.write(tty, 0, ppp->xtail, count); - ppp->stats.sbytes += actual; - if (actual == count) { - ppp->xtail = 0; - tty->flags &= ~TTY_DO_WRITE_WAKEUP; - - ppp_output_done(ppp); - } else { - ppp->xtail += actual; + if (chr == PPP_FLAG) { + /* + * FLAG. This is the end of the block. If the block + * ends with ESC FLAG, then the block is to be ignored. + */ + if (ppp->escape) + ppp->toss |= 0x80; + /* + * Process the frame if it was received correctly. + * If there was an error, let the VJ decompressor know. + * There are 4 cases here: + * skb != NULL, toss != 0: error in frame + * skb != NULL, toss == 0: frame ok + * skb == NULL, toss != 0: very first frame, + * error on 1st char, or alloc_skb failed + * skb == NULL, toss == 0: empty frame (~~) + */ + if (ppp->toss || !ppp_receive_frame(ppp, skb)) { + if (ppp->toss && (ppp->flags & SC_DEBUG)) + printk(KERN_DEBUG + "ppp: tossing frame (%x)\n", + ppp->toss); + if (skb != NULL) + kfree_skb(skb); + if (!(ppp->toss == 0xE0 || ppp->toss == 0x80)) + ++ppp->stats.ppp_ierrors; + ppp_receive_error(ppp); + } + /* + * Reset for the next frame. + */ + skb = NULL; + ppp->rfcs = PPP_INITFCS; + ppp->escape = 0; + ppp->toss = 0; + continue; + } + + /* If we're tossing, look no further. */ + if (ppp->toss != 0) + continue; + + /* If this is a control char to be ignored, do so */ + if (in_rmap(ppp, chr)) + continue; + + /* + * Modify the next character if preceded by escape. + * The escape character (0x7d) could be an escaped + * 0x5d, if it follows an escape :-) + */ + if (ppp->escape) { + chr ^= PPP_TRANS; + ppp->escape = 0; + } else if (chr == PPP_ESCAPE) { + ppp->escape = PPP_TRANS; + continue; + } + + /* + * Allocate an skbuff on the first character received. + * The 128 is room for VJ header expansion and FCS. + */ + if (skb == NULL) { + skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN); + if (skb == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "couldn't " + "alloc skb for recv\n"); + ppp->toss = 1; + continue; + } + } + + /* + * Decompress A/C and protocol compression here. + */ + if (skb->len == 0 && chr != PPP_ALLSTATIONS) { + p = skb_put(skb, 2); + p[0] = PPP_ALLSTATIONS; + p[1] = PPP_UI; + } + if (skb->len == 2 && (chr & 1) != 0) { + p = skb_put(skb, 1); + p[0] = 0; + } + + /* + * Check if we've overflowed the MRU + */ + if (skb->len >= ppp->mru + PPP_HDRLEN + 2 + || skb_tailroom(skb) <= 0) { + ++ppp->estats.rx_length_errors; + ppp->toss = 0xC0; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "rcv frame too long: " + "len=%d mru=%d hroom=%d troom=%d\n", + skb->len, ppp->mru, skb_headroom(skb), + skb_tailroom(skb)); + continue; + } + + /* + * Store the character and update the FCS. + */ + p = skb_put(skb, 1); + *p = chr; + ppp->rfcs = PPP_FCS(ppp->rfcs, chr); } - ppp->flags &= ~SC_XMIT_BUSY; + ppp->rpkt = skb; } -#endif /************************************************************* - * TTY INPUT - * The following functions handle input that arrives from - * the TTY. It recognizes PPP frames and either hands them - * to the network layer or queues them for delivery to a - * user process reading this TTY. + * PPP NETWORK INTERFACE SUPPORT + * The following code implements the PPP network + * interface device and handles those parts of + * the PPP processing which are independent of the + * type of hardware link being used, including + * VJ and packet compression. *************************************************************/ -/* stuff a single character into the receive buffer */ - -inline void -ppp_enqueue(struct ppp *ppp, unsigned char c) -{ - unsigned long flags; - - save_flags(flags); - cli(); - if (ppp->rhead < ppp->rend) { - *ppp->rhead = c; - ppp->rhead++; - ppp->rcount++; - } else - ppp->stats.roverrun++; - restore_flags(flags); -} +/* + * Network device driver callback routines + */ -#ifdef CHECK_CHARACTERS -static unsigned paritytab[8] = { - 0x96696996, 0x69969669, 0x69969669, 0x96696996, - 0x69969669, 0x96696996, 0x96696996, 0x69969669 -}; +static int ppp_init_dev(struct device *dev); +static int ppp_dev_open(struct device *); +static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static int ppp_dev_close(struct device *); +static int ppp_dev_xmit(struct sk_buff *, struct device *); +static struct net_device_stats *ppp_dev_stats (struct device *); + +#if LINUX_VERSION_CODE < VERSION(2,1,15) +static int ppp_dev_header(struct sk_buff *, struct device *, __u16, + void *, void *, unsigned int); +static int ppp_dev_rebuild(void *eth, struct device *dev, + unsigned long raddr, struct sk_buff *skb); #endif -#ifndef NEW_TTY_DRIVERS -static void -ppp_dump_inqueue(struct tty_struct *tty) -{ - int head = tty->read_q.head, - tail = tty->read_q.tail, - i, count; - char buffer[8]; - - PRINTK ((KERN_DEBUG "INQUEUE: head %d tail %d imode %x:\n", head, tail, - (unsigned int) tty->termios->c_iflag)) - - i = tail; - count = 0; - - while (i != head) { - buffer [count] = tty->read_q.buf[i]; - if (++count == 8) { - ppp_print_buffer (NULL, buffer, 8, KERNEL_DS); - count = 0; - } - i = (i + 1) & (TTY_BUF_SIZE - 1); - } - ppp_print_buffer (NULL, buffer, count, KERNEL_DS); -} - -/* called by lower levels of TTY driver when data becomes available. - all incoming data comes through this function. */ - -void ppp_tty_input_ready(struct tty_struct *tty) -{ - struct ppp *ppp = ppp_find(tty); - int n, error; - unsigned char buff[128]; - -/* PRINTK( (KERN_DEBUG "PPP: handler called.\n") ) */ - if (!ppp || ppp->magic != PPP_MAGIC) { - PRINTKN (1, - (KERN_ERR "PPP: handler called but couldn't find PPP struct.\n")); - return; - } - - CHECK_PPP_VOID(); - - /* ZZZ */ - if (ppp_debug >= 5) - ppp_dump_inqueue(ppp->tty); - - do { - n = tty_read_raw_data(tty, buff, 128); - if ( n == 0 ) /* nothing there */ - break; - - if (ppp_debug >= 5) - ppp_print_buffer ("receive buffer", buff, n > 0 ? n : -n, KERNEL_DS); - - if ( n < 0 ) { - /* Last character is error flag. - Process the previous characters, then set toss flag. */ - n = (-n) - 1; - error = buff[n]; - } else error = 0; - ppp->stats.rbytes += n; - ppp_unesc(ppp,buff,n); - if (error) - ppp->toss = error; - } while (1); -} +/* + * Information for the protocol decoder + */ -/* recover frame by undoing PPP escape mechanism; - copies N chars of input data from C into PPP->rbuff - calls ppp_doframe to dispose of any frames it finds -*/ +typedef int (*pfn_proto) (struct ppp *, struct sk_buff *); + +typedef struct ppp_proto_struct { + int proto; + pfn_proto func; +} ppp_proto_type; + +static int rcv_proto_ip (struct ppp *, struct sk_buff *); +static int rcv_proto_ipx (struct ppp *, struct sk_buff *); +static int rcv_proto_at (struct ppp *, struct sk_buff *); +static int rcv_proto_vjc_comp (struct ppp *, struct sk_buff *); +static int rcv_proto_vjc_uncomp (struct ppp *, struct sk_buff *); +static int rcv_proto_ccp (struct ppp *, struct sk_buff *); +static int rcv_proto_unknown (struct ppp *, struct sk_buff *); + +static +ppp_proto_type proto_list[] = { + { PPP_IP, rcv_proto_ip }, + { PPP_IPX, rcv_proto_ipx }, + { PPP_AT, rcv_proto_at }, + { PPP_VJC_COMP, rcv_proto_vjc_comp }, + { PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp }, + { PPP_CCP, rcv_proto_ccp }, + { 0, rcv_proto_unknown } /* !!! MUST BE LAST !!! */ +}; -static void -ppp_unesc(struct ppp *ppp, unsigned char *c, int n) +/* + * Called when the PPP network interface device is actually created. + */ +static int +ppp_init_dev (struct device *dev) { - int i; - - for (i = 0; i < n; i++, c++) { - PRINTKN (6,(KERN_DEBUG "(%x)", (unsigned int) *c)); + dev->hard_header_len = PPP_HDRLEN; +#if LINUX_VERSION_CODE < VERSION(2,1,15) + dev->hard_header = ppp_dev_header; + dev->rebuild_header = ppp_dev_rebuild; +#endif -#ifdef CHECK_CHARACTERS - if (*c & 0x80) - sc->sc_flags |= SC_RCV_B7_1; - else - sc->sc_flags |= SC_RCV_B7_0; - - if (paritytab[*c >> 5] & (1 << (*c & 0x1F))) - sc->sc_flags |= SC_RCV_ODDP; - else - sc->sc_flags |= SC_RCV_EVNP; + /* device INFO */ + dev->mtu = PPP_MTU; + dev->hard_start_xmit = ppp_dev_xmit; + dev->open = ppp_dev_open; + dev->stop = ppp_dev_close; + dev->get_stats = ppp_dev_stats; + dev->do_ioctl = ppp_dev_ioctl; + dev->addr_len = 0; + dev->tx_queue_len = 10; + dev->type = ARPHRD_PPP; + +#if LINUX_VERSION_CODE < VERSION(2,1,20) + { + int indx; + + for (indx = 0; indx < DEV_NUMBUFFS; indx++) + skb_queue_head_init (&dev->buffs[indx]); + } +#else + dev_init_buffers(dev); #endif - switch (*c) { - case PPP_ESC: /* PPP_ESC: invert 0x20 in next character */ - ppp->escape = PPP_TRANS; - break; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; - case PPP_FLAG: /* PPP_FLAG: end of frame */ - if (ppp->escape) /* PPP_ESC just before PPP_FLAG is illegal */ - ppp->toss = 0xFF; + return 0; +} - if ((ppp->toss & 0x80) == 0) - ppp_doframe(ppp); /* pass frame on to next layers */ +/* + * Callback from the network layer when the device goes up. + */ - ppp->rcount = 0; - ppp->rhead = ppp->rbuff; - ppp->escape = 0; - ppp->toss = 0; - break; +static int +ppp_dev_open (struct device *dev) +{ + struct ppp *ppp = dev2ppp(dev); - default: /* regular character */ - if (!in_rmap (ppp, *c)) { - if (ppp->toss == 0) - ppp_enqueue (ppp, *c ^ ppp->escape); - ppp->escape = 0; - } - break; - } - } -} + if (!ppp->inuse || ppp2tty(ppp) == NULL) { + printk(KERN_ERR "ppp: %s not active\n", dev->name); + return -ENXIO; + } -#else -static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp, - char *fp, int count) -{ - register struct ppp *ppp = ppp_find (tty); - unsigned char c; - -/* PRINTK( ("PPP: handler called.\n") ); */ - - if (!ppp || ppp->magic != PPP_MAGIC) { - PRINTKN (1,("PPP: handler called but couldn't find " - "PPP struct.\n")); - return; - } - - CHECK_PPP_VOID(); - - if (ppp_debug >= 5) { - ppp_print_buffer ("receive buffer", cp, count, KERNEL_DS); - } - - while (count-- > 0) { - c = *cp++; - - if (fp) { - if (*fp && ppp->toss == 0) - ppp->toss = *fp; - fp++; - } + MOD_INC_USE_COUNT; -#ifdef CHECK_CHARACTERS - if (c & 0x80) - sc->sc_flags |= SC_RCV_B7_1; - else - sc->sc_flags |= SC_RCV_B7_0; - - if (paritytab[c >> 5] & (1 << (c & 0x1F))) - sc->sc_flags |= SC_RCV_ODDP; - else - sc->sc_flags |= SC_RCV_EVNP; -#endif + return 0; +} - switch (c) { - case PPP_ESC: /* PPP_ESC: invert 0x20 in next character */ - ppp->escape = PPP_TRANS; - break; +/* + * Callback from the network layer when the ppp device goes down. + */ - case PPP_FLAG: /* PPP_FLAG: end of frame */ - if (ppp->escape) /* PPP_ESC just before PPP_FLAG is "cancel"*/ - ppp->toss = 0xFF; +static int +ppp_dev_close (struct device *dev) +{ + struct ppp *ppp = dev2ppp (dev); - if ((ppp->toss & 0x80) == 0) - ppp_doframe(ppp); /* pass frame on to next layers */ + CHECK_PPP_MAGIC(ppp); - ppp->rcount = 0; - ppp->rhead = ppp->rbuff; - ppp->escape = 0; - ppp->toss = 0; - break; + MOD_DEC_USE_COUNT; - default: /* regular character */ - if (!in_rmap (ppp, c)) { - if (ppp->toss == 0) - ppp_enqueue (ppp, c ^ ppp->escape); - ppp->escape = 0; - } - } - } + return 0; } -#endif -/* on entry, a received frame is in ppp->rbuff - check it and dispose as appropriate */ -static void -ppp_doframe(struct ppp *ppp) +static inline void +get_vj_stats(struct vjstat *vj, struct slcompress *slc) { - u_char *c = ppp->rbuff; - u_short proto; - int count = ppp->rcount; - - /* forget it if we've already noticed an error */ - if (ppp->toss) { - PRINTKN (1, (KERN_WARNING "ppp_toss: tossing frame, reason = %d\n", - ppp->toss)); - ppp->stats.rerrors++; - return; - } - - /* do this before printing buffer to avoid generating copious output */ - if (count == 0) - return; - - if (ppp_debug >= 3) - ppp_print_buffer ("receive frame", c, count, KERNEL_DS); - - if (count < 4) { - PRINTKN (1,(KERN_WARNING "ppp: got runt ppp frame, %d chars\n", count)); - ppp->stats.runts++; - return; - } - - /* check PPP error detection field */ - if (!ppp_check_fcs(ppp)) { - PRINTKN (1,(KERN_WARNING "ppp: frame with bad fcs\n")); - ppp->stats.rerrors++; - return; - } - - count -= 2; /* ignore last two characters */ - - /* now we have a good frame */ - /* figure out the protocol field */ - if ((c[0] == PPP_ADDRESS) && (c[1] == PPP_CONTROL)) { - c = c + 2; /* ADDR/CTRL not compressed, so skip */ - count -= 2; - } - - proto = (u_short) *c++; /* PROTO compressed */ - if (proto & 1) { - count--; - } else { - proto = (proto << 8) | (u_short) *c++; /* PROTO uncompressed */ - count -= 2; - } - - /* Send the frame to the network if the ppp device is up */ - if ((ppp->dev->flags & IFF_UP) && ppp_do_ip(ppp, proto, c, count)) { - ppp->ddinfo.ip_rjiffies = jiffies; - return; - } - - /* If we got here, it has to go to a user process doing a read, - so queue it. - - User process expects to get whole frame (for some reason), so - use count+2 so as to include FCS field. */ - - if (ppp_us_queue (ppp, proto, c, count+2)) { - ppp->ddinfo.nip_rjiffies = jiffies; - ppp->stats.rothers++; - return; - } - - /* couldn't cope. */ - PRINTKN (1,(KERN_WARNING - "ppp: dropping packet on the floor: nobody could take it.\n")); - ppp->stats.tossed++; + vj->vjs_packets = slc->sls_o_compressed + slc->sls_o_uncompressed; + vj->vjs_compressed = slc->sls_o_compressed; + vj->vjs_searches = slc->sls_o_searches; + vj->vjs_misses = slc->sls_o_misses; + vj->vjs_errorin = slc->sls_i_error; + vj->vjs_tossed = slc->sls_i_tossed; + vj->vjs_uncompressedin = slc->sls_i_uncompressed; + vj->vjs_compressedin = slc->sls_i_compressed; } -/* Examine packet at C, attempt to pass up to net layer. - PROTO is the protocol field from the PPP frame. - Return 1 if could handle it, 0 otherwise. */ - +/* + * Callback from the network layer to process the sockioctl functions. + */ static int -ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c, - int count) +ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd) { - int flags, done; - - PRINTKN (4,(KERN_DEBUG "ppp_do_ip: proto %x len %d first byte %x\n", - (int) proto, count, c[0])); - - if (ppp_debug_netpackets) { - PRINTK (("KERN_DEBUG %s <-- proto %x len %d\n", ppp->dev->name, - (int) proto, count)); - } - - if (proto == PROTO_IP) { - ppp->stats.runcomp++; - goto sendit; - } - - if ((proto == PROTO_VJCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) { - /* get space for uncompressing the header */ - done = 0; - save_flags (flags); - cli(); - if ((ppp->rhead + 80) < ppp->rend) { - ppp->rhead += 80; - ppp->rcount += 80; - done = 1; - } - restore_flags(flags); - - if (! done) { - PRINTKN (1,(KERN_NOTICE - "ppp: no space to decompress VJ compressed TCP header.\n")); - ppp->stats.roverrun++; - return 1; - } - - count = slhc_uncompress(ppp->slcomp, c, count); - if (count <= 0) { - ppp->stats.rerrors++; - PRINTKN (1,(KERN_NOTICE "ppp: error in VJ decompression\n")); - return 1; - } - ppp->stats.rcomp++; - goto sendit; - } - - if ((proto == PROTO_VJUNCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) { - if (slhc_remember(ppp->slcomp, c, count) <= 0) { - ppp->stats.rerrors++; - PRINTKN (1,(KERN_NOTICE "ppp: error in VJ memorizing\n")); - return 1; - } - ppp->stats.runcomp++; - goto sendit; - } - - /* not ours */ - return 0; - - sendit: - if (ppp_debug_netpackets) { - struct iphdr *iph = (struct iphdr *) c; - PRINTK ((KERN_INFO "%s <-- src %lx dst %lx len %d\n", ppp->dev->name, - iph->saddr, iph->daddr, count)) - } - - /* receive the frame through the network software */ - while ((dev_rint(c, count, 0, ppp->dev) & ~1) != 0) - ; - - return 1; -} + struct ppp *ppp = dev2ppp(dev); + int nb; + union { + struct ppp_stats stats; + struct ppp_comp_stats cstats; + char vers[32]; + } u; + + CHECK_PPP_MAGIC(ppp); + + memset(&u, 0, sizeof(u)); + switch (cmd) { + case SIOCGPPPSTATS: + u.stats.p = ppp->stats; + if (ppp->slcomp != NULL) + get_vj_stats(&u.stats.vj, ppp->slcomp); + nb = sizeof(u.stats); + break; + + case SIOCGPPPCSTATS: + if (ppp->sc_xc_state != NULL) + (*ppp->sc_xcomp->comp_stat) + (ppp->sc_xc_state, &u.cstats.c); + if (ppp->sc_rc_state != NULL) + (*ppp->sc_rcomp->decomp_stat) + (ppp->sc_rc_state, &u.cstats.d); + nb = sizeof(u.cstats); + break; + + case SIOCGPPPVER: + strcpy(u.vers, szVersion); + nb = strlen(u.vers) + 1; + break; + + default: + return -EINVAL; + } -/* stuff packet at BUF, length LEN, into the us_rbuff buffer - prepend PROTO information */ + return COPY_TO_USER((void *) ifr->ifr_ifru.ifru_data, &u, nb); +} -#define PUTC(c,label) *ppp->us_rbuff_head++ = c; \ - if (ppp->us_rbuff_head == ppp->us_rbuff_end) \ - ppp->us_rbuff_head = ppp->us_rbuff; \ - if (ppp->us_rbuff_head == ppp->us_rbuff_tail) \ - goto label; -#define GETC(c) c = *ppp->us_rbuff_tail++; \ - if (ppp->us_rbuff_tail == ppp->us_rbuff_end) \ - ppp->us_rbuff_tail = ppp->us_rbuff; +/* + * Process the generic PPP ioctls, i.e. those which are not specific + * to any particular type of hardware link. + */ +static int +ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3) +{ + register int temp_i = 0, oldflags; + int error = -EFAULT; + unsigned long flags; + struct ppp_idle cur_ddinfo; + struct npioctl npi; + + CHECK_PPP(-ENXIO); + + /* + * The user must have an euid of root to do these requests. + */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (param2) { + case PPPIOCSMRU: + /* + * Set the MRU value + */ + if (GET_USER(temp_i, (int *) param3)) + break; + if (temp_i < PPP_MRU) + temp_i = PPP_MRU; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: set mru to %x\n", temp_i); + error = 0; + break; + + case PPPIOCGFLAGS: + /* + * Fetch the current flags + */ + temp_i = ppp->flags & SC_MASK; +#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */ + temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | + SC_RCV_ODDP | SC_RCV_EVNP; +#endif + if (PUT_USER(temp_i, (int *) param3)) + break; + error = 0; + break; + + case PPPIOCSFLAGS: + /* + * Set the flags for the various options + */ + if (GET_USER(temp_i, (int *) param3)) + break; + + if (ppp->flags & ~temp_i & SC_CCP_OPEN) + ppp_ccp_closed(ppp); + + save_flags(flags); + cli(); + oldflags = ppp->flags; + temp_i = (temp_i & SC_MASK) | (oldflags & ~SC_MASK); + ppp->flags = temp_i; + restore_flags(flags); + + if ((oldflags | temp_i) & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: set flags to %x\n", temp_i); + error = 0; + break; + + case PPPIOCSCOMPRESS: + /* + * Set the compression mode + */ + error = ppp_set_compression + (ppp, (struct ppp_option_data *) param3); + break; + + case PPPIOCGUNIT: + /* + * Obtain the unit number for this device. + */ + if (PUT_USER(ppp->line, (int *) param3)) + break; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: get unit: %d\n", ppp->line); + error = 0; + break; + + case PPPIOCSDEBUG: + /* + * Set the debug level + */ + if (GET_USER(temp_i, (int *) param3)) + break; + temp_i = (temp_i & 0x1F) << 16; + + if ((ppp->flags | temp_i) & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: set dbg flags to %x\n", temp_i); + + save_flags(flags); + cli(); + ppp->flags = (ppp->flags & ~0x1F0000) | temp_i; + restore_flags(flags); + error = 0; + break; + + case PPPIOCGDEBUG: + /* + * Get the debug level + */ + temp_i = (ppp->flags >> 16) & 0x1F; + if (PUT_USER(temp_i, (int *) param3)) + break; + error = 0; + break; + + case PPPIOCGIDLE: + /* + * Get the times since the last send/receive frame operation + */ + /* change absolute times to relative times. */ + cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ; + cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ; + if (COPY_TO_USER((void *) param3, &cur_ddinfo, + sizeof (cur_ddinfo))) + break; + error = 0; + break; + + case PPPIOCSMAXCID: + /* + * Set the maximum VJ header compression slot number. + */ + if (GET_USER(temp_i, (int *) param3)) + break; + error = -EINVAL; + if (temp_i < 2 || temp_i > 255) + break; + ++temp_i; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "ppp_ioctl: set maxcid to %d\n", + temp_i); + if (ppp->slcomp != NULL) + slhc_free(ppp->slcomp); + ppp->slcomp = slhc_init(16, temp_i); + + error = -ENOMEM; + if (ppp->slcomp == NULL) { + printk(KERN_ERR "ppp: no memory for VJ compression\n"); + break; + } + error = 0; + break; + + case PPPIOCGNPMODE: + case PPPIOCSNPMODE: + if (COPY_FROM_USER(&npi, (void *) param3, sizeof(npi))) + break; + + switch (npi.protocol) { + case PPP_IP: + npi.protocol = NP_IP; + break; + case PPP_IPX: + npi.protocol = NP_IPX; + break; + case PPP_AT: + npi.protocol = NP_AT; + break; + default: + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "pppioc[gs]npmode: " + "invalid proto %d\n", npi.protocol); + error = -EINVAL; + goto out; + } + + if (param2 == PPPIOCGNPMODE) { + npi.mode = ppp->sc_npmode[npi.protocol]; + if (COPY_TO_USER((void *) param3, &npi, sizeof(npi))) + break; + } else { + ppp->sc_npmode[npi.protocol] = npi.mode; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "ppp: set np %d to %d\n", + npi.protocol, npi.mode); + mark_bh(NET_BH); + } + error = 0; + break; + + default: + /* + * All other ioctl() events will come here. + */ + if (ppp->flags & SC_DEBUG) + printk(KERN_ERR + "ppp_ioctl: invalid ioctl: %x, addr %lx\n", + param2, param3); + + error = -ENOIOCTLCMD; + break; + } +out: + return error; +} +/* + * Process the set-compression ioctl. + */ static int -ppp_us_queue(struct ppp *ppp, unsigned short proto, - unsigned char *buf, int len) +ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp) { - int totlen; - unsigned char *saved_head; + struct compressor *cp; + int error, nb; + unsigned long flags; + __u8 *ptr; + __u8 ccp_option[CCP_MAX_OPTION_LENGTH]; + struct ppp_option_data data; + + /* + * Fetch the compression parameters + */ + if (COPY_FROM_USER(&data, odp, sizeof (data))) + return -EFAULT; + + nb = data.length; + ptr = data.ptr; + if ((unsigned) nb >= CCP_MAX_OPTION_LENGTH) + nb = CCP_MAX_OPTION_LENGTH; + + if (COPY_FROM_USER(ccp_option, ptr, nb)) + return -EFAULT; + + if (ccp_option[1] < 2) /* preliminary check on the length byte */ + return -EINVAL; + + save_flags(flags); + cli(); + ppp->flags &= ~(data.transmit? SC_COMP_RUN: SC_DECOMP_RUN); + restore_flags(flags); + + cp = find_compressor (ccp_option[0]); +#if defined(CONFIG_KMOD) || defined(CONFIG_KERNELD) + if (cp == NULL) { + char modname[32]; + sprintf(modname, "ppp-compress-%d", ccp_option[0]); + request_module(modname); + cp = find_compressor(ccp_option[0]); + } +#endif /* CONFIG_KMOD */ + + if (cp == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG + "%s: no compressor for [%x %x %x], %x\n", + ppp->name, ccp_option[0], ccp_option[1], + ccp_option[2], nb); + return -EINVAL; /* compressor not loaded */ + } - totlen = len+2; /* including protocol */ + /* + * Found a handler for the protocol - try to allocate + * a compressor or decompressor. + */ + error = 0; + if (data.transmit) { + if (ppp->sc_xc_state != NULL) + (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state); + ppp->sc_xc_state = NULL; + + ppp->sc_xcomp = cp; + ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb); + + if (ppp->sc_xc_state == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp_alloc failed\n", + ppp->name); + error = -ENOBUFS; + } + } else { + if (ppp->sc_rc_state != NULL) + (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state); + ppp->sc_rc_state = NULL; + + ppp->sc_rcomp = cp; + ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb); + if (ppp->sc_rc_state == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp_alloc failed\n", + ppp->name); + error = -ENOBUFS; + } + } - if (set_bit(1, &ppp->us_rbuff_lock)) { - PRINTKN (1, (KERN_NOTICE "ppp_us_queue: can't get lock\n")); - return 0; - } - saved_head = ppp->us_rbuff_head; + return error; +} - PUTC((totlen & 0xff00) >> 8, failure); - PUTC(totlen & 0x00ff, failure); - PUTC((proto & 0xff00) >> 8, failure); - PUTC(proto & 0x00ff, failure); +/* + * Handle a CCP packet. + * + * The CCP packet is passed along to the pppd process just like any + * other PPP frame. The difference is that some processing needs to be + * immediate or the compressors will become confused on the peer. + */ - while (len-- > 0) { - PUTC(*buf++, failure); - } +static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd) +{ + int slen = CCP_LENGTH(dp); + __u8 *opt = dp + CCP_HDRLEN; + int opt_len = slen - CCP_HDRLEN; + unsigned long flags; - PRINTKN (3, (KERN_INFO "ppp: successfully queued %d bytes\n", totlen)); - clear_bit(1, &ppp->us_rbuff_lock); - wake_up_interruptible (&ppp->read_wait); + if (slen > len) + return; -#ifdef NEW_TTY_DRIVERS - kill_fasync(ppp->tty->fasync, SIGIO); -#endif + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "ppp_proto_ccp rcvd=%d code=%x flags=%x\n", + rcvd, CCP_CODE(dp), ppp->flags); + save_flags(flags); + switch (CCP_CODE(dp)) { + case CCP_CONFREQ: + case CCP_TERMREQ: + case CCP_TERMACK: + /* + * CCP must be going down - disable compression + */ + if (ppp->flags & SC_CCP_UP) { + cli(); + ppp->flags &= ~(SC_CCP_UP | + SC_COMP_RUN | + SC_DECOMP_RUN); + } + break; + + case CCP_CONFACK: + if ((ppp->flags & SC_CCP_OPEN) == 0) + break; + if (ppp->flags & SC_CCP_UP) + break; + if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN)) + break; + if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN)) + break; + if (!rcvd) { + /* + * we're agreeing to send compressed packets. + */ + if (ppp->sc_xc_state == NULL) + break; + + if ((*ppp->sc_xcomp->comp_init) + (ppp->sc_xc_state, + opt, opt_len, + ppp->line, 0, ppp->flags & SC_DEBUG)) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp running\n", + ppp->name); + cli(); + ppp->flags |= SC_COMP_RUN; + } + break; + } + + /* + * peer is agreeing to send compressed packets. + */ + if (ppp->sc_rc_state == NULL) + break; + + if ((*ppp->sc_rcomp->decomp_init) + (ppp->sc_rc_state, + opt, opt_len, + ppp->line, 0, ppp->mru, ppp->flags & SC_DEBUG)) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp running\n", + ppp->name); + cli(); + ppp->flags |= SC_DECOMP_RUN; + ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR); + } + break; + + case CCP_RESETACK: + /* + * CCP Reset-ack resets compressors and decompressors + * as it passes through. + */ + if ((ppp->flags & SC_CCP_UP) == 0) + break; + + if (!rcvd) { + if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) { + (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp reset\n", + ppp->name); + } + } else { + if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) { + (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp reset\n", + ppp->name); + cli(); + ppp->flags &= ~SC_DC_ERROR; + } + } + break; + } + restore_flags(flags); +} - if (ppp->inp_sig && ppp->inp_sig_pid) - if (kill_proc (ppp->inp_sig_pid, ppp->inp_sig, 1) != 0) { - /* process is gone */ - PRINTKN (2,(KERN_NOTICE - "ppp: process that requested notification is gone\n")); - ppp->inp_sig = 0; - ppp->inp_sig_pid = 0; - } - return 1; +/* + * CCP is down; free (de)compressor state if necessary. + */ - failure: - ppp->us_rbuff_head = saved_head; - clear_bit(1, &ppp->us_rbuff_lock); +static void +ppp_ccp_closed(struct ppp *ppp) +{ + unsigned long flags; - PRINTKN (1, (KERN_NOTICE "ppp_us_queue: ran out of buffer space.\n")); + save_flags(flags); + cli(); + ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN); + restore_flags(flags); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: ccp closed\n", ppp->name); + if (ppp->sc_xc_state) { + (*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state); + ppp->sc_xc_state = NULL; + } - return 0; + if (ppp->sc_rc_state) { + (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state); + ppp->sc_rc_state = NULL; + } } /************************************************************* - * LINE DISCIPLINE SUPPORT - * The following functions form support user programs - * which read and write data on a TTY with the PPP line - * discipline. Reading is done from a circular queue, - * filled by the lower TTY levels. + * RECEIVE-SIDE ROUTINES *************************************************************/ -/* read a PPP frame from the us_rbuff circular buffer, - waiting if necessary -*/ - +/* + * On entry, a received frame is in skb. + * Check it and dispose as appropriate. + */ static int -ppp_read(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr) +ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb) { - struct ppp *ppp = ppp_find(tty); - unsigned char c; - int len, i; - - if (!ppp || ppp->magic != PPP_MAGIC) { - PRINTKN (1,(KERN_ERR "ppp_read: cannnot find ppp channel\n")); - return -EIO; - } - - CHECK_PPP(-ENXIO); - - PRINTKN (4,(KERN_DEBUG "ppp_read: called %x num %u\n", - (unsigned int) buf, - nr)); - - do { - /* try to acquire read lock */ - if (set_bit(0, &ppp->us_rbuff_lock) == 0) { - /* got lock */ - if (ppp->us_rbuff_head == ppp->us_rbuff_tail) { - /* no data */ - PRINTKN (4,(KERN_DEBUG "ppp_read: no data\n")); - clear_bit(0, &ppp->us_rbuff_lock); - if (ppp->inp_sig) { - PRINTKN (4,(KERN_DEBUG "ppp_read: EWOULDBLOCK\n")); - return -EWOULDBLOCK; - } else goto wait; - } - - /* reset the time of the last read operation */ - ppp->ddinfo.nip_rjiffies = jiffies; - - GETC (c); len = c << 8; GETC (c); len += c; - - PRINTKN (4,(KERN_DEBUG "ppp_read: len = %d\n", len)); - - if (len + 2 > nr) { - /* frame too big; can't copy it, but do update us_rbuff_head */ - PRINTKN (1,(KERN_DEBUG - "ppp: read of %u bytes too small for %d frame\n", - nr, len+2)); - ppp->us_rbuff_head += len; - if (ppp->us_rbuff_head > ppp->us_rbuff_end) - ppp->us_rbuff_head += - (ppp->us_rbuff_end - ppp->us_rbuff); - clear_bit(0, &ppp->us_rbuff_lock); - wake_up_interruptible (&ppp->read_wait); - ppp->stats.rgiants++; - return -EOVERFLOW; /* ZZZ; HACK! */ - } else { - /* have the space: copy the packet, faking the first two bytes */ - put_fs_byte (PPP_ADDRESS, buf++); - put_fs_byte (PPP_CONTROL, buf++); - i = len; - while (i-- > 0) { - GETC (c); - put_fs_byte (c, buf++); - } - } - - clear_bit(0, &ppp->us_rbuff_lock); - PRINTKN (3,(KERN_DEBUG "ppp_read: passing %d bytes up\n", len + 2)); - ppp->stats.rothers++; - return len + 2; - } - - /* need to wait */ - wait: - current->timeout = 0; - PRINTKN (3,(KERN_DEBUG "ppp_read: sleeping\n")); - interruptible_sleep_on (&ppp->read_wait); - if (current->signal & ~current->blocked) - return -EINTR; - } while (1); + __u8 *data; + int count; + int proto; + int new_count; + struct sk_buff *new_skb; + ppp_proto_type *proto_ptr; + + /* + * An empty frame is ignored. This occurs if the FLAG sequence + * precedes and follows each frame. + */ + if (skb == NULL) + return 1; + if (skb->len == 0) { + kfree_skb(skb); + return 1; + } + data = skb->data; + count = skb->len; + + /* + * Generate an error if the frame is too small. + */ + if (count < PPP_HDRLEN + 2) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG + "ppp: got runt ppp frame, %d chars\n", count); + ++ppp->estats.rx_length_errors; + return 0; + } + + /* + * Verify the FCS of the frame and discard the FCS characters + * from the end of the buffer. + */ + if (ppp->rfcs != PPP_GOODFCS) { + if (ppp->flags & SC_DEBUG) { + printk(KERN_DEBUG + "ppp: frame with bad fcs, length = %d\n", + count); + ppp_print_buffer("bad frame", data, count); + } + ++ppp->estats.rx_crc_errors; + return 0; + } + count -= 2; /* ignore the fcs characters */ + skb_trim(skb, count); + + /* + * Process the active decompressor. + */ + if (ppp->sc_rc_state != NULL && + (ppp->flags & SC_DECOMP_RUN) && + ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) { + if (PPP_PROTOCOL(data) == PPP_COMP) { + /* + * If the frame is compressed then decompress it. + */ + new_skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN); + if (new_skb == NULL) { + printk(KERN_ERR "ppp_recv_frame: no memory\n"); + new_count = DECOMP_ERROR; + } else { + new_count = (*ppp->sc_rcomp->decompress) + (ppp->sc_rc_state, data, count, + new_skb->data, ppp->mru + PPP_HDRLEN); + } + if (new_count > 0) { + /* Frame was decompressed OK */ + kfree_skb(skb); + skb = new_skb; + count = new_count; + data = skb_put(skb, count); + + } else { + /* + * On a decompression error, we pass the + * compressed frame up to pppd as an + * error indication. + */ + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "%s: decomp err %d\n", + ppp->name, new_count); + if (new_skb != 0) + kfree_skb(new_skb); + if (ppp->slcomp != 0) + slhc_toss(ppp->slcomp); + ++ppp->stats.ppp_ierrors; + if (new_count == DECOMP_FATALERROR) { + ppp->flags |= SC_DC_FERROR; + } else { + ppp->flags |= SC_DC_ERROR; + } + } + + + } else { + /* + * The frame is not compressed. Pass it to the + * decompression code so it can update its + * dictionary if necessary. + */ + (*ppp->sc_rcomp->incomp)(ppp->sc_rc_state, + data, count); + } + } + else if (PPP_PROTOCOL(data) == PPP_COMP && (ppp->flags & SC_DEBUG)) + printk(KERN_INFO "%s: not decomp, rc_state=%p flags=%x\n", + ppp->name, ppp->sc_rc_state, ppp->flags); + + /* + * Count the frame and print it + */ + ++ppp->stats.ppp_ipackets; + ppp->stats.ppp_ioctects += count; + if (ppp->flags & SC_LOG_INPKT) + ppp_print_buffer ("receive frame", data, count); + + /* + * Find the procedure to handle this protocol. + * The last one is marked as protocol 0 which is the 'catch-all' + * to feed it to the pppd daemon. + */ + proto = PPP_PROTOCOL(data); + proto_ptr = proto_list; + while (proto_ptr->proto != 0 && proto_ptr->proto != proto) + ++proto_ptr; + + /* + * Update the appropriate statistic counter. + */ + if (!(*proto_ptr->func)(ppp, skb)) { + kfree_skb(skb); + ++ppp->stats.ppp_discards; + } + + return 1; } -/* stuff a character into the transmit buffer, using PPP's way of escaping - special characters. - also, update ppp->fcs to take account of new character */ -static inline void -ppp_stuff_char(struct ppp *ppp, unsigned char c) +/* + * An input error has been detected, so we need to inform + * the VJ decompressor. + */ +static void +ppp_receive_error(struct ppp *ppp) { - int curpt = ppp->xhead - ppp->xbuff; - if ((curpt < 0) || (curpt > 3000)) { - PRINTK ((KERN_DEBUG "ppp_stuff_char: %x %x %d\n", - (unsigned int) ppp->xbuff, (unsigned int) ppp->xhead, curpt)) - } - if (in_xmap (ppp, c)) { - *ppp->xhead++ = PPP_ESC; - *ppp->xhead++ = c ^ PPP_TRANS; - } else - *ppp->xhead++ = c; - ppp->fcs = (ppp->fcs >> 8) ^ fcstab[(ppp->fcs ^ c) & 0xff]; -} + CHECK_PPP_VOID(); -/* write a frame with NR chars from BUF to TTY - we have to put the FCS field on ourselves -*/ + if (ppp->slcomp != 0) + slhc_toss(ppp->slcomp); +} +/* + * Put the input frame into the networking system for the indicated protocol + */ static int -ppp_write(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr) +ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb) { - struct ppp *ppp = ppp_find(tty); - int i; - - if (!ppp || ppp->magic != PPP_MAGIC) { - PRINTKN (1,(KERN_ERR "ppp_write: cannot find ppp unit\n")); - return -EIO; - } - - CHECK_PPP(-ENXIO); - - if (ppp->mtu != ppp->dev->mtu) /* Someone has been ifconfigging */ - ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru); - - if (nr > ppp->mtu) { - PRINTKN (1,(KERN_WARNING - "ppp_write: truncating user packet from %u to mtu %d\n", - nr, ppp->mtu)); - nr = ppp->mtu; - } - - if (ppp_debug >= 3) - ppp_print_buffer ("write frame", buf, nr, USER_DS); - - /* lock this PPP unit so we will be the only writer; - sleep if necessary */ - while ((ppp->sending == 1) || !ppp_lock(ppp)) { - current->timeout = 0; - PRINTKN (3,(KERN_DEBUG "ppp_write: sleeping\n")); - interruptible_sleep_on(&ppp->write_wait); - if (current->signal & ~current->blocked) - return -EINTR; - } - - /* OK, locked. Stuff the given bytes into the buffer. */ - - PRINTKN(4,(KERN_DEBUG "ppp_write: acquired write lock\n")); - ppp->xhead = ppp->xbuff; - -#ifdef OPTIMIZE_FLAG_TIME - if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME) - *ppp->xhead++ = PPP_FLAG; - ppp->last_xmit = jiffies; -#else - *ppp->xhead++ = PPP_FLAG; -#endif - - ppp->fcs = PPP_FCS_INIT; - i = nr; - while (i-- > 0) - ppp_stuff_char(ppp,get_fs_byte(buf++)); - - ppp_add_fcs(ppp); /* concatenate FCS at end */ - *ppp->xhead++ = PPP_FLAG; - - /* reset the time of the last write operation */ - ppp->ddinfo.nip_sjiffies = jiffies; + /* + * Fill in a few fields of the skb and give it to netif_rx(). + */ + skb->dev = ppp2dev(ppp); /* We are the device */ + skb->protocol = htons(proto); + skb->mac.raw = skb->data; + skb_pull(skb, PPP_HDRLEN); /* pull off ppp header */ + ppp->last_recv = jiffies; +#if LINUX_VERSION_CODE < VERSION(2,1,15) + skb->free = 1; +#endif + netif_rx (skb); + return 1; +} - if (ppp_debug >= 6) - ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS); - else { - PRINTKN (4,(KERN_DEBUG - "ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff)); - } +/* + * Process the receipt of an IP frame + */ +static int +rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0) + && ppp->sc_npmode[NP_IP] == NPMODE_PASS) + return ppp_rcv_rx(ppp, ETH_P_IP, skb); + return 0; +} - /* packet is ready-to-go */ - ++ppp->stats.sothers; - ppp_kick_tty(ppp); +/* + * Process the receipt of an IPX frame + */ +static int +rcv_proto_ipx(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + if (((ppp2dev(ppp)->flags & IFF_UP) != 0) && (skb->len > 0) + && ppp->sc_npmode[NP_IPX] == NPMODE_PASS) + return ppp_rcv_rx(ppp, ETH_P_IPX, skb); + return 0; +} - return((int)nr); +/* + * Process the receipt of an Appletalk frame + */ +static int +rcv_proto_at(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0) + && ppp->sc_npmode[NP_AT] == NPMODE_PASS) + return ppp_rcv_rx(ppp, ETH_P_PPPTALK, skb); + return 0; } - + +/* + * Process the receipt of an VJ Compressed frame + */ static int -ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i, - unsigned long l) +rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb) { - struct ppp *ppp = ppp_find(tty); - register int temp_i = 0; - int error; - - if (!ppp || ppp->magic != PPP_MAGIC) { - PRINTK ((KERN_ERR "ppp_ioctl: can't find PPP block from tty!\n")) - return -EBADF; - } - - CHECK_PPP(-ENXIO); - - /* This must be root user */ - if (!suser()) - return -EPERM; - - switch (i) { - case PPPIOCSMRU: - error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i)); - if (error == 0) { - PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %x\n", temp_i)); - temp_i = (int) get_fs_long (l); - if (ppp->mru != temp_i) - ppp_changedmtu (ppp, ppp->mtu, temp_i); - } - break; - - case PPPIOCGFLAGS: - error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i)); - if (error == 0) { - temp_i = (ppp->flags & SC_MASK); -#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */ - temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | SC_RCV_ODDP | SC_RCV_EVNP; -#endif - put_fs_long ((long) temp_i, l); - PRINTKN (3,(KERN_DEBUG "ppp_ioctl: get flags: addr %lx flags %x\n", - l, - temp_i)); - } - break; - - case PPPIOCSFLAGS: - error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i)); - if (error == 0) { - temp_i = (int) get_fs_long (l); - ppp->flags ^= ((ppp->flags ^ temp_i) & SC_MASK); - PRINTKN (3,(KERN_INFO "ppp_ioctl: set flags to %x\n", temp_i)); - } - break; - - case PPPIOCGASYNCMAP: - error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i)); - if (error == 0) { - put_fs_long (ppp->xmit_async_map[0], l); - PRINTKN (3,(KERN_INFO "ppp_ioctl: get asyncmap: addr %lx asyncmap %lx\n", - l, ppp->xmit_async_map[0])); - } - break; - - case PPPIOCSASYNCMAP: - error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i)); - if (error == 0) { - memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map)); - ppp->xmit_async_map[0] = get_fs_long (l); - bset (ppp->xmit_async_map, PPP_FLAG); - bset (ppp->xmit_async_map, PPP_ESC); - PRINTKN (3,(KERN_INFO "ppp_ioctl: set xmit asyncmap %lx\n", - ppp->xmit_async_map[0])); - } - break; - - case PPPIOCRASYNCMAP: - error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i)); - if (error == 0) { - ppp->recv_async_map = get_fs_long (l); - PRINTKN (3,(KERN_INFO "ppp_ioctl: set recv asyncmap %lx\n", - ppp->recv_async_map)); - } - break; - - case PPPIOCGUNIT: - error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i)); - if (error == 0) { - put_fs_long (ppp->dev->base_addr, l); - PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %d", ppp->dev->base_addr)); - } - break; - - case PPPIOCSINPSIG: - error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i)); - if (error == 0) { - ppp->inp_sig = (int) get_fs_long (l); - ppp->inp_sig_pid = current->pid; - PRINTKN (3,(KERN_INFO "ppp_ioctl: set input signal %d\n", ppp->inp_sig)); - } - break; - - case PPPIOCSDEBUG: - error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i)); - if (error == 0) { - ppp_debug = (int) get_fs_long (l); - ppp_debug_netpackets = (ppp_debug & 0xff00) >> 8; - ppp_debug &= 0xff; - PRINTKN (1, (KERN_INFO "ppp_ioctl: set debug level %d, netpacket %d\n", - ppp_debug, ppp_debug_netpackets)); - } - break; - - case PPPIOCGDEBUG: - error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i)); - if (error == 0) { - put_fs_long ((long) (ppp_debug | (ppp_debug_netpackets << 8)), l); - PRINTKN (3,(KERN_INFO "ppp_ioctl: get debug level %d\n", - ppp_debug | (ppp_debug_netpackets << 8))); - } - break; - - case PPPIOCGSTAT: - error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_stats)); - if (error == 0) { - memcpy_tofs ((void *) l, &ppp->stats, sizeof (struct ppp_stats)); - PRINTKN (3,(KERN_INFO "ppp_ioctl: read statistics\n")); - } - break; - - case PPPIOCGTIME: - error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_ddinfo)); - if (error == 0) { - struct ppp_ddinfo cur_ddinfo; - unsigned long cur_jiffies = jiffies; - - /* change absolute times to relative times. */ - cur_ddinfo.ip_sjiffies = cur_jiffies - ppp->ddinfo.ip_sjiffies; - cur_ddinfo.ip_rjiffies = cur_jiffies - ppp->ddinfo.ip_rjiffies; - cur_ddinfo.nip_sjiffies = cur_jiffies - ppp->ddinfo.nip_sjiffies; - cur_ddinfo.nip_rjiffies = cur_jiffies - ppp->ddinfo.nip_rjiffies; - - memcpy_tofs ((void *) l, &cur_ddinfo, sizeof (struct ppp_ddinfo)); - PRINTKN (3,(KERN_INFO "ppp_ioctl: read demand dial info\n")); - } - break; - - case PPPIOCGXASYNCMAP: - error = verify_area (VERIFY_WRITE, - (void *) l, - sizeof (ppp->xmit_async_map)); - if (error == 0) { - memcpy_tofs ((void *) l, - ppp->xmit_async_map, - sizeof (ppp->xmit_async_map)); - PRINTKN (3,(KERN_INFO "ppp_ioctl: get xasyncmap: addr %lx\n", l)); - } - break; - - case PPPIOCSXASYNCMAP: - error = verify_area (VERIFY_READ, (void *) l, - sizeof (ppp->xmit_async_map)); - if (error == 0) { - unsigned long temp_tbl [8]; - - memcpy_fromfs (temp_tbl, (void *) l, sizeof (ppp->xmit_async_map)); - temp_tbl[1] = 0x00000000; /* must not escape 0x20 - 0x3f */ - temp_tbl[2] &= ~0x40000000; /* must not escape 0x5e */ - temp_tbl[3] |= 0x60000000; /* must escape 0x7d and 0x7e */ - - if ((temp_tbl[2] & temp_tbl[3]) != 0 || - (temp_tbl[4] & temp_tbl[5]) != 0 || - (temp_tbl[6] & temp_tbl[7]) != 0) - error = -EINVAL; - else { - memcpy (ppp->xmit_async_map, temp_tbl, sizeof (ppp->xmit_async_map)); - PRINTKN (3,(KERN_INFO "ppp_ioctl: set xasyncmap\n")); - } - } - break; - - case PPPIOCSMAXCID: - error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i)); - if (error == 0) { - temp_i = (int) get_fs_long (l) + 1; - PRINTKN (3,(KERN_INFO "ppp_ioctl: set maxcid to %d\n", temp_i)); - if (ppp->slcomp != NULL) - slhc_free (ppp->slcomp); - - ppp->slcomp = slhc_init (temp_i, temp_i); - - if (ppp->slcomp == NULL) { - PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n")); - ppp_release (ppp); - error = -ENOMEM; - } - } - break; - -#ifdef NEW_TTY_DRIVERS - /* Allow stty to read, but not set, the serial port */ - case TCGETS: - case TCGETA: - error = n_tty_ioctl(tty, file, i, l); - break; -#endif + int new_count; + + CHECK_PPP(0); + if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL) + return 0; + new_count = slhc_uncompress(ppp->slcomp, skb->data + PPP_HDRLEN, + skb->len - PPP_HDRLEN); + if (new_count < 0) { + if (ppp->flags & SC_DEBUG) + printk(KERN_NOTICE + "ppp: error in VJ decompression\n"); + return 0; + } + skb_put(skb, new_count + PPP_HDRLEN - skb->len); + return rcv_proto_ip(ppp, skb); +} /* - * All other ioctl() events will come here. + * Process the receipt of an VJ Un-compressed frame */ +static int +rcv_proto_vjc_uncomp(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL) + return 0; + if (slhc_remember(ppp->slcomp, skb->data + PPP_HDRLEN, + skb->len - PPP_HDRLEN) <= 0) { + if (ppp->flags & SC_DEBUG) + printk(KERN_NOTICE "ppp: error in VJ memorizing\n"); + return 0; + } + return rcv_proto_ip(ppp, skb); +} - default: - PRINTKN (1,(KERN_ERR "ppp_ioctl: invalid ioctl: %x, addr %lx\n", - i, - l)); -#ifdef NEW_TTY_DRIVERS - error = -ENOIOCTLCMD; -#else - error = -EINVAL; -#endif - break; - } - return error; +static int +rcv_proto_ccp(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + ppp_proto_ccp (ppp, skb->data + PPP_HDRLEN, skb->len - PPP_HDRLEN, 1); + return rcv_proto_unknown(ppp, skb); } +/* + * Receive all unclassified protocols. + */ static int -ppp_select (struct tty_struct *tty, struct inode * inode, - struct file * filp, int sel_type, select_table * wait) +rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb) { - struct ppp *ppp = ppp_find (tty); - - if (!ppp || ppp->magic != PPP_MAGIC) { - PRINTK ((KERN_ERR "ppp_select: can't find PPP block from tty!\n")) - return -EBADF; - } - - /* If the PPP protocol is no longer active, return false */ - CHECK_PPP (0); - - /* Process the request based upon the type desired */ - switch (sel_type) { - case SEL_IN: - if (set_bit(0, &ppp->us_rbuff_lock) == 0) { - /* Test for the presence of data in the queue */ - if (ppp->us_rbuff_head != ppp->us_rbuff_tail) { - clear_bit (0, &ppp->us_rbuff_lock); - return 1; - } - clear_bit (0, &ppp->us_rbuff_lock); - } /* fall through */ - - case SEL_EX: - /* Is there a pending error condition? */ - if (tty->packet && tty->link->ctrl_status) - return 1; - - /* closed? */ - if (tty->flags & (1 << TTY_SLAVE_CLOSED)) - return 1; - - /* If the tty is disconnected, then this is an exception too */ - if (tty_hung_up_p(filp)) - return 1; - - select_wait (&ppp->read_wait, wait); - break; - - case SEL_OUT: - if (ppp_lock (ppp)) { - if (ppp->sending == 0) { - ppp_unlock (ppp); + CHECK_PPP(0); + + /* + * Limit queue length by dropping old frames. + */ + skb_queue_tail(&ppp->rcv_q, skb); + while (ppp->rcv_q.qlen > PPP_MAX_RCV_QLEN) { + struct sk_buff *skb = skb_dequeue(&ppp->rcv_q); + if (skb) + kfree_skb(skb); + } + + wake_up_interruptible (&ppp->read_wait); + if (ppp->tty->fasync != NULL) + kill_fasync (ppp->tty->fasync, SIGIO); + return 1; - } - ppp_unlock (ppp); - } - select_wait (&ppp->write_wait, wait); - break; - } - return 0; } /************************************************************* - * NETWORK OUTPUT - * This routine accepts requests from the network layer - * and attempts to deliver the packets. - * It also includes various routines we are compelled to - * have to make the network layer work (arp, etc...). + * TRANSMIT-SIDE ROUTINES *************************************************************/ -int -ppp_xmit(struct sk_buff *skb, struct device *dev) +/* local function to store a value into the LQR frame */ +extern inline __u8 * store_long (register __u8 *p, register int value) { + *p++ = (__u8) (value >> 24); + *p++ = (__u8) (value >> 16); + *p++ = (__u8) (value >> 8); + *p++ = (__u8) value; + return p; +} + +/* + * Compress and send an frame to the peer. + * Should be called with dev->tbusy == 1, having been set by the caller. + * That is, we use dev->tbusy as a lock to prevent reentry of this + * procedure. + */ +static void +ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) { - struct tty_struct *tty; - struct ppp *ppp; - unsigned char *p; - unsigned short proto; - int len; - - /* just a little sanity check. */ - if (skb == NULL) { - PRINTKN(3,(KERN_WARNING "ppp_xmit: null packet!\n")); - return 0; - } - - /* Get pointers to the various components */ - ppp = &ppp_ctrl[dev->base_addr]; - tty = ppp->tty; - p = (unsigned char *) (skb + 1); - len = skb->len; - proto = PROTO_IP; - - PRINTKN(4,(KERN_DEBUG "ppp_xmit [%s]: skb %lX busy %d\n", dev->name, - (unsigned long int) skb, ppp->sending)); - - CHECK_PPP(0); - - if (tty == NULL) { - PRINTKN(1,(KERN_ERR "ppp_xmit: %s not connected to a TTY!\n", dev->name)); - goto done; - } - - if (!(dev->flags & IFF_UP)) { - PRINTKN(1,(KERN_WARNING - "ppp_xmit: packet sent on interface %s, which is down for IP\n", - dev->name)); - goto done; - } - - /* get length from IP header as per Alan Cox bugfix for slip.c */ - if (len < sizeof(struct iphdr)) { - PRINTKN(0,(KERN_ERR "ppp_xmit: given runt packet, ignoring\n")); - return 1; - } - len = ntohs( ((struct iphdr *)(skb->data)) -> tot_len ); - - /* If doing demand dial then divert the first frame to pppd. */ - if (ppp->flags & SC_IP_DOWN) { - if (ppp->flags & SC_IP_FLUSH == 0) { - if (ppp_us_queue (ppp, proto, p, len)) - ppp->flags |= SC_IP_FLUSH; - } - goto done; - } - - /* Attempt to acquire send lock */ - if (ppp->sending || !ppp_lock(ppp)) { - PRINTKN(3,(KERN_WARNING "ppp_xmit: busy\n")); - ppp->stats.sbusy++; - return 1; - } - - ppp->xhead = ppp->xbuff; - - /* try to compress, if VJ compression mode is on */ - if (ppp->flags & SC_COMP_TCP) { - /* NOTE: last 0 argument says never to compress connection ID */ - len = slhc_compress(ppp->slcomp, p, len, ppp->cbuff, &p, 0); - if (p[0] & SL_TYPE_COMPRESSED_TCP) - proto = PROTO_VJCOMP; - else { - if (p[0] >= SL_TYPE_UNCOMPRESSED_TCP) { - proto = PROTO_VJUNCOMP; - p[0] = (p[0] & 0x0f) | 0x40; - } - } - } - - /* increment appropriate counter */ - if (proto == PROTO_VJCOMP) - ++ppp->stats.scomp; - else - ++ppp->stats.suncomp; - - if (ppp_debug_netpackets) { - struct iphdr *iph = (struct iphdr *) (skb + 1); - PRINTK ((KERN_DEBUG "%s ==> proto %x len %d src %x dst %x proto %d\n", - dev->name, (int) proto, (int) len, (int) iph->saddr, - (int) iph->daddr, (int) iph->protocol)) - } - - /* start of frame: FLAG ALL_STATIONS CONTROL */ -#ifdef OPTIMIZE_FLAG_TIME - if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME) - *ppp->xhead++ = PPP_FLAG; - ppp->last_xmit = jiffies; -#else - *ppp->xhead++ = PPP_FLAG; -#endif + int proto; + __u8 *data; + int count; + __u8 *p; + int ret; + + CHECK_PPP_VOID(); + data = skb->data; + count = skb->len; + + /* dump the buffer */ + if (ppp->flags & SC_LOG_OUTPKT) + ppp_print_buffer ("write frame", data, count); + + /* + * Handle various types of protocol-specific compression + * and other processing, including: + * - VJ TCP header compression + * - updating LQR packets + * - updating CCP state on CCP packets + */ + proto = PPP_PROTOCOL(data); + switch (proto) { + case PPP_IP: + if ((ppp->flags & SC_COMP_TCP) && ppp->slcomp != NULL) + skb = ppp_vj_compress(ppp, skb); + break; + + case PPP_LQR: + /* + * Update the LQR frame with the current MIB information. + * This way the information is accurate and up-to-date. + */ + if (count < 48) + break; + p = data + 40; /* Point to last two items. */ + p = store_long(p, ppp->stats.ppp_opackets + 1); + p = store_long(p, ppp->stats.ppp_ooctects + count); + ++ppp->stats.ppp_olqrs; + break; + + case PPP_CCP: + /* + * Outbound compression control frames + */ + ppp_proto_ccp(ppp, data + PPP_HDRLEN, count - PPP_HDRLEN, 0); + break; + } + data = skb->data; + count = skb->len; + + /* + * Compress the whole frame if possible. + */ + if (((ppp->flags & SC_COMP_RUN) != 0) && + (ppp->sc_xc_state != (void *) 0) && + (proto != PPP_LCP) && + (proto != PPP_CCP)) { + struct sk_buff *new_skb; + int new_count; + + /* Allocate an skb for the compressed frame. */ + new_skb = alloc_skb(ppp->mtu + PPP_HDRLEN, GFP_ATOMIC); + if (new_skb == NULL) { + printk(KERN_ERR "ppp_send_frame: no memory\n"); + kfree_skb(skb); + ppp->dev.tbusy = 0; + return; + } + + /* Compress the frame. */ + new_count = (*ppp->sc_xcomp->compress) + (ppp->sc_xc_state, data, new_skb->data, + count, ppp->mtu + PPP_HDRLEN); + + /* Did it compress? */ + if (new_count > 0 && (ppp->flags & SC_CCP_UP)) { + skb_put(new_skb, new_count); + kfree_skb(skb); + skb = new_skb; + } else { + /* + * The frame could not be compressed, or it could not + * be sent in compressed form because CCP is down. + */ + kfree_skb(new_skb); + } + } - ppp->fcs = PPP_FCS_INIT; - if (!(ppp->flags & SC_COMP_AC)) { - ppp_stuff_char(ppp, PPP_ADDRESS); - ppp_stuff_char(ppp, PPP_CONTROL); - } - - if (!(ppp->flags & SC_COMP_PROT) || (proto & 0xff00)) - ppp_stuff_char(ppp, proto>>8); - ppp_stuff_char(ppp, proto&0xff); - - /* data part */ - while (len-- > 0) - ppp_stuff_char(ppp, *p++); - - /* fcs and flag */ - ppp_add_fcs(ppp); - *ppp->xhead++ = PPP_FLAG; - - /* update the time for demand dial function */ - ppp->ddinfo.ip_sjiffies = jiffies; - - /* send it! */ - if (ppp_debug >= 6) - ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS); - else { - PRINTKN (4,(KERN_DEBUG - "ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff)); - } - - ppp_kick_tty(ppp); - - done: - if (skb->free) - kfree_skb(skb, FREE_WRITE); - return 0; + /* + * Send the frame + */ + ret = ppp_async_send(ppp, skb); + if (ret > 0) { + /* we can release the lock */ + ppp->dev.tbusy = 0; + } else if (ret < 0) { + /* this can't happen, since the caller got the tbusy lock */ + printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n"); + } } - -static unsigned short -ppp_type_trans (struct sk_buff *skb, struct device *dev) + +/* + * Apply VJ TCP header compression to a packet. + */ +static struct sk_buff * +ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb) { - return(htons(ETH_P_IP)); + __u8 *orig_data, *data; + struct sk_buff *new_skb; + int len, proto; + + new_skb = alloc_skb(skb->len, GFP_ATOMIC); + if (new_skb == NULL) { + printk(KERN_ERR "ppp: no memory for vj compression\n"); + return skb; + } + + orig_data = data = skb->data + PPP_HDRLEN; + len = slhc_compress(ppp->slcomp, data, skb->len - PPP_HDRLEN, + new_skb->data + PPP_HDRLEN, &data, + (ppp->flags & SC_NO_TCP_CCID) == 0); + + if (data == orig_data) { + /* Couldn't compress the data */ + kfree_skb(new_skb); + return skb; + } + + /* The data has been changed */ + if (data[0] & SL_TYPE_COMPRESSED_TCP) { + proto = PPP_VJC_COMP; + data[0] ^= SL_TYPE_COMPRESSED_TCP; + } else { + if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP) + proto = PPP_VJC_UNCOMP; + else + proto = PPP_IP; + data[0] = orig_data[0]; + } + + data = skb_put(new_skb, len + PPP_HDRLEN); + data[0] = PPP_ALLSTATIONS; + data[1] = PPP_UI; + data[2] = 0; + data[3] = proto; + + kfree_skb(skb); + return new_skb; } -#ifdef NET02D -static int -ppp_header(unsigned char *buff, struct device *dev, unsigned short type, - unsigned long daddr, unsigned long saddr, unsigned len) +static inline void +ppp_send_frames(struct ppp *ppp) { - return(0); + struct sk_buff *skb; + + while (!test_and_set_bit(0, &ppp->dev.tbusy)) { + skb = skb_dequeue(&ppp->xmt_q); + if (skb == NULL) { + ppp->dev.tbusy = 0; + mark_bh(NET_BH); + break; + } + ppp_send_frame(ppp, skb); + } } -static int -ppp_rebuild_header(void *buff, struct device *dev) +/* + * Called from the hardware (tty) layer when it can accept + * another packet. + */ +static void +ppp_output_wakeup(struct ppp *ppp) { - return(0); + CHECK_PPP_VOID(); + + if (!ppp->dev.tbusy) { + printk(KERN_ERR "ppp_output_wakeup called but tbusy==0\n"); + return; + } + ppp->dev.tbusy = 0; + ppp_send_frames(ppp); } +/* + * Send a control frame (from pppd). + */ static void -ppp_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev) +ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb) { + CHECK_PPP_VOID(); + + /* + * Put the packet on the queue, then send as many as we can. + */ + skb_queue_tail(&ppp->xmt_q, skb); + ppp_send_frames(ppp); } -#else +/************************************************************* + * NETWORK OUTPUT + * This routine accepts requests from the network layer + * and attempts to deliver the packets. + *************************************************************/ +/* + * Send a frame to the peer. + * Returns 1 iff the frame was not accepted. + */ static int -ppp_header(unsigned char *buff, struct device *dev, unsigned short type, - void *daddr, void *saddr, unsigned len, struct sk_buff *skb) +ppp_dev_xmit(struct sk_buff *skb, struct device *dev) +{ + struct ppp *ppp = dev2ppp(dev); + struct tty_struct *tty = ppp2tty(ppp); + enum NPmode npmode; + int proto; + unsigned char *hdr; + + /* just a little sanity check. */ + if (skb == NULL) + return 0; + if (skb->data == NULL) { + kfree_skb(skb); + return 0; + } + + /* + * Avoid timing problem should tty hangup while data is + * queued to be sent. + */ + if (!ppp->inuse) { + dev_kfree_skb(skb); + return 0; + } + + /* + * Validate the tty interface + */ + if (tty == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_ERR + "ppp_dev_xmit: %s not connected to a TTY!\n", + dev->name); + dev_kfree_skb(skb); + return 0; + } + + /* + * Work out the appropriate network-protocol mode for this packet. + */ + npmode = NPMODE_PASS; /* default */ + switch (ntohs(skb->protocol)) { + case ETH_P_IP: + proto = PPP_IP; + npmode = ppp->sc_npmode[NP_IP]; + break; + case ETH_P_IPX: + proto = PPP_IPX; + npmode = ppp->sc_npmode[NP_IPX]; + break; + case ETH_P_PPPTALK: + case ETH_P_ATALK: + proto = PPP_AT; + npmode = ppp->sc_npmode[NP_AT]; + break; + default: + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "%s: packet for unknown proto %x\n", + ppp->name, ntohs(skb->protocol)); + dev_kfree_skb(skb); + return 0; + } + + /* + * Drop, accept or reject the packet depending on the mode. + */ + switch (npmode) { + case NPMODE_PASS: + break; + + case NPMODE_QUEUE: + /* + * We may not send the packet now, so drop it. + * XXX It would be nice to be able to return it to the + * network system to be queued and retransmitted later. + */ + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: returning frame\n", ppp->name); + dev_kfree_skb(skb); + return 0; + + case NPMODE_ERROR: + case NPMODE_DROP: + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG + "ppp_dev_xmit: dropping (npmode = %d) on %s\n", + npmode, ppp->name); + dev_kfree_skb(skb); + return 0; + } + + /* + * The dev->tbusy field acts as a lock to allow only + * one packet to be processed at a time. If we can't + * get the lock, try again later. + */ + if (test_and_set_bit(0, &dev->tbusy)) + return 1; + + /* + * Put the 4-byte PPP header on the packet. + * If there isn't room for it, we have to copy the packet. + */ + if (skb_headroom(skb) < PPP_HDRLEN) { + struct sk_buff *new_skb; + + new_skb = alloc_skb(skb->len + PPP_HDRLEN, GFP_ATOMIC); + if (new_skb == NULL) { + printk(KERN_ERR "%s: skb hdr alloc failed\n", + ppp->name); + dev_kfree_skb(skb); + dev->tbusy = 0; + return 0; + } + skb_reserve(new_skb, PPP_HDRLEN); + memcpy(skb_put(new_skb, skb->len), skb->data, skb->len); + dev_kfree_skb(skb); + skb = new_skb; + } + + hdr = skb_push(skb, PPP_HDRLEN); + hdr[0] = PPP_ALLSTATIONS; + hdr[1] = PPP_UI; + hdr[2] = proto >> 8; + hdr[3] = proto; + + ppp_send_frame(ppp, skb); + return 0; +} + +#if LINUX_VERSION_CODE < VERSION(2,1,15) +/* + * Null hard_header and header_rebuild routines. + */ +static int ppp_dev_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, + void *saddr, unsigned int len) { - return(0); + return 0; } -static int -ppp_rebuild_header(void *buff, struct device *dev, unsigned long raddr, - struct sk_buff *skb) +static int ppp_dev_rebuild(void *eth, struct device *dev, + unsigned long raddr, struct sk_buff *skb) { - return(0); + return 0; } -#endif +#endif /* < 2.1.15 */ -static struct enet_statistics * -ppp_get_stats (struct device *dev) +/* + * Generate the statistic information for the /proc/net/dev listing. + */ +static struct net_device_stats * +ppp_dev_stats (struct device *dev) { - struct ppp *ppp = &ppp_ctrl[dev->base_addr]; - static struct enet_statistics ppp_stats; - - ppp_stats.rx_packets = ppp->stats.rcomp + ppp->stats.runcomp; - ppp_stats.rx_errors = ppp->stats.rerrors; - ppp_stats.rx_dropped = ppp->stats.tossed; - ppp_stats.rx_fifo_errors = 0; - ppp_stats.rx_length_errors = ppp->stats.runts; - ppp_stats.rx_over_errors = ppp->stats.roverrun; - ppp_stats.rx_crc_errors = 0; - ppp_stats.rx_frame_errors = 0; - ppp_stats.tx_packets = ppp->stats.scomp + ppp->stats.suncomp; - ppp_stats.tx_errors = ppp->stats.serrors; - ppp_stats.tx_dropped = 0; - ppp_stats.tx_fifo_errors = 0; - ppp_stats.collisions = ppp->stats.sbusy; - ppp_stats.tx_carrier_errors = 0; - ppp_stats.tx_aborted_errors = 0; - ppp_stats.tx_window_errors = 0; - ppp_stats.tx_heartbeat_errors = 0; - - PRINTKN (3, (KERN_INFO "ppp_get_stats called")); - return &ppp_stats; + struct ppp *ppp = dev2ppp (dev); + + ppp->estats.rx_packets = ppp->stats.ppp_ipackets; + ppp->estats.rx_errors = ppp->stats.ppp_ierrors; + ppp->estats.tx_packets = ppp->stats.ppp_opackets; + ppp->estats.tx_errors = ppp->stats.ppp_oerrors; +#if LINUX_VERSION_CODE >= VERSION(2,1,25) + ppp->estats.rx_bytes = ppp->stats.ppp_ibytes; + ppp->estats.tx_bytes = ppp->stats.ppp_obytes; +#endif + + return &ppp->estats; } /************************************************************* @@ -1840,155 +2655,374 @@ ppp_get_stats (struct device *dev) * Miscellany called by various functions above. *************************************************************/ -#ifndef NEW_TTY_DRIVERS -/* find a PPP channel given a TTY */ -struct ppp * -ppp_find(struct tty_struct *tty) +/* Locate the previous instance of the PPP channel */ +static struct ppp * +ppp_find(int pid_value) { - int i; - for (i = 0; i < PPP_NRUNIT; i++) - if (ppp_ctrl[i].inuse && (ppp_ctrl[i].tty == tty)) return &ppp_ctrl[i]; - - return NULL; + struct ppp *ppp; + + /* try to find the device which this pid is already using */ + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { + if (ppp->inuse && ppp->sc_xfer == pid_value) { + ppp->sc_xfer = 0; + break; + } + } + return ppp; } -#endif -/* allocate a PPP channel */ +/* allocate or create a PPP channel */ static struct ppp * ppp_alloc(void) { - int i; - for (i = 0; i < PPP_NRUNIT; i++) - if (!set_bit(0, &ppp_ctrl[i].inuse)) return &ppp_ctrl[i]; + int if_num; + int status; + struct device *dev; + struct ppp *ppp; + + /* try to find an free device */ + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { + if (!test_and_set_bit(0, &ppp->inuse)) { + dev = ppp2dev(ppp); + if (dev->flags & IFF_UP) { + clear_bit(0, &ppp->inuse); + continue; + } + /* Reregister device */ + unregister_netdev(dev); + if (register_netdev(dev) == 0) + return ppp; + printk(KERN_DEBUG "could not reregister ppp device\n"); + /* leave inuse set in this case */ + } + } + + /* + * There are no available units, so make a new one. + */ + ppp = (struct ppp *) kmalloc(sizeof(struct ppp), GFP_KERNEL); + if (ppp == 0) { + printk(KERN_ERR "ppp: struct ppp allocation failed\n"); + return 0; + } + memset(ppp, 0, sizeof(*ppp)); + + /* initialize channel control data */ + ppp->magic = PPP_MAGIC; + ppp->next = NULL; + ppp->inuse = 1; + ppp->read_wait = NULL; + + /* + * Make up a suitable name for this device + */ + dev = ppp2dev(ppp); + dev->name = ppp->name; + if_num = dev_alloc_name(dev, "ppp%d"); + if (if_num < 0) { + printk(KERN_ERR "ppp: dev_alloc_name failed (%d)\n", if_num); + kfree(ppp); + return 0; + } + ppp->line = if_num; + ppp->slcomp = NULL; + + dev->next = NULL; + dev->init = ppp_init_dev; + dev->name = ppp->name; + dev->priv = (void *) ppp; + + /* register device so that we can be ifconfig'd */ + /* ppp_init_dev() will be called as a side-effect */ + status = register_netdev (dev); + if (status == 0) { + printk(KERN_INFO "registered device %s\n", dev->name); + } else { + printk(KERN_ERR + "ppp_alloc - register_netdev(%s) = %d failure.\n", + dev->name, status); + kfree(ppp); + ppp = NULL; + } - return NULL; + /* link this unit into our list */ + if (ppp_list == 0) + ppp_list = ppp; + else + ppp_last->next = ppp; + ppp_last = ppp; + + return ppp; } -/* marks a PPP interface 'busy'. user processes will wait, if - they try to write, and the network code will refrain from sending - return nonzero if succeeded in acquiring lock -*/ +/* + * Initialize the generic parts of the ppp structure. + */ +static void +ppp_generic_init(struct ppp *ppp) +{ + int indx; -static int -ppp_lock(struct ppp *ppp) + ppp->flags = 0; + ppp->mtu = PPP_MTU; + ppp->mru = PPP_MRU; + + skb_queue_head_init(&ppp->xmt_q); + skb_queue_head_init(&ppp->rcv_q); + + ppp->last_xmit = jiffies; + ppp->last_recv = jiffies; + + /* clear statistics */ + memset(&ppp->stats, 0, sizeof (struct pppstat)); + memset(&ppp->estats, 0, sizeof(struct net_device_stats)); + + /* PPP compression data */ + ppp->sc_xc_state = NULL; + ppp->sc_rc_state = NULL; + + for (indx = 0; indx < NUM_NP; ++indx) + ppp->sc_npmode[indx] = NPMODE_PASS; +} + +/* + * Called to clean up the generic parts of the ppp structure. + */ +static void +ppp_release(struct ppp *ppp) { - int flags, locked; - save_flags(flags); - cli(); - locked = ppp->sending; - ppp->sending = 1; - if (ppp->dev->flags & IFF_UP) - ppp->dev->tbusy = 1; - restore_flags(flags); - return locked == 0; + struct sk_buff *skb; + + CHECK_PPP_MAGIC(ppp); + + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s released\n", ppp->name); + + ppp_ccp_closed(ppp); + + /* Ensure that the pppd process is not hanging on select()/poll() */ + wake_up_interruptible(&ppp->read_wait); + + if (ppp->slcomp) { + slhc_free(ppp->slcomp); + ppp->slcomp = NULL; + } + + while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL) + kfree_skb(skb); + while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL) + kfree_skb(skb); } +/* + * Utility procedures to print a buffer in hex/ascii + */ static void -ppp_unlock(struct ppp *ppp) +ppp_print_hex (register __u8 * out, const __u8 * in, int count) { - int flags; - save_flags(flags); - cli(); - ppp->sending = 0; - if (ppp->dev->flags & IFF_UP) - ppp->dev->tbusy = 0; - restore_flags(flags); + register __u8 next_ch; + static char hex[] = "0123456789ABCDEF"; + + while (count-- > 0) { + next_ch = *in++; + *out++ = hex[(next_ch >> 4) & 0x0F]; + *out++ = hex[next_ch & 0x0F]; + ++out; + } } -/* FCS support functions */ +static void +ppp_print_char (register __u8 * out, const __u8 * in, int count) +{ + register __u8 next_ch; + + while (count-- > 0) { + next_ch = *in++; + + if (next_ch < 0x20 || next_ch > 0x7e) + *out++ = '.'; + else { + *out++ = next_ch; + if (next_ch == '%') /* printk/syslogd has a bug !! */ + *out++ = '%'; + } + } + *out = '\0'; +} static void -ppp_add_fcs(struct ppp *ppp) +ppp_print_buffer (const char *name, const __u8 *buf, int count) { - unsigned short fcs = ppp->fcs; - - fcs ^= 0xffff; - ppp_stuff_char(ppp, fcs & 0x00ff); - ppp_stuff_char(ppp, (fcs & 0xff00) >> 8); - ASSERT (ppp->fcs == PPP_FCS_GOOD); - PRINTKN (4,(KERN_DEBUG "ppp_add_fcs: fcs is %lx\n", - (long) (unsigned long) fcs)); + __u8 line[44]; + + if (name != NULL) + printk(KERN_DEBUG "ppp: %s, count = %d\n", name, count); + + while (count > 8) { + memset (line, 32, 44); + ppp_print_hex (line, buf, 8); + ppp_print_char (&line[8 * 3], buf, 8); + printk(KERN_DEBUG "%s\n", line); + count -= 8; + buf += 8; + } + + if (count > 0) { + memset (line, 32, 44); + ppp_print_hex (line, buf, count); + ppp_print_char (&line[8 * 3], buf, count); + printk(KERN_DEBUG "%s\n", line); + } } -static int -ppp_check_fcs(struct ppp *ppp) +/************************************************************* + * Compressor module interface + *************************************************************/ + +struct compressor_link { + struct compressor_link *next; + struct compressor *comp; +}; + +static struct compressor_link *ppp_compressors = (struct compressor_link *) 0; + +static struct compressor *find_compressor (int type) { - unsigned short fcs = PPP_FCS_INIT, msgfcs; - unsigned char *c = ppp->rbuff; - int i; - - for (i = 0; i < ppp->rcount - 2; i++, c++) - fcs = (fcs >> 8) ^ fcstab[(fcs ^ *c) & 0xff]; - - fcs ^= 0xffff; - msgfcs = (c[1] << 8) + c[0]; - PRINTKN (4,(KERN_INFO "ppp_check_fcs: got %lx want %lx\n", - (unsigned long) msgfcs, (unsigned long) fcs)); - return fcs == msgfcs; + struct compressor_link *lnk; + unsigned long flags; + + save_flags(flags); + cli(); + + lnk = ppp_compressors; + while (lnk != (struct compressor_link *) 0) { + if ((int) (__u8) lnk->comp->compress_proto == type) { + restore_flags(flags); + return lnk->comp; + } + lnk = lnk->next; + } + + restore_flags(flags); + return (struct compressor *) 0; } -static char hex[] = "0123456789ABCDEF"; +#ifdef CONFIG_MODULES +static int ppp_register_compressor (struct compressor *cp) +{ + struct compressor_link *new; + unsigned long flags; + + new = (struct compressor_link *) + kmalloc (sizeof (struct compressor_link), GFP_KERNEL); + + if (new == (struct compressor_link *) 0) + return 1; + + save_flags(flags); + cli(); + + if (find_compressor (cp->compress_proto)) { + restore_flags(flags); + kfree (new); + return 0; + } -inline void ppp_print_hex (register char *out, char *in, int count); -inline void ppp_print_hex (register char *out, char *in, int count) + new->next = ppp_compressors; + new->comp = cp; + ppp_compressors = new; + + restore_flags(flags); + return 0; +} + +static void ppp_unregister_compressor (struct compressor *cp) { - register unsigned char next_ch; + struct compressor_link *prev = (struct compressor_link *) 0; + struct compressor_link *lnk; + unsigned long flags; - while (count-- > 0) { - next_ch = (unsigned char) get_fs_byte (in); + save_flags(flags); + cli(); - *out++ = hex[(next_ch >> 4) & 0x0F]; - *out++ = hex[next_ch & 0x0F]; - ++out; - ++in; - } + lnk = ppp_compressors; + while (lnk != (struct compressor_link *) 0) { + if (lnk->comp == cp) { + if (prev) + prev->next = lnk->next; + else + ppp_compressors = lnk->next; + kfree (lnk); + break; + } + prev = lnk; + lnk = lnk->next; + } + restore_flags(flags); } +#endif -inline void ppp_print_char (register char *out, char *in, int count); -inline void ppp_print_char (register char *out, char *in, int count) +/************************************************************* + * Module support routines + *************************************************************/ + +#ifdef MODULE +int +init_module(void) { - register unsigned char next_ch; - - while (count-- > 0) { - next_ch = (unsigned char) get_fs_byte (in); - - if (next_ch < 0x20 || next_ch > 0x7e) - *out++ = '.'; - else { - *out++ = next_ch; - if (next_ch == '%') /* printk/syslogd has a bug !! */ - *out++ = '%'; - } - ++in; - } - *out = '\0'; + int status; + + /* register our line disciplines */ + status = ppp_first_time(); + if (status != 0) + printk(KERN_INFO "PPP: ppp_init() failure %d\n", status); +#if LINUX_VERSION_CODE < VERSION(2,1,18) + else + (void) register_symtab (&ppp_syms); +#endif + + return status; } -static void ppp_print_buffer(const char *name, char *buf, int count, int seg) +void +cleanup_module(void) { - char line [44]; - int old_fs = get_fs(); - - set_fs (seg); - - if (name != (char *) NULL) - PRINTK ((KERN_DEBUG "ppp: %s, count = %d\n", name, count)); - - while (count > 8) { - memset (line, ' ', sizeof (line)); - ppp_print_hex (line, buf, 8); - ppp_print_char (&line[8 * 3], buf, 8); - PRINTK ((KERN_DEBUG "%s\n", line)); - count -= 8; - buf += 8; - } - - if (count > 0) { - memset (line, ' ', sizeof (line)); - ppp_print_hex (line, buf, count); - ppp_print_char (&line[8 * 3], buf, count); - PRINTK ((KERN_DEBUG "%s\n", line)); - } - - set_fs (old_fs); + int status; + struct ppp *ppp, *next_ppp; + int busy = 0; + + /* + * Ensure that the devices are not in operation. + */ + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { + CHECK_PPP_MAGIC(ppp); + if (ppp->inuse || (ppp->dev.flags & IFF_UP)) + ++busy; + } + if (busy) + printk(KERN_CRIT "PPP: removing despite %d units in use!\n", + busy); + + /* + * Release the tty registration of the line discipline so that + * ttys can no longer be put into PPP line discipline. + */ + status = tty_register_ldisc (N_PPP, NULL); + if (status != 0) + printk(KERN_ERR + "PPP: Unable to unregister ppp line discipline " + "(err = %d)\n", status); + else + printk(KERN_INFO + "PPP: ppp line discipline successfully unregistered\n"); + + /* + * De-register the devices so that there is no problem with them + */ + for (ppp = ppp_list; ppp != 0; ppp = next_ppp) { + next_ppp = ppp->next; + unregister_netdev(&ppp->dev); + kfree (ppp); + } } +#endif