📄 slip.c
字号:
/* * slip.c This module implements the SLIP protocol for kernel-based * devices like TTY. It interfaces between a raw TTY, and the * kernel's INET protocol layers. * * Version: @(#)slip.c 0.8.3 12/24/94 * * Authors: Laurence Culhane, <loz@holmes.demon.co.uk> * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> * * Fixes: * Alan Cox : Sanity checks and avoid tx overruns. * Has a new sl->mtu field. * Alan Cox : Found cause of overrun. ifconfig sl0 mtu upwards. * Driver now spots this and grows/shrinks its buffers(hack!). * Memory leak if you run out of memory setting up a slip driver fixed. * Matt Dillon : Printable slip (borrowed from NET2E) * Pauline Middelink : Slip driver fixes. * Alan Cox : Honours the old SL_COMPRESSED flag * Alan Cox : KISS AX.25 and AXUI IP support * Michael Riepe : Automatic CSLIP recognition added * Charles Hedrick : CSLIP header length problem fix. * Alan Cox : Corrected non-IP cases of the above. * Alan Cox : Now uses hardware type as per FvK. * Alan Cox : Default to 192.168.0.0 (RFC 1597) * A.N.Kuznetsov : dev_tint() recursion fix. * Dmitry Gorodchanin : SLIP memory leaks * Dmitry Gorodchanin : Code cleanup. Reduce tty driver * buffering from 4096 to 256 bytes. * Improving SLIP response time. * CONFIG_SLIP_MODE_SLIP6. * ifconfig sl? up & down now works correctly. * Modularization. * Alan Cox : Oops - fix AX.25 buffer lengths * Dmitry Gorodchanin : Even more cleanups. Preserve CSLIP * statistics. Include CSLIP code only * if it really needed. * Alan Cox : Free slhc buffers in the right place. * Alan Cox : Allow for digipeated IP over AX.25 * Matti Aarnio : Dynamic SLIP devices, with ideas taken * from Jim Freeman's <jfree@caldera.com> * dynamic PPP devices. We do NOT kfree() * device entries, just reg./unreg. them * as they are needed. We kfree() them * at module cleanup. * With MODULE-loading ``insmod'', user can * issue parameter: slip_maxdev=1024 * (Or how much he/she wants.. Default is 256) * * Stanislav Voronyi : Slip line checking, with ideas taken * from multislip BSDI driver which was written * by Igor Chechik, RELCOM Corp. Only algorithms * have been ported to Linux SLIP driver. * Vitaly E. Lavrov : Sane behaviour on tty hangup. * Alexey Kuznetsov : Cleanup interfaces to tty&netdevice modules. */#define SL_CHECK_TRANSMIT#include <linux/config.h>#include <linux/module.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/in.h>#include <linux/tty.h>#include <linux/errno.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/rtnetlink.h>#include <linux/if_arp.h>#include <linux/if_slip.h>#include <linux/init.h>#include "slip.h"#ifdef CONFIG_INET#include <linux/ip.h>#include <linux/tcp.h>#include <net/slhc_vj.h>#endif#ifdef MODULE#define SLIP_VERSION "0.8.4-NET3.019-NEWTTY-MODULAR"#else#define SLIP_VERSION "0.8.4-NET3.019-NEWTTY"#endiftypedef struct slip_ctrl { struct slip ctrl; /* SLIP things */ struct net_device dev; /* the device */} slip_ctrl_t;static slip_ctrl_t **slip_ctrls = NULL;int slip_maxdev = SL_NRUNIT; /* Can be overridden with insmod! */MODULE_PARM(slip_maxdev, "i");static struct tty_ldisc sl_ldisc;static int slip_esc(unsigned char *p, unsigned char *d, int len);static void slip_unesc(struct slip *sl, unsigned char c);#ifdef CONFIG_SLIP_MODE_SLIP6static int slip_esc6(unsigned char *p, unsigned char *d, int len);static void slip_unesc6(struct slip *sl, unsigned char c);#endif#ifdef CONFIG_SLIP_SMARTstatic void sl_keepalive(unsigned long sls);static void sl_outfill(unsigned long sls);static int sl_ioctl(struct net_device *dev,struct ifreq *rq,int cmd);#endif/********************************* Buffer administration routines:* sl_alloc_bufs()* sl_free_bufs()* sl_realloc_bufs()** NOTE: sl_realloc_bufs != sl_free_bufs + sl_alloc_bufs, because* sl_realloc_bufs provides strong atomicity and reallocation* on actively running device.*********************************//* Allocate channel buffers. */static intsl_alloc_bufs(struct slip *sl, int mtu){ int err = -ENOBUFS; unsigned long len; char * rbuff = NULL; char * xbuff = NULL;#ifdef SL_INCLUDE_CSLIP char * cbuff = NULL; struct slcompress *slcomp = NULL;#endif /* * Allocate the SLIP frame buffers: * * rbuff Receive buffer. * xbuff Transmit buffer. * cbuff Temporary compression buffer. */ len = mtu * 2; /* * allow for arrival of larger UDP packets, even if we say not to * also fixes a bug in which SunOS sends 512-byte packets even with * an MSS of 128 */ if (len < 576 * 2) len = 576 * 2; rbuff = kmalloc(len + 4, GFP_KERNEL); if (rbuff == NULL) goto err_exit; xbuff = kmalloc(len + 4, GFP_KERNEL); if (xbuff == NULL) goto err_exit;#ifdef SL_INCLUDE_CSLIP cbuff = kmalloc(len + 4, GFP_KERNEL); if (cbuff == NULL) goto err_exit; slcomp = slhc_init(16, 16); if (slcomp == NULL) goto err_exit;#endif spin_lock_bh(&sl->lock); if (sl->tty == NULL) { spin_unlock_bh(&sl->lock); err = -ENODEV; goto err_exit; } sl->mtu = mtu; sl->buffsize = len; sl->rcount = 0; sl->xleft = 0; rbuff = xchg(&sl->rbuff, rbuff); xbuff = xchg(&sl->xbuff, xbuff);#ifdef SL_INCLUDE_CSLIP cbuff = xchg(&sl->cbuff, cbuff); slcomp = xchg(&sl->slcomp, slcomp);#ifdef CONFIG_SLIP_MODE_SLIP6 sl->xdata = 0; sl->xbits = 0;#endif#endif spin_unlock_bh(&sl->lock); err = 0; /* Cleanup */err_exit:#ifdef SL_INCLUDE_CSLIP if (cbuff) kfree(cbuff); if (slcomp) slhc_free(slcomp);#endif if (xbuff) kfree(xbuff); if (rbuff) kfree(rbuff); return err;}/* Free a SLIP channel buffers. */static voidsl_free_bufs(struct slip *sl){ void * tmp; /* Free all SLIP frame buffers. */ if ((tmp = xchg(&sl->rbuff, NULL)) != NULL) kfree(tmp); if ((tmp = xchg(&sl->xbuff, NULL)) != NULL) kfree(tmp);#ifdef SL_INCLUDE_CSLIP if ((tmp = xchg(&sl->cbuff, NULL)) != NULL) kfree(tmp); if ((tmp = xchg(&sl->slcomp, NULL)) != NULL) slhc_free(tmp);#endif}/* Reallocate slip channel buffers. */static int sl_realloc_bufs(struct slip *sl, int mtu){ int err = 0; struct net_device *dev = sl->dev; unsigned char *xbuff, *rbuff;#ifdef SL_INCLUDE_CSLIP unsigned char *cbuff;#endif int len = mtu * 2;/* * allow for arrival of larger UDP packets, even if we say not to * also fixes a bug in which SunOS sends 512-byte packets even with * an MSS of 128 */ if (len < 576 * 2) len = 576 * 2; xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);#ifdef SL_INCLUDE_CSLIP cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);#endif#ifdef SL_INCLUDE_CSLIP if (xbuff == NULL || rbuff == NULL || cbuff == NULL) {#else if (xbuff == NULL || rbuff == NULL) {#endif if (mtu >= sl->mtu) { printk("%s: unable to grow slip buffers, MTU change cancelled.\n", dev->name); err = -ENOBUFS; } goto done; } spin_lock_bh(&sl->lock); err = -ENODEV; if (sl->tty == NULL) goto done_on_bh; xbuff = xchg(&sl->xbuff, xbuff); rbuff = xchg(&sl->rbuff, rbuff);#ifdef SL_INCLUDE_CSLIP cbuff = xchg(&sl->cbuff, cbuff);#endif if (sl->xleft) { if (sl->xleft <= len) { memcpy(sl->xbuff, sl->xhead, sl->xleft); } else { sl->xleft = 0; sl->tx_dropped++; } } sl->xhead = sl->xbuff; if (sl->rcount) { if (sl->rcount <= len) { memcpy(sl->rbuff, rbuff, sl->rcount); } else { sl->rcount = 0; sl->rx_over_errors++; set_bit(SLF_ERROR, &sl->flags); } } sl->mtu = mtu; dev->mtu = mtu; sl->buffsize = len; err = 0;done_on_bh: spin_unlock_bh(&sl->lock);done: if (xbuff) kfree(xbuff); if (rbuff) kfree(rbuff);#ifdef SL_INCLUDE_CSLIP if (cbuff) kfree(cbuff);#endif return err;}/* Set the "sending" flag. This must be atomic hence the set_bit. */static inline voidsl_lock(struct slip *sl){ netif_stop_queue(sl->dev);}/* Clear the "sending" flag. This must be atomic, hence the ASM. */static inline voidsl_unlock(struct slip *sl){ netif_wake_queue(sl->dev);}/* Send one completely decapsulated IP datagram to the IP layer. */static voidsl_bump(struct slip *sl){ struct sk_buff *skb; int count; count = sl->rcount;#ifdef SL_INCLUDE_CSLIP if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) { unsigned char c; if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) { /* ignore compressed packets when CSLIP is off */ if (!(sl->mode & SL_MODE_CSLIP)) { printk("%s: compressed packet ignored\n", sl->dev->name); return; } /* make sure we've reserved enough space for uncompress to use */ if (count + 80 > sl->buffsize) { sl->rx_over_errors++; return; } count = slhc_uncompress(sl->slcomp, sl->rbuff, count); if (count <= 0) { return; } } else if (c >= SL_TYPE_UNCOMPRESSED_TCP) { if (!(sl->mode & SL_MODE_CSLIP)) { /* turn on header compression */ sl->mode |= SL_MODE_CSLIP; sl->mode &= ~SL_MODE_ADAPTIVE; printk("%s: header compression turned on\n", sl->dev->name); } sl->rbuff[0] &= 0x4f; if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) { return; } } }#endif /* SL_INCLUDE_CSLIP */ sl->rx_bytes+=count; skb = dev_alloc_skb(count); if (skb == NULL) { printk("%s: memory squeeze, dropping packet.\n", sl->dev->name); sl->rx_dropped++; return; } skb->dev = sl->dev; memcpy(skb_put(skb,count), sl->rbuff, count); skb->mac.raw=skb->data; skb->protocol=htons(ETH_P_IP); netif_rx(skb); sl->rx_packets++;}/* Encapsulate one IP datagram and stuff into a TTY queue. */static voidsl_encaps(struct slip *sl, unsigned char *icp, int len){ unsigned char *p; int actual, count; if (len > sl->mtu) { /* Sigh, shouldn't occur BUT ... */ printk ("%s: truncating oversized transmit packet!\n", sl->dev->name); sl->tx_dropped++; sl_unlock(sl); return; } p = icp;#ifdef SL_INCLUDE_CSLIP if (sl->mode & SL_MODE_CSLIP) { len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1); }#endif#ifdef CONFIG_SLIP_MODE_SLIP6 if(sl->mode & SL_MODE_SLIP6) count = slip_esc6(p, (unsigned char *) sl->xbuff, len); else#endif count = slip_esc(p, (unsigned char *) sl->xbuff, len); /* Order of next two lines is *very* important. * When we are sending a little amount of data, * the transfer may be completed inside driver.write() * routine, because it's running with interrupts enabled. * In this case we *never* got WRITE_WAKEUP event, * if we did not request it before write operation. * 14 Oct 1994 Dmitry Gorodchanin. */ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);#ifdef SL_CHECK_TRANSMIT sl->dev->trans_start = jiffies;#endif sl->xleft = count - actual; sl->xhead = sl->xbuff + actual;#ifdef CONFIG_SLIP_SMART /* VSV */ clear_bit(SLF_OUTWAIT, &sl->flags); /* reset outfill flag */#endif}/* * Called by the driver when there's room for more data. If we have * more packets to send, we send them here. */static void slip_write_wakeup(struct tty_struct *tty){ int actual; struct slip *sl = (struct slip *) tty->disc_data; /* First make sure we're connected. */ if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) { return; } if (sl->xleft <= 0) { /* Now serial buffer is almost free & we can start * transmission of another packet */ sl->tx_packets++; tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); sl_unlock(sl); return; } actual = tty->driver.write(tty, 0, sl->xhead, sl->xleft); sl->xleft -= actual; sl->xhead += actual;}static void sl_tx_timeout(struct net_device *dev){ struct slip *sl = (struct slip*)(dev->priv); spin_lock(&sl->lock); if (netif_queue_stopped(dev)) { struct slip *sl = (struct slip*)(dev->priv); if (!netif_running(dev)) goto out; /* May be we must check transmitter timeout here ? * 14 Oct 1994 Dmitry Gorodchanin. */#ifdef SL_CHECK_TRANSMIT if (jiffies - dev->trans_start < 20 * HZ) { /* 20 sec timeout not reached */ goto out; } printk("%s: transmit timed out, %s?\n", dev->name, (sl->tty->driver.chars_in_buffer(sl->tty) || sl->xleft) ? "bad line quality" : "driver error"); sl->xleft = 0; sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); sl_unlock(sl);#endif }out: spin_unlock(&sl->lock);}/* Encapsulate an IP datagram and kick it into a TTY queue. */static intsl_xmit(struct sk_buff *skb, struct net_device *dev){ struct slip *sl = (struct slip*)(dev->priv);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -