/* PPP for Linux * * Michael Callahan * Al Longyear * Paul Mackerras * Cyrus Durgin (changes for kmod) * * Dynamic PPP devices by Jim Freeman . * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid * * ==FILEVERSION 980123== * * 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: slip.c RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of Multi-protocol Datagrams over Point-to-Point Links RFC1332: IPCP ppp-2.0 Flags for this module (any combination is acceptable for testing.): 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 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 OPTIMIZE_FLAG_TIME ((HZ * 3)/2) #define CHECK_CHARACTERS 1 #define PPP_COMPRESS 1 /* $Id: ppp.c,v 1.14 1997/11/27 06:04:45 paulus Exp $ */ #include /* for CONFIG_KMOD */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* to get the struct task_struct */ #include /* used in new tty drivers */ #include /* used in new tty drivers */ #include #include #include #include #include #include #include #include #include #include #include typedef struct sk_buff sk_buff; #define skb_data(skb) ((__u8 *) (skb)->data) #include #include #include #include #define fcstab ppp_crc16_table /* Name of the table in the kernel */ #include #include #include #include #include #ifdef CONFIG_KMOD #include #endif #ifndef PPP_IPX #define PPP_IPX 0x2b /* IPX protocol over PPP */ #endif #ifndef PPP_LQR #define PPP_LQR 0xc025 /* Link Quality Reporting Protocol */ #endif static int ppp_register_compressor (struct compressor *cp); static void ppp_unregister_compressor (struct compressor *cp); /* * Local functions */ static struct compressor *find_compressor (int type); static void ppp_init_ctrl_blk (register struct ppp *); static void ppp_kick_tty (struct ppp *, struct ppp_buffer *bfr); static struct ppp *ppp_alloc (void); static struct ppp *ppp_find (int pid_value); static void ppp_print_buffer (const __u8 *, const __u8 *, int); extern inline void ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf, register __u8 chr); extern inline int lock_buffer (register struct ppp_buffer *buf); static int ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf, __u8 *data, int len, enum NPmode npmode); static int rcv_proto_ip (struct ppp *, __u16, __u8 *, int); static int rcv_proto_ipx (struct ppp *, __u16, __u8 *, int); static int rcv_proto_vjc_comp (struct ppp *, __u16, __u8 *, int); static int rcv_proto_vjc_uncomp (struct ppp *, __u16, __u8 *, int); static int rcv_proto_unknown (struct ppp *, __u16, __u8 *, int); static int rcv_proto_lqr (struct ppp *, __u16, __u8 *, int); static void ppp_doframe_lower (struct ppp *, __u8 *, int); static int ppp_doframe (struct ppp *); static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd); static int rcv_proto_ccp (struct ppp *, __u16, __u8 *, int); #define ins_char(pbuf,c) (buf_base(pbuf) [(pbuf)->count++] = (__u8)(c)) #ifndef OPTIMIZE_FLAG_TIME #define OPTIMIZE_FLAG_TIME 0 #endif /* * Parameters which may be changed via insmod. */ static int flag_time = OPTIMIZE_FLAG_TIME; MODULE_PARM(flag_time, "i"); /* * The "main" procedure to the ppp device */ int ppp_init (struct device *); /* * Network device driver callback routines */ 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 (sk_buff *, struct device *); static struct net_device_stats *ppp_dev_stats (struct device *); /* * TTY callbacks */ static ssize_t ppp_tty_read (struct tty_struct *, struct file *, __u8 *, size_t); static ssize_t ppp_tty_write (struct tty_struct *, struct file *, const __u8 *, size_t); static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int, unsigned long); static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait); 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); #define CHECK_PPP_MAGIC(ppp) do { \ if (ppp->magic != PPP_MAGIC) { \ printk(KERN_WARNING "bad magic for ppp %p at %s:%d\n", \ 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__); \ } \ } while (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))) #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) #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; /* Buffer types */ #define BUFFER_TYPE_DEV_RD 0 /* ppp read buffer */ #define BUFFER_TYPE_TTY_WR 1 /* tty write buffer */ #define BUFFER_TYPE_DEV_WR 2 /* ppp write buffer */ #define BUFFER_TYPE_TTY_RD 3 /* tty read buffer */ #define BUFFER_TYPE_VJ 4 /* vj compression buffer */ /* Define this string only once for all macro invocations */ static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n"; static char szVersion[] = PPP_VERSION; /* * Information for the protocol decoder */ typedef int (*pfn_proto) (struct ppp *, __u16, __u8 *, int); typedef struct ppp_proto_struct { int proto; pfn_proto func; } ppp_proto_type; static ppp_proto_type proto_list[] = { { PPP_IP, rcv_proto_ip }, { PPP_IPX, rcv_proto_ipx }, { PPP_VJC_COMP, rcv_proto_vjc_comp }, { PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp }, { PPP_LQR, rcv_proto_lqr }, { PPP_CCP, rcv_proto_ccp }, { 0, rcv_proto_unknown } /* !!! MUST BE LAST !!! */ }; __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 }; #ifdef CHECK_CHARACTERS static __u32 paritytab[8] = { 0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x69969669, 0x96696996, 0x96696996, 0x69969669 }; #endif /* 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; } /************************************************************* * INITIALIZATION *************************************************************/ /* This procedure is called once and once only to define who we are to * the operating system and the various procedures that it may use in * accessing the ppp protocol. */ __initfunc(static int ppp_first_time (void)) { static struct tty_ldisc ppp_ldisc; int status; printk (KERN_INFO "PPP: version %s (demand dialling)" "\n", szVersion); #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 /* * 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; ppp_ldisc.poll = ppp_tty_poll; 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; } /************************************************************* * INITIALIZATION *************************************************************/ /* called when the device is actually created */ static int ppp_init_dev (struct device *dev) { dev->hard_header_len = PPP_HDRLEN; /* 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; dev_init_buffers(dev); /* New-style flags */ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; return 0; } /* * Local procedure to initialize the ppp structure */ static void ppp_init_ctrl_blk (register struct ppp *ppp) { ppp->magic = PPP_MAGIC; ppp->toss = 0xE0; ppp->escape = 0; ppp->flags = 0; ppp->mtu = PPP_MTU; ppp->mru = PPP_MRU; 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->rbuf = NULL; ppp->wbuf = NULL; ppp->ubuf = NULL; ppp->cbuf = NULL; ppp->slcomp = NULL; ppp->read_wait = NULL; ppp->write_wait = NULL; ppp->last_xmit = jiffies - flag_time; ppp->last_recv = jiffies; /* clear statistics */ memset(&ppp->stats, 0, sizeof (struct pppstat)); memset(&ppp->estats, 0, sizeof(ppp->estats)); /* PPP compression data */ ppp->sc_xc_state = ppp->sc_rc_state = NULL; } EXPORT_SYMBOL(ppp_register_compressor); EXPORT_SYMBOL(ppp_unregister_compressor); EXPORT_SYMBOL(ppp_crc16_table); /* called at boot/load time for each ppp device defined in the kernel */ #ifndef MODULE 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 (answer == 0) answer = -ENODEV; return answer; } #endif #define BUFFER_MAGIC 0x1d10 #define CHECK_BUF_MAGIC(buf) do { \ if (buf->magic != BUFFER_MAGIC) { \ printk(KERN_WARNING "bad magic for ppp buffer %p at %s:%d\n", \ buf, __FILE__, __LINE__); \ } \ } while (0) /* * Routine to allocate a buffer for later use by the driver. */ static struct ppp_buffer * ppp_alloc_buf (int size, int type) { struct ppp_buffer *buf; buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer), GFP_ATOMIC); if (buf != NULL) { buf->size = size - 1; /* Mask for the buffer size */ buf->type = type; buf->locked = 0; buf->count = 0; buf->head = 0; buf->tail = 0; buf->fcs = PPP_INITFCS; buf->magic = BUFFER_MAGIC; } return (buf); } /* * Routine to release the allocated buffer. */ static void ppp_free_buf (struct ppp_buffer *ptr) { if (ptr != NULL) { CHECK_BUF_MAGIC(ptr); kfree (ptr); } } /* * Lock the indicated transmit buffer */ extern inline int lock_buffer (register struct ppp_buffer *buf) { unsigned long state; unsigned long flags; /* * Save the current state and if free then set it to the "busy" state */ CHECK_BUF_MAGIC(buf); save_flags (flags); cli (); state = buf->locked; if (state == 0) buf->locked = 2; restore_flags (flags); return (state); } /* * 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. */ static int ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru) { struct device *dev; unsigned long flags; struct ppp_buffer *new_rbuf; struct ppp_buffer *new_wbuf; struct ppp_buffer *new_cbuf; struct ppp_buffer *new_tbuf; struct ppp_buffer *old_rbuf; struct ppp_buffer *old_wbuf; struct ppp_buffer *old_cbuf; struct ppp_buffer *old_tbuf; int mtu, mru; /* * Allocate the buffer from the kernel for the data */ CHECK_PPP(0); dev = ppp2dev (ppp); if (ppp->flags & SC_DEBUG) printk(KERN_DEBUG "%s: changedmtu %d %d\n", ppp->name, new_mtu, new_mru); mru = new_mru; /* allow for possible escaping of every character */ mtu = (new_mtu * 2) + 20; /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */ if (mru < PPP_MRU) mru = PPP_MRU; mru += 10; new_wbuf = ppp_alloc_buf (mtu+PPP_HDRLEN, BUFFER_TYPE_DEV_WR); new_tbuf = ppp_alloc_buf ((PPP_MTU * 2) + 24, BUFFER_TYPE_TTY_WR); new_rbuf = ppp_alloc_buf (mru + 84, BUFFER_TYPE_DEV_RD); new_cbuf = ppp_alloc_buf (mru+PPP_HDRLEN, BUFFER_TYPE_VJ); /* * If the buffers failed to allocate then complain and release the partial * allocations. */ if (new_wbuf == NULL || new_tbuf == NULL || new_rbuf == NULL || new_cbuf == NULL) { printk (KERN_ERR "ppp: failed to allocate new buffers\n"); ppp_free_buf (new_wbuf); ppp_free_buf (new_tbuf); ppp_free_buf (new_rbuf); ppp_free_buf (new_cbuf); return 0; } /* * Update the pointers to the new buffer structures. */ save_flags(flags); cli (); old_wbuf = ppp->wbuf; old_rbuf = ppp->rbuf; old_cbuf = ppp->cbuf; old_tbuf = ppp->tbuf; ppp->wbuf = new_wbuf; ppp->rbuf = new_rbuf; ppp->cbuf = new_cbuf; ppp->tbuf = new_tbuf; if (old_wbuf) new_wbuf->locked = old_wbuf->locked; ppp->rbuf->size -= 80; /* reserve space for vj header expansion */ dev->mem_start = (unsigned long) buf_base (new_wbuf); dev->mem_end = (unsigned long) (dev->mem_start + mtu); dev->rmem_start = (unsigned long) buf_base (new_rbuf); dev->rmem_end = (unsigned long) (dev->rmem_start + mru); /* * Update the parameters for the new buffer sizes */ ppp->toss = 0xE0; /* To ignore characters until new FLAG */ ppp->escape = 0; /* No pending escape character */ dev->mtu = ppp->mtu = new_mtu; ppp->mru = new_mru; ppp->s1buf = NULL; ppp->s2buf = NULL; ppp->xbuf = NULL; ppp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); ppp->flags &= ~SC_XMIT_BUSY; restore_flags(flags); /* * Release old buffer pointers */ ppp_free_buf (old_rbuf); ppp_free_buf (old_wbuf); ppp_free_buf (old_cbuf); ppp_free_buf (old_tbuf); return 1; } /* * CCP is down; free (de)compressor state if necessary. */ static void ppp_ccp_closed (struct ppp *ppp) { unsigned long flags; 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; } if (ppp->sc_rc_state) { (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state); ppp->sc_rc_state = NULL; } } /* * Called to release all of the information in the current PPP structure. * * It is called when the ppp device goes down or if it is unable to go * up. */ static void ppp_release (struct ppp *ppp) { struct tty_struct *tty; struct device *dev; CHECK_PPP_MAGIC(ppp); tty = ppp2tty (ppp); dev = ppp2dev (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 poll() */ wake_up_interruptible (&ppp->read_wait); wake_up_interruptible (&ppp->write_wait); if (tty != NULL && tty->disc_data == ppp) tty->disc_data = NULL; /* Break the tty->ppp link */ ppp_free_buf (ppp->rbuf); ppp_free_buf (ppp->wbuf); ppp_free_buf (ppp->cbuf); ppp_free_buf (ppp->ubuf); ppp_free_buf (ppp->tbuf); ppp->rbuf = ppp->wbuf = ppp->cbuf = ppp->tbuf = ppp->xbuf = ppp->s1buf = ppp->s2buf = ppp->ubuf = NULL; if (ppp->slcomp) { slhc_free (ppp->slcomp); ppp->slcomp = NULL; } ppp->inuse = 0; ppp->tty = NULL; ppp->backup_tty = NULL; } /* * TTY callback. * * Called when the line discipline is changed to something * else, the tty is closed, or the tty detects a hangup. */ static void ppp_tty_close (struct tty_struct *tty) { struct ppp *ppp = tty2ppp (tty); if (ppp != NULL) { if (ppp->magic != PPP_MAGIC) { if (ppp->flags & SC_DEBUG) printk (KERN_WARNING "ppp: trying to close unopened tty!\n"); return; } CHECK_PPP_VOID(); tty->disc_data = NULL; 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->sc_xfer = 0; if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp: channel %s closing.\n", ppp2dev(ppp)->name); ppp_release (ppp); MOD_DEC_USE_COUNT; } } } /* * TTY callback. * * Called when the tty discipline is switched to PPP. */ static int ppp_tty_open (struct tty_struct *tty) { struct ppp *ppp = tty2ppp (tty); int indx; /* * There should not be an existing table for this slot. */ if (ppp) { printk (KERN_ERR "ppp_tty_open: gack! tty already associated to %s!\n", ppp->magic == PPP_MAGIC ? ppp2dev(ppp)->name : "unknown"); return -EEXIST; } /* * Allocate the structure from the system */ 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); tty->disc_data = ppp; ppp->tty = tty; } else { ppp = ppp_alloc(); if (ppp == NULL) { printk (KERN_ERR "ppp_alloc failed\n"); return -ENFILE; } /* * Initialize the control block */ ppp_init_ctrl_blk (ppp); tty->disc_data = ppp; ppp->tty = tty; /* * Allocate space for the default VJ header compression slots */ ppp->slcomp = slhc_init (16, 16); if (ppp->slcomp == NULL) { printk (KERN_ERR "ppp_tty_open: " "no space for compression buffers!\n"); ppp_release (ppp); return -ENOMEM; } /* * Allocate space for the MTU and MRU buffers */ if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) { ppp_release (ppp); return -ENOMEM; } /* * Allocate space for a user level buffer */ ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD); if (ppp->ubuf == NULL) { printk (KERN_ERR "ppp_tty_open: " "no space for user receive buffer\n"); ppp_release (ppp); return -ENOMEM; } if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp: channel %s open\n", ppp2dev(ppp)->name); for (indx = 0; indx < NUM_NP; ++indx) ppp->sc_npmode[indx] = NPMODE_PASS; MOD_INC_USE_COUNT; } /* * Flush any pending characters in the driver and discipline. */ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer (tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer (tty); return (ppp->line); } /* * Local function to send the next portion of the buffer. * * Called by the tty driver's tty_wakeup function should it be entered * because the partial buffer was transmitted. * * Called by kick_tty to send the initial portion of the buffer. * * Completion processing of the buffer transmission is handled here. */ static void ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty, struct ppp_buffer *xbuf) { register int count, actual; unsigned long flags; CHECK_PPP_VOID(); CHECK_BUF_MAGIC(xbuf); /* * Prevent re-entrancy by ensuring that this routine is called only once. */ save_flags(flags); cli (); if (ppp->flags & SC_XMIT_BUSY) { restore_flags(flags); return; } ppp->flags |= SC_XMIT_BUSY; restore_flags(flags); /* * Send the next block of data to the modem */ count = xbuf->count - xbuf->tail; actual = tty->driver.write (tty, 0, buf_base (xbuf) + xbuf->tail, count); /* * Terminate transmission of any block which may have an error. * This could occur should the carrier drop. */ if (actual < 0) { ppp->stats.ppp_oerrors++; actual = count; } else ppp->bytes_sent += actual; /* * If the buffer has been transmitted then clear the indicators. */ xbuf->tail += actual; if (actual == count) { xbuf = NULL; ppp->flags &= ~SC_XMIT_BUSY; /* * Complete the transmission on the current buffer. */ xbuf = ppp->xbuf; if (xbuf != NULL) { tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); xbuf->locked = 0; ppp->xbuf = NULL; /* * If the completed buffer came from the device write, then complete the * transmission block. */ ppp2dev (ppp)->tbusy = 0; mark_bh (NET_BH); /* * Wake up the transmission queue for all completion events. */ wake_up_interruptible (&ppp->write_wait); /* * Look at the priorities. Choose a daemon write over the device driver. */ save_flags(flags); cli(); xbuf = ppp->s1buf; ppp->s1buf = NULL; if (xbuf == NULL) { xbuf = ppp->s2buf; ppp->s2buf = NULL; } /* * If there is a pending buffer then transmit it now. */ if (xbuf != NULL) { ppp->flags &= ~SC_XMIT_BUSY; ppp_kick_tty (ppp, xbuf); restore_flags(flags); return; } restore_flags(flags); } } /* * Clear the re-entry flag */ save_flags(flags); /* &=~ may not be atomic */ cli (); ppp->flags &= ~SC_XMIT_BUSY; restore_flags(flags); } /* * 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. * * In addition, it is used to send the first part of the buffer since the * logic and the inter-locking would be identical. */ static void ppp_tty_wakeup (struct tty_struct *tty) { struct ppp_buffer *xbuf; struct ppp *ppp = tty2ppp (tty); if (!ppp) return; CHECK_PPP_VOID(); if (tty != ppp->tty) { tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); return; } /* * Ensure that there is a transmission pending. Clear the re-entry flag if * there is no pending buffer. Otherwise, send the buffer. */ xbuf = ppp->xbuf; if (xbuf == NULL) tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); else ppp_tty_wakeup_code (ppp, tty, xbuf); } /* * This function is called to transmit a buffer to the remote. The buffer * is placed on the pending queue if there is presently a buffer being * sent or it is transmitted with the aid of ppp_tty_wakeup. */ static void ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf) { unsigned long flags; CHECK_PPP_VOID(); CHECK_BUF_MAGIC(xbuf); /* * Hold interrupts. */ save_flags (flags); cli (); /* * Control the flags which are best performed with the interrupts masked. */ xbuf->locked = 1; xbuf->tail = 0; /* * If the transmitter is busy then place the buffer on the appropriate * priority queue. */ if (ppp->xbuf != NULL) { if (xbuf->type == BUFFER_TYPE_TTY_WR) ppp->s1buf = xbuf; else ppp->s2buf = xbuf; restore_flags (flags); return; } /* * If the transmitter is not busy then this is the highest priority frame */ ppp->flags &= ~SC_XMIT_BUSY; ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); ppp->xbuf = xbuf; restore_flags (flags); /* * Do the "tty wakeup_code" to actually send this buffer. */ ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf); } /************************************************************* * 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. *************************************************************/ /* * 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) { return 65536; /* We can handle an infinite amount of data. :-) */ } /* * 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 struct ppp *ppp = tty2ppp (tty); register struct ppp_buffer *buf = NULL; __u8 chr; if (ppp != 0) CHECK_PPP_VOID(); /* * This can happen if stuff comes in on the backup tty. */ if (ppp == 0 || tty != ppp->tty) return; /* * Fetch the pointer to the buffer. Be careful about race conditions. */ buf = ppp->rbuf; if (buf == NULL) 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; } CHECK_PPP_VOID (); /* * Print the buffer if desired */ if (ppp->flags & SC_LOG_RAWIN) ppp_print_buffer ("receive buffer", data, count); /* * Collect the character and error condition for the character. Set the toss * flag for the first character error. */ while (count-- > 0) { ppp->bytes_rcvd++; chr = *data++; if (flags) { if (*flags && ppp->toss == 0) { ppp->toss = *flags; switch (ppp->toss) { case TTY_OVERRUN: ++ppp->estats.rx_fifo_errors; break; case TTY_FRAME: case TTY_BREAK: ++ppp->estats.rx_frame_errors; break; } } ++flags; } /* * 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. */ #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 /* * Branch on the character. */ switch (chr) { /* * FLAG. This is the end of the block. If the block terminated by ESC FLAG, * then the block is to be ignored. In addition, characters before the very * first FLAG are also tossed by this procedure. */ case PPP_FLAG: /* PPP_FLAG: end of frame */ ppp->stats.ppp_ibytes += ppp->rbuf->count; if (ppp->escape) ppp->toss |= 0x80; /* * Process frames which are not to be ignored. If the processing failed, * then clean up the VJ tables. */ if (ppp_doframe (ppp) == 0) { ++ppp->stats.ppp_ierrors; slhc_toss (ppp->slcomp); } /* * Reset all indicators for the new frame to follow. */ buf->count = 0; buf->fcs = PPP_INITFCS; ppp->escape = 0; ppp->toss = 0; break; /* * All other characters in the data come here. If the character is in the * receive mask then ignore the character. */ default: /* If we're tossing, look no further. */ if (ppp->toss != 0) break; /* If this is a control char to be ignored, do so */ if (in_rmap (ppp, chr)) break; /* * 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; break; } /* * Decompress A/C and protocol compression here. */ if (buf->count == 0 && chr != PPP_ALLSTATIONS) { buf_base(buf)[0] = PPP_ALLSTATIONS; buf_base(buf)[1] = PPP_UI; buf->count = 2; } if (buf->count == 2 && (chr & 1) != 0) { buf_base(buf)[2] = 0; buf->count = 3; } /* * If the count sent is within reason then store the character, bump the * count, and update the FCS for the character. */ if (buf->count < buf->size) { buf_base (buf)[buf->count++] = chr; buf->fcs = PPP_FCS (buf->fcs, chr); break; } /* * The peer sent too much data. Set the flags to discard the current frame * and wait for the re-synchronization FLAG to be sent. */ ++ppp->estats.rx_length_errors; ppp->toss |= 0xC0; break; } } } /* on entry, a received frame is in ppp->rbuf.bufr check it and dispose as appropriate */ static int ppp_doframe (struct ppp *ppp) { __u8 *data = buf_base (ppp->rbuf); int count = ppp->rbuf->count; int proto; int new_count; __u8 *new_data; CHECK_PPP(0); CHECK_BUF_MAGIC(ppp->rbuf); /* * If there is a pending error from the receiver then log it and discard * the damaged frame. */ if (ppp->toss) { if ((ppp->flags & SC_DEBUG) && count > 0) printk (KERN_DEBUG "ppp_toss: tossing frame, reason = %x\n", ppp->toss); return 0; } /* * An empty frame is ignored. This occurs if the FLAG sequence precedes and * follows each frame. */ if (count == 0) return 1; /* * 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 CRC of the frame and discard the CRC characters from the * end of the buffer. */ if (ppp->rbuf->fcs != 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 */ /* * Obtain the protocol from the frame */ proto = PPP_PROTOCOL(data); /* * Process the active decompressor. */ if ((ppp->sc_rc_state != (void *) 0) && (ppp->flags & SC_DECOMP_RUN) && ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) { if (proto == PPP_COMP) { /* * If the frame is compressed then decompress it. */ new_data = kmalloc (ppp->mru + PPP_HDRLEN, GFP_ATOMIC); if (new_data == NULL) { printk (KERN_ERR "ppp_doframe: no memory\n"); new_count = DECOMP_ERROR; } else { new_count = (*ppp->sc_rcomp->decompress) (ppp->sc_rc_state, data, count, new_data, ppp->mru + PPP_HDRLEN); } switch (new_count) { default: ppp_doframe_lower (ppp, new_data, new_count); kfree (new_data); return 1; case DECOMP_ERROR: ppp->flags |= SC_DC_ERROR; break; case DECOMP_FATALERROR: ppp->flags |= SC_DC_FERROR; printk(KERN_ERR "ppp: fatal decomp error\n"); break; } /* * Log the error condition and discard the frame. */ if (new_data != 0) kfree (new_data); slhc_toss (ppp->slcomp); ++ppp->stats.ppp_ierrors; } else { /* * The frame is not special. Pass it through the compressor without * actually compressing the data */ (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state, data, count); } } /* * Process the uncompressed frame. */ ppp_doframe_lower (ppp, data, count); return 1; } static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count) { __u16 proto = PPP_PROTOCOL (data); ppp_proto_type *proto_ptr; CHECK_PPP_VOID(); /* * Ignore empty frames */ if (count <= PPP_HDRLEN) return; /* * Count the frame and print it */ ++ppp->stats.ppp_ipackets; 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 a protocol 0 which is the 'catch-all' to feed it to the pppd daemon. */ 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, proto, &data[PPP_HDRLEN], count - PPP_HDRLEN)) ppp->stats.ppp_ioctects += count; else ++ppp->stats.ppp_discards; } /* * Put the input frame into the networking system for the indicated protocol */ static int ppp_rcv_rx (struct ppp *ppp, __u16 proto, __u8 * data, int count) { sk_buff *skb = dev_alloc_skb (count); /* * Generate a skb buffer for the new frame. */ if (skb == NULL) { if (ppp->flags & SC_DEBUG) printk (KERN_ERR "ppp_do_ip: packet dropped on %s (no memory)!\n", ppp2dev (ppp)->name); return 0; } /* * Move the received data from the input buffer to the skb buffer. */ skb->dev = ppp2dev (ppp); /* We are the device */ skb->protocol = proto; skb->mac.raw = skb_data(skb); memcpy (skb_put(skb,count), data, count); /* move data */ /* * Tag the frame and kick it to the proper receive routine */ ppp->last_recv = jiffies; netif_rx (skb); return 1; } /* * Process the receipt of an IP frame */ static int rcv_proto_ip (struct ppp *ppp, __u16 proto, __u8 * data, int count) { CHECK_PPP(0); if ((ppp2dev (ppp)->flags & IFF_UP) && (count > 0)) if (ppp->sc_npmode[NP_IP] == NPMODE_PASS) return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count); return 0; } /* * Process the receipt of an IPX frame */ static int rcv_proto_ipx (struct ppp *ppp, __u16 proto, __u8 * data, int count) { CHECK_PPP(0); if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0)) return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count); return 0; } /* * Process the receipt of an VJ Compressed frame */ static int rcv_proto_vjc_comp (struct ppp *ppp, __u16 proto, __u8 *data, int count) { CHECK_PPP(0); if ((ppp->flags & SC_REJ_COMP_TCP) == 0) { int new_count = slhc_uncompress (ppp->slcomp, data, count); if (new_count >= 0) { return rcv_proto_ip (ppp, PPP_IP, data, new_count); } if (ppp->flags & SC_DEBUG) printk (KERN_NOTICE "ppp: error in VJ decompression\n"); } return 0; } /* * Process the receipt of an VJ Un-compressed frame */ static int rcv_proto_vjc_uncomp (struct ppp *ppp, __u16 proto, __u8 *data, int count) { CHECK_PPP(0); if ((ppp->flags & SC_REJ_COMP_TCP) == 0) { if (slhc_remember (ppp->slcomp, data, count) > 0) { return rcv_proto_ip (ppp, PPP_IP, data, count); } if (ppp->flags & SC_DEBUG) printk (KERN_NOTICE "ppp: error in VJ memorizing\n"); } return 0; } /* * Receive all unclassified protocols. */ static int rcv_proto_unknown (struct ppp *ppp, __u16 proto, __u8 *data, int len) { int totlen; register int current_idx; #define PUTC(c) \ { \ buf_base (ppp->ubuf) [current_idx++] = (__u8) (c); \ current_idx &= ppp->ubuf->size; \ if (current_idx == ppp->ubuf->tail) \ goto failure; \ } CHECK_PPP(0); /* * The total length includes the protocol data. * Lock the user information buffer. */ if (test_and_set_bit (0, &ppp->ubuf->locked)) { if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp: rcv_proto_unknown: can't get lock\n"); } else { CHECK_BUF_MAGIC(ppp->ubuf); current_idx = ppp->ubuf->head; /* * Insert the buffer length (not counted), the protocol, and the data */ totlen = len + 2; PUTC (totlen >> 8); PUTC (totlen); PUTC (proto >> 8); PUTC (proto); totlen -= 2; while (totlen-- > 0) { PUTC (*data++); } #undef PUTC /* * The frame is complete. Update the head pointer and wakeup the pppd * process. */ ppp->ubuf->head = current_idx; clear_bit (0, &ppp->ubuf->locked); wake_up_interruptible (&ppp->read_wait); if (ppp->tty->fasync != NULL) kill_fasync (ppp->tty->fasync, SIGIO); return 1; /* * The buffer is full. Unlock the header */ failure: clear_bit (0, &ppp->ubuf->locked); if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp: rcv_proto_unknown: buffer overflow\n"); } /* * Discard the frame. There are no takers for this protocol. */ if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp: rcv_proto_unknown: dropping packet\n"); return 0; } /* * 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. */ 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; if (slen > len) return; 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; /* * we're agreeing to send compressed packets. */ if (!rcvd) { if (ppp->sc_xc_state == NULL) break; if ((*ppp->sc_xcomp->comp_init) (ppp->sc_xc_state, opt, opt_len, ppp2dev (ppp)->base_addr, 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, ppp2dev (ppp)->base_addr, 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; /* * CCP Reset-ack resets compressors and decompressors as it passes through. */ case CCP_RESETACK: 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); } static int rcv_proto_ccp (struct ppp *ppp, __u16 proto, __u8 *dp, int len) { CHECK_PPP(0); ppp_proto_ccp (ppp, dp, len, 1); return rcv_proto_unknown (ppp, proto, dp, len); } /* * Handle a LQR packet. */ static int rcv_proto_lqr (struct ppp *ppp, __u16 proto, __u8 * data, int len) { return rcv_proto_unknown (ppp, proto, data, len); } /************************************************************* * 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. *************************************************************/ /* read a PPP frame from the us_rbuff circular buffer, waiting if necessary */ static ssize_t ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf, size_t nr) { struct ppp *ppp = tty2ppp (tty); __u8 c; int error; ssize_t len, ret; #define GETC(c) \ { \ c = buf_base (ppp->ubuf) [ppp->ubuf->tail++]; \ ppp->ubuf->tail &= ppp->ubuf->size; \ } /* * Validate the pointers */ if (!ppp) return -EIO; /* if (ppp->magic != PPP_MAGIC) 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. */ error = verify_area (VERIFY_WRITE, buf, nr); if (error != 0) return (error); /* * Acquire the read lock. */ for (;;) { ppp = tty2ppp (tty); if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse || tty != ppp->tty) return 0; if (test_and_set_bit (0, &ppp->ubuf->locked) != 0) { #if 0 if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp_tty_read: sleeping(ubuf)\n"); #endif current->timeout = 0; current->state = TASK_INTERRUPTIBLE; schedule (); if (signal_pending(current)) return -EINTR; continue; } /* * Fetch the length of the buffer from the first two bytes. */ if (ppp->ubuf->head == ppp->ubuf->tail) len = 0; else { GETC (c); len = c << 8; GETC (c); len += c; if (len) break; } /* * If there is no length then wait for the data to arrive. */ /* no data */ clear_bit (0, &ppp->ubuf->locked); if (file->f_flags & O_NONBLOCK) return -EAGAIN; current->timeout = 0; #if 0 if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp_tty_read: sleeping(read_wait)\n"); #endif interruptible_sleep_on (&ppp->read_wait); if (signal_pending(current)) return -EINTR; } /* * Ensure that the frame will fit within the caller's buffer. If not, then * discard the frame from the input buffer. */ if (len + 2 > nr) { /* Can't copy it, update us_rbuff_head */ if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp: read of %lu bytes too small for %ld " "frame\n", (unsigned long) nr, (long) len + 2); ppp->stats.ppp_ierrors++; error = -EOVERFLOW; goto out; } /* * Fake the insertion of the ADDRESS and CONTROL information because these * were not saved in the buffer. */ error = put_user((u_char) PPP_ALLSTATIONS, buf); if (error) goto out; ++buf; error = put_user((u_char) PPP_UI, buf); if (error) goto out; ++buf; /* * Copy the received data from the buffer to the caller's area. */ ret = len + 2; /* Account for ADDRESS and CONTROL bytes */ while (len-- > 0) { GETC (c); error = put_user(c, buf); if (error) goto out; ++buf; } clear_bit (0, &ppp->ubuf->locked); return ret; out: ppp->ubuf->tail += len; ppp->ubuf->tail &= ppp->ubuf->size; clear_bit (0, &ppp->ubuf->locked); return error; #undef GETC } /* stuff a character into the transmit buffer, using PPP's way of escaping special characters. also, update fcs to take account of new character */ extern inline void ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf, register __u8 chr) { /* * The buffer should not be full. */ if (ppp->flags & SC_DEBUG) { if ((buf->count < 0) || (buf->count > 3000)) printk (KERN_DEBUG "ppp_stuff_char: %d %x\n", (unsigned int) buf->count, (unsigned int) chr); } /* * Update the FCS and if the character needs to be escaped, do it. */ buf->fcs = PPP_FCS (buf->fcs, chr); if (in_xmap (ppp, chr)) { chr ^= PPP_TRANS; ins_char (buf, PPP_ESCAPE); } /* * Add the character to the buffer. */ ins_char (buf, chr); } /* * Procedure to encode the data with the proper escaping and send the * data to the remote system. */ static void ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf, __u8 *data, int count, int non_ip) { __u16 write_fcs; int address, control; int proto; CHECK_PPP_VOID(); CHECK_BUF_MAGIC(buf); ++ppp->stats.ppp_opackets; ppp->stats.ppp_ooctects += count; /* * Insert the leading FLAG character */ buf->count = 0; if (non_ip || flag_time == 0) ins_char (buf, PPP_FLAG); else { if (jiffies - ppp->last_xmit >= flag_time) ins_char (buf, PPP_FLAG); } ppp->last_xmit = jiffies; buf->fcs = PPP_INITFCS; /* * Emit the address/control information if needed */ address = PPP_ADDRESS (data); control = PPP_CONTROL (data); proto = PPP_PROTOCOL (data); if (address != PPP_ALLSTATIONS || control != PPP_UI || (ppp->flags & SC_COMP_AC) == 0) { ppp_stuff_char (ppp, buf, address); ppp_stuff_char (ppp, buf, control); } /* * Emit the protocol (compressed if possible) */ if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00)) ppp_stuff_char (ppp, buf, proto >> 8); ppp_stuff_char (ppp, buf, proto); /* * Insert the data */ data += 4; count -= 4; while (count-- > 0) ppp_stuff_char (ppp, buf, *data++); /* * Add the trailing CRC and the final flag character */ write_fcs = buf->fcs ^ 0xFFFF; ppp_stuff_char (ppp, buf, write_fcs); ppp_stuff_char (ppp, buf, write_fcs >> 8); /* * Add the trailing flag character */ ins_char (buf, PPP_FLAG); /* * Send the block to the tty driver. */ ppp->stats.ppp_obytes += buf->count; ppp_kick_tty (ppp, buf); } /* * Compress and send an frame to the peer. * * Return 0 if frame was queued for transmission. * 1 if frame must be re-queued for later driver support. */ static int ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf, __u8 *data, int count) { int proto; int address, control; __u8 *new_data; int new_count; CHECK_PPP(0); CHECK_BUF_MAGIC(buf); /* * Print the buffer */ if (ppp->flags & SC_LOG_OUTPKT) ppp_print_buffer ("write frame", data, count); /* * Determine if the frame may be compressed. Attempt to compress the * frame if possible. */ proto = PPP_PROTOCOL (data); address = PPP_ADDRESS (data); control = PPP_CONTROL (data); if (((ppp->flags & SC_COMP_RUN) != 0) && (ppp->sc_xc_state != (void *) 0) && (address == PPP_ALLSTATIONS) && (control == PPP_UI) && (proto != PPP_LCP) && (proto != PPP_CCP)) { new_data = kmalloc (ppp->mtu, GFP_ATOMIC); if (new_data == NULL) { if (ppp->flags & SC_DEBUG) printk (KERN_ERR "ppp_dev_xmit_frame: no memory\n"); return 1; } new_count = (*ppp->sc_xcomp->compress) (ppp->sc_xc_state, data, new_data, count, ppp->mtu); if (new_count > 0 && (ppp->flags & SC_CCP_UP)) { ppp_dev_xmit_lower (ppp, buf, new_data, new_count, 0); kfree (new_data); return 0; } /* * The frame could not be compressed, or it could not be sent in * compressed form because CCP is not yet up. */ kfree (new_data); } /* * Go to the escape encoding */ ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00)); return 0; } /* * Revise the tty frame for specific protocols. */ static int send_revise_frame (register struct ppp *ppp, __u8 *data, int len) { __u8 *p; switch (PPP_PROTOCOL (data)) { /* * Update the LQR frame with the current MIB information. This saves having * the daemon read old MIB data from the driver. */ case PPP_LQR: len = 48; /* total size of this frame */ p = (__u8 *) &data [40]; /* Point to last two items. */ p = store_long (p, ppp->stats.ppp_opackets + 1); p = store_long (p, ppp->stats.ppp_ooctects + len); break; /* * Outbound compression frames */ case PPP_CCP: ppp_proto_ccp (ppp, data + PPP_HDRLEN, len - PPP_HDRLEN, 0); break; default: break; } return len; } /* * write a frame with NR chars from BUF to TTY * we have to put the FCS field on ourselves */ static ssize_t ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data, size_t count) { struct ppp *ppp = tty2ppp (tty); __u8 *new_data; int error; struct wait_queue wait = {current, NULL}; /* * Verify the pointers. */ error = -EIO; if (!ppp) goto out; if (ppp->magic != PPP_MAGIC) goto out; 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; } /* * Allocate a buffer for the data and fetch it from the user space. */ new_data = kmalloc (count, GFP_KERNEL); if (new_data == NULL) { printk (KERN_ERR "ppp_tty_write: no memory\n"); return 0; } /* * Retrieve the user's buffer */ error = -EFAULT; if (copy_from_user(new_data, data, count)) goto out_free; /* * Lock this PPP unit so we will be the only writer, * sleeping if necessary. * * Note that we add our task to the wait queue before * attempting to lock, as the lock flag may be cleared * from an interrupt. */ add_wait_queue(&ppp->write_wait, &wait); while (1) { error = 0; current->timeout = 0; current->state = TASK_INTERRUPTIBLE; if (lock_buffer(ppp->tbuf) == 0) break; schedule(); error = -EINVAL; ppp = tty2ppp (tty); if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse || tty != ppp->tty) { printk("ppp_tty_write: %p invalid after wait!\n", ppp); break; } error = -EINTR; if (signal_pending(current)) break; } current->state = TASK_RUNNING; remove_wait_queue(&ppp->write_wait, &wait); if (error) goto out_free; /* * Change the LQR frame */ count = send_revise_frame (ppp, new_data, count); /* * Send the data */ if (PPP_PROTOCOL(new_data) == PPP_IP) { /* * IP frames can be sent by pppd when we're doing * demand-dialling. We send them via ppp_dev_xmit_ip * to make sure that VJ compression happens properly. */ ppp_dev_xmit_ip(ppp, ppp->tbuf, new_data + PPP_HDRLEN, count - PPP_HDRLEN, NPMODE_PASS); } else { ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count); } error = count; out_free: kfree (new_data); out: return error; } /* * Process the BSD compression IOCTL event for the tty device. */ static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp) { 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 */ error = -EFAULT; if (copy_from_user(&data, odp, sizeof (data))) goto out; nb = data.length; ptr = data.ptr; if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH) nb = CCP_MAX_OPTION_LENGTH; if (copy_from_user(ccp_option, ptr, nb)) goto out; error = -EINVAL; if (ccp_option[1] < 2) /* preliminary check on the length byte */ goto out; save_flags(flags); cli(); ppp->flags &= ~(SC_COMP_RUN | SC_DECOMP_RUN); restore_flags(flags); cp = find_compressor (ccp_option[0]); #ifdef CONFIG_KMOD 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) goto out_no_comp; /* * 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) { printk(KERN_WARNING "%s: comp_alloc failed\n", ppp->name); error = -ENOBUFS; } else if (ppp->flags & SC_DEBUG) printk(KERN_DEBUG "%s: comp_alloc -> %p\n", ppp->name, ppp->sc_xc_state); } 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) { printk(KERN_WARNING "%s: decomp_alloc failed\n", ppp->name); error = -ENOBUFS; } else if (ppp->flags & SC_DEBUG) printk(KERN_DEBUG "%s: decomp_alloc -> %p\n", ppp->name, ppp->sc_rc_state); } out: return error; out_no_comp: error = -EINVAL; /* no handler found */ 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); goto out; } /* * Process the IOCTL event for the tty device. */ 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 = 0; /* * Verify the status of the PPP device. */ if (!ppp) return -EBADF; if (ppp->magic != PPP_MAGIC) return -EBADF; CHECK_PPP (-ENXIO); /* * The user must have an euid of root to do these requests. */ if (!suser ()) return -EPERM; /* * Set the MRU value */ switch (param2) { case PPPIOCSMRU: error = get_user(temp_i, (int *) param3); if (error != 0) break; if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp_tty_ioctl: set mru to %x\n", temp_i); if (ppp->mru != temp_i) ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i); break; /* * Fetch the flags */ case PPPIOCGFLAGS: 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 error = put_user(temp_i, (int *) param3); break; /* * Set the flags for the various options */ case PPPIOCSFLAGS: error = get_user(temp_i, (int *) param3); if (error != 0) break; temp_i &= SC_MASK; temp_i |= (ppp->flags & ~SC_MASK); if ((ppp->flags & SC_CCP_OPEN) && (temp_i & SC_CCP_OPEN) == 0) ppp_ccp_closed (ppp); if ((ppp->flags | temp_i) & SC_DEBUG) printk (KERN_INFO "ppp_tty_ioctl: set flags to %x\n", temp_i); ppp->flags = temp_i; break; /* * Set the compression mode */ case PPPIOCSCOMPRESS: error = ppp_set_compression (ppp, (struct ppp_option_data *) param3); break; /* * Retrieve the transmit async map */ case PPPIOCGASYNCMAP: error = put_user(ppp->xmit_async_map[0], (int *) param3); break; /* * Set the transmit async map */ case PPPIOCSASYNCMAP: error = get_user(temp_i, (int *) param3); if (error != 0) 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]); break; /* * Set the receive async map */ case PPPIOCSRASYNCMAP: error = get_user(temp_i, (int *) param3); if (error != 0) 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); break; /* * Obtain the unit number for this device. */ case PPPIOCGUNIT: error = put_user(ppp2dev (ppp)->base_addr, (int *) param3); if (error != 0) break; if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp_tty_ioctl: get unit: %ld\n", ppp2dev (ppp)->base_addr); break; /* * Set the debug level */ case PPPIOCSDEBUG: error = get_user(temp_i, (int *) param3); if (error != 0) break; temp_i = (temp_i & 0x1F) << 16; temp_i |= (ppp->flags & ~0x1F0000); if ((ppp->flags | temp_i) & SC_DEBUG) printk (KERN_INFO "ppp_tty_ioctl: set flags to %x\n", temp_i); ppp->flags = temp_i; break; /* * Get the debug level */ case PPPIOCGDEBUG: temp_i = (ppp->flags >> 16) & 0x1F; error = put_user(temp_i, (int *) param3); break; /* * Get the times since the last send/receive frame operation */ case PPPIOCGIDLE: { struct ppp_idle cur_ddinfo; /* change absolute times to relative times. */ cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ; cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ; error = -EFAULT; if (!copy_to_user((void *) param3, &cur_ddinfo, sizeof (cur_ddinfo))) error = 0; } break; /* * Retrieve the extended async map */ case PPPIOCGXASYNCMAP: error = -EFAULT; if (!copy_to_user((void *) param3, ppp->xmit_async_map, sizeof (ppp->xmit_async_map))) error = 0; break; /* * Set the async extended map */ case PPPIOCSXASYNCMAP: { __u32 temp_tbl[8]; error = -EFAULT; if (copy_from_user(temp_tbl, (void *) param3, sizeof (temp_tbl))) break; temp_tbl[1] = 0x00000000; temp_tbl[2] &= ~0x40000000; temp_tbl[3] |= 0x60000000; error = 0; 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)); if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp_tty_ioctl: set xasyncmap\n"); } } break; /* * Set the maximum VJ header compression slot number. */ case PPPIOCSMAXCID: error = get_user(temp_i, (int *) param3); if (error != 0) break; temp_i = (temp_i & 255) + 1; if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp_tty_ioctl: set maxcid to %d\n", temp_i); if (ppp->slcomp != NULL) slhc_free (ppp->slcomp); ppp->slcomp = NULL; ppp->slcomp = slhc_init (16, temp_i); if (ppp->slcomp == NULL) { printk (KERN_ERR "ppp_tty_ioctl: " "no space for compression buffers!\n"); ppp_release (ppp); error = -ENOMEM; } break; case PPPIOCXFERUNIT: ppp->backup_tty = tty; ppp->sc_xfer = current->pid; break; case PPPIOCGNPMODE: case PPPIOCSNPMODE: { struct npioctl npi; error = -EFAULT; if (copy_from_user(&npi, (void *) param3, sizeof(npi))) break; if (npi.protocol != PPP_IP) { if (ppp->flags & SC_DEBUG) printk(KERN_DEBUG "pppioc[gs]npmode: " "invalid protocol %d\n", npi.protocol); error = -EINVAL; break; } npi.protocol = NP_IP; if (param2 == PPPIOCGNPMODE) { npi.mode = ppp->sc_npmode[npi.protocol]; if (copy_to_user((void *) param3, &npi, sizeof (npi))) break; } 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); /* N.B. Why is the busy flag cleared here? */ ppp2dev(ppp)->tbusy = 0; mark_bh(NET_BH); error = 0; } break; /* * Allow users to read, but not set, the serial port parameters */ case TCGETS: case TCGETA: error = n_tty_ioctl (tty, file, param2, param3); break; case FIONREAD: { int count = ppp->ubuf->tail - ppp->ubuf->head; if (count < 0) count += (ppp->ubuf->size + 1); error = put_user(count, (int *) param3); } break; /* * All other ioctl() events will come here. */ default: if (ppp->flags & SC_DEBUG) printk (KERN_WARNING "ppp_tty_ioctl: " "invalid ioctl=%x, addr=%lx\n", param2, param3); error = -ENOIOCTLCMD; break; } return error; } /* * TTY callback. * * Process the poll() statement for the PPP device. */ static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait) { struct ppp *ppp = tty2ppp (tty); unsigned int mask = 0; if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) { CHECK_PPP (0); poll_wait(filp, &ppp->read_wait, wait); poll_wait(filp, &ppp->write_wait, wait); /* Must lock the user buffer area while checking. */ CHECK_BUF_MAGIC(ppp->ubuf); if(test_and_set_bit(0, &ppp->ubuf->locked) == 0) { if(ppp->ubuf->head != ppp->ubuf->tail) mask |= POLLIN | POLLRDNORM; clear_bit(0, &ppp->ubuf->locked); } if(tty->flags & (1 << TTY_OTHER_CLOSED)) mask |= POLLHUP; if(tty_hung_up_p(filp)) mask |= POLLHUP; if(ppp->tbuf->locked == 0) mask |= POLLOUT | POLLWRNORM; } return mask; } /************************************************************* * 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...). *************************************************************/ /* * Callback from the network layer when the device goes up. */ static int ppp_dev_open (struct device *dev) { struct ppp *ppp = dev2ppp (dev); if (ppp2tty (ppp) == NULL) { printk (KERN_ERR "ppp: %s not connected to a TTY! can't go open!\n", dev->name); return -ENXIO; } if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp: channel %s going up for IP packets!\n", dev->name); CHECK_PPP (-ENXIO); return 0; } /* * Callback from the network layer when the ppp device goes down. */ static int ppp_dev_close (struct device *dev) { struct ppp *ppp = dev2ppp (dev); if (ppp2tty (ppp) == NULL) { return -ENXIO; } /* * We don't do anything about the device going down. It is not important * for us. */ if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp: channel %s going down for IP packets!\n", dev->name); CHECK_PPP (-ENXIO); return 0; } /* * IOCTL operation to read the version of the driver. */ static int ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr) { int error; char *result = (char *) ifr->ifr_ifru.ifru_data; int len = strlen (szVersion) + 1; /* * Move the version data */ error = -EFAULT; if (!copy_to_user(result, szVersion, len)) error = 0; return error; } /* * IOCTL to read the statistics for the pppstats program. */ static int ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev) { struct ppp_stats *result, temp; int error; /* * Supply the information for the caller. First move the version data * then move the ppp stats; and finally the vj stats. */ memset (&temp, 0, sizeof(temp)); if (dev->flags & IFF_UP) { memcpy (&temp.p, &ppp->stats, sizeof (struct pppstat)); if (ppp->slcomp != NULL) { temp.vj.vjs_packets = ppp->slcomp->sls_o_compressed+ ppp->slcomp->sls_o_uncompressed; temp.vj.vjs_compressed = ppp->slcomp->sls_o_compressed; temp.vj.vjs_searches = ppp->slcomp->sls_o_searches; temp.vj.vjs_misses = ppp->slcomp->sls_o_misses; temp.vj.vjs_errorin = ppp->slcomp->sls_i_error; temp.vj.vjs_tossed = ppp->slcomp->sls_i_tossed; temp.vj.vjs_uncompressedin = ppp->slcomp->sls_i_uncompressed; temp.vj.vjs_compressedin = ppp->slcomp->sls_i_compressed; } } result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data; error = -EFAULT; if (!copy_to_user(result, &temp, sizeof (temp))) error = 0; return error; } /* * IOCTL to read the compression statistics for the pppstats program. */ static int ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev) { struct ppp_comp_stats *result, temp; int error; /* * Supply the information for the caller. */ memset (&temp, 0, sizeof(temp)); if (dev->flags & IFF_UP) { if (ppp->sc_xc_state != NULL) (*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state, &temp.c); if (ppp->sc_rc_state != NULL) (*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state, &temp.d); } /* * Move the data to the caller's buffer */ result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data; error = -EFAULT; if (!copy_to_user(result, &temp, sizeof (temp))) error = 0; return error; } /* * Callback from the network layer to process the sockioctl functions. */ static int ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd) { struct ppp *ppp = dev2ppp (dev); int error; CHECK_PPP_MAGIC(ppp); /* * Process the requests */ switch (cmd) { case SIOCGPPPSTATS: error = ppp_dev_ioctl_stats (ppp, ifr, dev); break; case SIOCGPPPCSTATS: error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev); break; case SIOCGPPPVER: error = ppp_dev_ioctl_version (ppp, ifr); break; default: error = -EINVAL; break; } return error; } /* * Send an IP frame to the remote with vj header compression. * * Return 0 if frame was queued for transmission. * 1 if frame must be re-queued for later driver support. * -1 if frame should be dropped. */ static int ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf, __u8 *data, int len, enum NPmode npmode) { int proto = PPP_IP; __u8 *hdr; /* * Branch on the type of processing for the IP frame. */ 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); return -1; case NPMODE_ERROR: case NPMODE_DROP: if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp_dev_xmit: npmode = %d on %s\n", ppp->sc_npmode[NP_IP], ppp->name); return -1; default: if (ppp->flags & SC_DEBUG) printk (KERN_WARNING "ppp_dev_xmit: unknown npmode %d on %s\n", ppp->sc_npmode[NP_IP], ppp->name); return -1; } /* * At this point, the buffer will be transmitted. There is no other exit. * * Try to compress the header. */ if (ppp->flags & SC_COMP_TCP) { len = slhc_compress (ppp->slcomp, data, len, buf_base (ppp->cbuf) + PPP_HDRLEN, &data, (ppp->flags & SC_NO_TCP_CCID) == 0); 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; data[0] = (data[0] & 0x0f) | 0x40; } } /* * Send the frame */ len += PPP_HDRLEN; hdr = data - PPP_HDRLEN; hdr[0] = PPP_ALLSTATIONS; hdr[1] = PPP_UI; hdr[2] = 0; hdr[3] = proto; return ppp_dev_xmit_frame (ppp, buf, hdr, len); } /* * Send a non-IP data frame (such as an IPX frame) to the remote. * * Return 0 if frame was queued for transmission. * 1 if frame must be re-queued for later driver support. */ static int ppp_dev_xmit_other (struct device *dev, struct ppp *ppp, __u8 *data, int len, int proto) { __u8 *hdr; /* * Send the frame */ len += PPP_HDRLEN; hdr = data - PPP_HDRLEN; hdr[0] = PPP_ALLSTATIONS; hdr[1] = PPP_UI; hdr[2] = proto >> 8; hdr[3] = proto; return ppp_dev_xmit_frame (ppp, ppp->wbuf, hdr, len); } /* * Send a frame to the remote. */ static int ppp_dev_xmit (sk_buff *skb, struct device *dev) { int answer, len; __u8 *data; struct ppp *ppp = dev2ppp (dev); struct tty_struct *tty = ppp2tty (ppp); /* * just a little sanity check. */ if (skb == NULL) { if (ppp->flags & SC_DEBUG) printk (KERN_WARNING "ppp_dev_xmit: null packet!\n"); 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; } /* * Fetch the pointer to the data */ len = skb->len; data = skb_data(skb); if (data == (__u8 *) 0) { if (ppp->flags & SC_DEBUG) printk (KERN_CRIT "ppp_dev_xmit: %s Null skb data\n", dev->name); dev_kfree_skb (skb); return 0; } /* * Detect a change in the transfer size */ if (ppp->mtu != ppp2dev (ppp)->mtu) { ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, ppp->mru); } /* * Acquire the lock on the transmission buffer. If the buffer was busy then * mark the device as busy. * We also require that ppp->tbuf be unlocked, in order to serialize * calls to ppp_dev_xmit_frame (which does compression) and the output * of frames w.r.t. tty writes from pppd. */ CHECK_BUF_MAGIC(ppp->wbuf); if (ppp->tbuf->locked || lock_buffer (ppp->wbuf) != 0) { dev->tbusy = 1; if (ppp->flags & SC_DEBUG) printk(KERN_DEBUG "dev_xmit blocked, t=%lu w=%lu\n", ppp->tbuf->locked, ppp->wbuf->locked); return 1; } /* * Look at the protocol in the skb to determine the difference between * an IP frame and an IPX frame. */ switch (ntohs (skb->protocol)) { case ETH_P_IPX: answer = ppp_dev_xmit_other (dev, ppp, data, len, PPP_IPX); break; case ETH_P_IP: answer = ppp_dev_xmit_ip (ppp, ppp->wbuf, data, len, ppp->sc_npmode[NP_IP]); break; default: /* All others have no support at this time. */ dev_kfree_skb (skb); return 0; } /* * This is the end of the transmission. Release the buffer if it was sent. */ if (answer == 0) { /* packet queued OK */ dev_kfree_skb (skb); } else { ppp->wbuf->locked = 0; if (answer < 0) { /* packet should be dropped */ dev_kfree_skb (skb); answer = 0; } else { /* packet should be queued for later */ dev->tbusy = 1; } } return answer; } /* * Generate the statistic information for the /proc/net/dev listing. */ static struct net_device_stats * ppp_dev_stats (struct device *dev) { 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; ppp->estats.rx_bytes = ppp->stats.ppp_ibytes; ppp->estats.tx_bytes = ppp->stats.ppp_obytes; return &ppp->estats; } /************************************************************* * UTILITIES * Miscellany called by various functions above. *************************************************************/ /* Locate the previous instance of the PPP channel */ static struct ppp * ppp_find (int pid_value) { 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; } /* Collect hanged up channels */ static void ppp_sync(void) { struct device *dev; struct ppp *ppp; rtnl_lock(); for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { if (!ppp->inuse) { dev = ppp2dev(ppp); if (dev->flags&IFF_UP) dev_close(dev); } } rtnl_unlock(); } /* allocate or create a PPP channel */ static struct ppp * ppp_alloc (void) { int if_num; int status; struct device *dev; struct ppp *ppp; ppp_sync(); /* try to find an free device */ if_num = 0; for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { if (!test_and_set_bit(0, &ppp->inuse)) { /* Reregister device */ dev = ppp2dev(ppp); unregister_netdev (dev); if (register_netdev (dev)) { printk(KERN_DEBUG "cannot reregister ppp device\n"); return NULL; } return ppp; } ++if_num; } /* * There are no available units, so make a new one. */ ppp = (struct ppp *) kmalloc (sizeof(struct ppp), GFP_KERNEL); if (ppp == 0) return 0; memset(ppp, 0, sizeof(*ppp)); /* initialize channel control data */ set_bit(0, &ppp->inuse); ppp->line = if_num; ppp->tty = NULL; ppp->backup_tty = NULL; if (ppp_last == 0) ppp_list = ppp; else ppp_last->next = ppp; ppp_last = ppp; ppp->next = 0; dev = ppp2dev(ppp); dev->next = NULL; dev->init = ppp_init_dev; dev->name = ppp->name; sprintf(dev->name, "ppp%d", if_num); dev->base_addr = (__u32) if_num; 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); ppp = NULL; /* This one will forever be busy as it is not initialized */ } return ppp; } /* * Utility procedures to print a buffer in hex/ascii */ static void ppp_print_hex (register __u8 * out, const __u8 * in, int count) { 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; } } 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_print_buffer (const __u8 * name, const __u8 * buf, int count) { __u8 line[44]; if (name != (__u8 *) 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); } } /************************************************************* * 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) { 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 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; } new->next = ppp_compressors; new->comp = cp; ppp_compressors = new; restore_flags(flags); return 0; } static void ppp_unregister_compressor (struct compressor *cp) { struct compressor_link *prev = (struct compressor_link *) 0; struct compressor_link *lnk; unsigned long flags; save_flags(flags); cli(); 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); } /************************************************************* * Module support routines *************************************************************/ #ifdef MODULE int init_module(void) { int status; /* register our line disciplines */ status = ppp_first_time(); if (status != 0) printk (KERN_INFO "PPP: ppp_init() failure %d\n", status); return (status); } void cleanup_module(void) { int status; struct device *dev; struct ppp *ppp, *next_ppp; int busy_flag = 0; /* * Ensure that the devices are not in operation. */ for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { if (ppp->inuse && ppp->tty != NULL) { busy_flag = 1; break; } dev = ppp2dev (ppp); if (dev->start || dev->flags & IFF_UP) { busy_flag = 1; break; } } /* * Ensure that there are no compressor modules registered */ if (ppp_compressors != NULL) busy_flag = 1; if (busy_flag) { printk (KERN_INFO "PPP: device busy, remove delayed\n"); return; } /* * Release the tty registration of the line discipline so that no new entries * may be created. */ status = tty_register_ldisc (N_PPP, NULL); if (status != 0) printk (KERN_INFO "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; ppp_release (ppp); unregister_netdev (&ppp->dev); kfree (ppp); } } #endif