From b5451d783ade99308dfccdf5ca284ed07affa4ff Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Wed, 3 Aug 2011 03:17:13 -0700 Subject: slip: Move the SLIP drivers Move the Serial Line Internet Protocol (SLIP) drivers into drivers/net/slip/ and make the necessary Kconfig and Makefile changes. Signed-off-by: Jeff Kirsher Acked-by: Alan Cox diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 3f72686..b3206c9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -199,6 +199,8 @@ source "drivers/net/fddi/Kconfig" source "drivers/net/plip/Kconfig" +source "drivers/net/slip/Kconfig" + source "drivers/net/tokenring/Kconfig" source "drivers/net/wireless/Kconfig" @@ -274,78 +276,6 @@ config RIONET_RX_SIZE depends on RIONET default "128" -config SLIP - tristate "SLIP (serial line) support" - ---help--- - Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to - connect to your Internet service provider or to connect to some - other local Unix box or if you want to configure your Linux box as a - Slip/CSlip server for other people to dial in. SLIP (Serial Line - Internet Protocol) is a protocol used to send Internet traffic over - serial connections such as telephone lines or null modem cables; - nowadays, the protocol PPP is more commonly used for this same - purpose. - - Normally, your access provider has to support SLIP in order for you - to be able to use it, but there is now a SLIP emulator called SLiRP - around (available from - ) which - allows you to use SLIP over a regular dial up shell connection. If - you plan to use SLiRP, make sure to say Y to CSLIP, below. The - NET-3-HOWTO, available from - , explains how to - configure SLIP. Note that you don't need this option if you just - want to run term (term is a program which gives you almost full - Internet connectivity if you have a regular dial up shell account on - some Internet connected Unix computer. Read - ). SLIP - support will enlarge your kernel by about 4 KB. If unsure, say N. - - To compile this driver as a module, choose M here. The module - will be called slip. - -config SLIP_COMPRESSED - bool "CSLIP compressed headers" - depends on SLIP - select SLHC - ---help--- - This protocol is faster than SLIP because it uses compression on the - TCP/IP headers (not on the data itself), but it has to be supported - on both ends. Ask your access provider if you are not sure and - answer Y, just in case. You will still be able to use plain SLIP. If - you plan to use SLiRP, the SLIP emulator (available from - ) which - allows you to use SLIP over a regular dial up shell connection, you - definitely want to say Y here. The NET-3-HOWTO, available from - , explains how to configure - CSLIP. This won't enlarge your kernel. - -config SLHC - tristate - help - This option enables Van Jacobsen serial line header compression - routines. - -config SLIP_SMART - bool "Keepalive and linefill" - depends on SLIP - help - Adds additional capabilities to the SLIP driver to support the - RELCOM line fill and keepalive monitoring. Ideal on poor quality - analogue lines. - -config SLIP_MODE_SLIP6 - bool "Six bit SLIP encapsulation" - depends on SLIP - help - Just occasionally you may need to run IP over hostile serial - networks that don't pass all control characters or are only seven - bit. Saying Y here adds an extra mode you can use with SLIP: - "slip6". In this mode, SLIP will only send normal ASCII symbols over - the serial device. Naturally, this has to be supported at the other - end of the link as well. It's good enough, for example, to run IP - over the async ports of a Camtec JNT Pad. If unsure, say N. - config NET_FC bool "Fibre Channel driver support" depends on SCSI && PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 52dae95..e6a183e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -20,8 +20,6 @@ obj-$(CONFIG_RIONET) += rionet.o obj-$(CONFIG_NET) += Space.o loopback.o obj-$(CONFIG_NET_SB1000) += sb1000.o -obj-$(CONFIG_SLIP) += slip.o -obj-$(CONFIG_SLHC) += slhc.o obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o obj-$(CONFIG_XEN_NETDEV_BACKEND) += xen-netback/ @@ -48,6 +46,8 @@ obj-$(CONFIG_PPP_SYNC_TTY) += ppp/ obj-$(CONFIG_PPPOE) += ppp/ obj-$(CONFIG_PPPOL2TP) += ppp/ obj-$(CONFIG_PPTP) += ppp/ +onj-$(CONFIG_SLIP) += slip/ +obj-$(CONFIG_SLHC) += slip/ obj-$(CONFIG_TR) += tokenring/ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_ARCNET) += arcnet/ diff --git a/drivers/net/slhc.c b/drivers/net/slhc.c deleted file mode 100644 index 0a0a664..0000000 --- a/drivers/net/slhc.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * Routines to compress and uncompress tcp packets (for transmission - * over low speed serial lines). - * - * Copyright (c) 1989 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: - * - Initial distribution. - * - * - * modified for KA9Q Internet Software Package by - * Katie Stevens (dkstevens@ucdavis.edu) - * University of California, Davis - * Computing Services - * - 01-31-90 initial adaptation (from 1.19) - * PPP.05 02-15-90 [ks] - * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression - * PPP.15 09-90 [ks] improve mbuf handling - * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities - * - * - Feb 1991 Bill_Simpson@um.cc.umich.edu - * variable number of conversation slots - * allow zero or one slots - * separate routines - * status display - * - Jul 1994 Dmitry Gorodchanin - * Fixes for memory leaks. - * - Oct 1994 Dmitry Gorodchanin - * Modularization. - * - Jan 1995 Bjorn Ekwall - * Use ip_fast_csum from ip.h - * - July 1995 Christos A. Polyzols - * Spotted bug in tcp option checking - * - * - * This module is a difficult issue. It's clearly inet code but it's also clearly - * driver code belonging close to PPP and SLIP - */ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_INET -/* Entire module is for IP only */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned char *encode(unsigned char *cp, unsigned short n); -static long decode(unsigned char **cpp); -static unsigned char * put16(unsigned char *cp, unsigned short x); -static unsigned short pull16(unsigned char **cpp); - -/* Initialize compression data structure - * slots must be in range 0 to 255 (zero meaning no compression) - */ -struct slcompress * -slhc_init(int rslots, int tslots) -{ - register short i; - register struct cstate *ts; - struct slcompress *comp; - - comp = kzalloc(sizeof(struct slcompress), GFP_KERNEL); - if (! comp) - goto out_fail; - - if ( rslots > 0 && rslots < 256 ) { - size_t rsize = rslots * sizeof(struct cstate); - comp->rstate = kzalloc(rsize, GFP_KERNEL); - if (! comp->rstate) - goto out_free; - comp->rslot_limit = rslots - 1; - } - - if ( tslots > 0 && tslots < 256 ) { - size_t tsize = tslots * sizeof(struct cstate); - comp->tstate = kzalloc(tsize, GFP_KERNEL); - if (! comp->tstate) - goto out_free2; - comp->tslot_limit = tslots - 1; - } - - comp->xmit_oldest = 0; - comp->xmit_current = 255; - comp->recv_current = 255; - /* - * don't accept any packets with implicit index until we get - * one with an explicit index. Otherwise the uncompress code - * will try to use connection 255, which is almost certainly - * out of range - */ - comp->flags |= SLF_TOSS; - - if ( tslots > 0 ) { - ts = comp->tstate; - for(i = comp->tslot_limit; i > 0; --i){ - ts[i].cs_this = i; - ts[i].next = &(ts[i - 1]); - } - ts[0].next = &(ts[comp->tslot_limit]); - ts[0].cs_this = 0; - } - return comp; - -out_free2: - kfree(comp->rstate); -out_free: - kfree(comp); -out_fail: - return NULL; -} - - -/* Free a compression data structure */ -void -slhc_free(struct slcompress *comp) -{ - if ( comp == NULLSLCOMPR ) - return; - - if ( comp->tstate != NULLSLSTATE ) - kfree( comp->tstate ); - - if ( comp->rstate != NULLSLSTATE ) - kfree( comp->rstate ); - - kfree( comp ); -} - - -/* Put a short in host order into a char array in network order */ -static inline unsigned char * -put16(unsigned char *cp, unsigned short x) -{ - *cp++ = x >> 8; - *cp++ = x; - - return cp; -} - - -/* Encode a number */ -static unsigned char * -encode(unsigned char *cp, unsigned short n) -{ - if(n >= 256 || n == 0){ - *cp++ = 0; - cp = put16(cp,n); - } else { - *cp++ = n; - } - return cp; -} - -/* Pull a 16-bit integer in host order from buffer in network byte order */ -static unsigned short -pull16(unsigned char **cpp) -{ - short rval; - - rval = *(*cpp)++; - rval <<= 8; - rval |= *(*cpp)++; - return rval; -} - -/* Decode a number */ -static long -decode(unsigned char **cpp) -{ - register int x; - - x = *(*cpp)++; - if(x == 0){ - return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ - } else { - return x & 0xff; /* -1 if PULLCHAR returned error */ - } -} - -/* - * icp and isize are the original packet. - * ocp is a place to put a copy if necessary. - * cpp is initially a pointer to icp. If the copy is used, - * change it to ocp. - */ - -int -slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, - unsigned char *ocp, unsigned char **cpp, int compress_cid) -{ - register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]); - register struct cstate *lcs = ocs; - register struct cstate *cs = lcs->next; - register unsigned long deltaS, deltaA; - register short changes = 0; - int hlen; - unsigned char new_seq[16]; - register unsigned char *cp = new_seq; - struct iphdr *ip; - struct tcphdr *th, *oth; - __sum16 csum; - - - /* - * Don't play with runt packets. - */ - - if(isizeprotocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) { - /* Send as regular IP */ - if(ip->protocol != IPPROTO_TCP) - comp->sls_o_nontcp++; - else - comp->sls_o_tcp++; - return isize; - } - /* Extract TCP header */ - - th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4); - hlen = ip->ihl*4 + th->doff*4; - - /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or - * some other control bit is set). Also uncompressible if - * it's a runt. - */ - if(hlen > isize || th->syn || th->fin || th->rst || - ! (th->ack)){ - /* TCP connection stuff; send as regular IP */ - comp->sls_o_tcp++; - return isize; - } - /* - * Packet is compressible -- we're going to send either a - * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way, - * we need to locate (or create) the connection state. - * - * States are kept in a circularly linked list with - * xmit_oldest pointing to the end of the list. The - * list is kept in lru order by moving a state to the - * head of the list whenever it is referenced. Since - * the list is short and, empirically, the connection - * we want is almost always near the front, we locate - * states via linear search. If we don't find a state - * for the datagram, the oldest state is (re-)used. - */ - for ( ; ; ) { - if( ip->saddr == cs->cs_ip.saddr - && ip->daddr == cs->cs_ip.daddr - && th->source == cs->cs_tcp.source - && th->dest == cs->cs_tcp.dest) - goto found; - - /* if current equal oldest, at end of list */ - if ( cs == ocs ) - break; - lcs = cs; - cs = cs->next; - comp->sls_o_searches++; - } - /* - * Didn't find it -- re-use oldest cstate. Send an - * uncompressed packet that tells the other side what - * connection number we're using for this conversation. - * - * Note that since the state list is circular, the oldest - * state points to the newest and we only need to set - * xmit_oldest to update the lru linkage. - */ - comp->sls_o_misses++; - comp->xmit_oldest = lcs->cs_this; - goto uncompressed; - -found: - /* - * Found it -- move to the front on the connection list. - */ - if(lcs == ocs) { - /* found at most recently used */ - } else if (cs == ocs) { - /* found at least recently used */ - comp->xmit_oldest = lcs->cs_this; - } else { - /* more than 2 elements */ - lcs->next = cs->next; - cs->next = ocs->next; - ocs->next = cs; - } - - /* - * Make sure that only what we expect to change changed. - * Check the following: - * IP protocol version, header length & type of service. - * The "Don't fragment" bit. - * The time-to-live field. - * The TCP header length. - * IP options, if any. - * TCP options, if any. - * If any of these things are different between the previous & - * current datagram, we send the current datagram `uncompressed'. - */ - oth = &cs->cs_tcp; - - if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl - || ip->tos != cs->cs_ip.tos - || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)) - || ip->ttl != cs->cs_ip.ttl - || th->doff != cs->cs_tcp.doff - || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) - || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){ - goto uncompressed; - } - - /* - * Figure out which of the changing fields changed. The - * receiver expects changes in the order: urgent, window, - * ack, seq (the order minimizes the number of temporaries - * needed in this section of code). - */ - if(th->urg){ - deltaS = ntohs(th->urg_ptr); - cp = encode(cp,deltaS); - changes |= NEW_U; - } else if(th->urg_ptr != oth->urg_ptr){ - /* argh! URG not set but urp changed -- a sensible - * implementation should never do this but RFC793 - * doesn't prohibit the change so we have to deal - * with it. */ - goto uncompressed; - } - if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){ - cp = encode(cp,deltaS); - changes |= NEW_W; - } - if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){ - if(deltaA > 0x0000ffff) - goto uncompressed; - cp = encode(cp,deltaA); - changes |= NEW_A; - } - if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){ - if(deltaS > 0x0000ffff) - goto uncompressed; - cp = encode(cp,deltaS); - changes |= NEW_S; - } - - switch(changes){ - case 0: /* Nothing changed. If this packet contains data and the - * last one didn't, this is probably a data packet following - * an ack (normal on an interactive connection) and we send - * it compressed. Otherwise it's probably a retransmit, - * retransmitted ack or window probe. Send it uncompressed - * in case the other side missed the compressed version. - */ - if(ip->tot_len != cs->cs_ip.tot_len && - ntohs(cs->cs_ip.tot_len) == hlen) - break; - goto uncompressed; - break; - case SPECIAL_I: - case SPECIAL_D: - /* actual changes match one of our special case encodings -- - * send packet uncompressed. - */ - goto uncompressed; - case NEW_S|NEW_A: - if(deltaS == deltaA && - deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ - /* special case for echoed terminal traffic */ - changes = SPECIAL_I; - cp = new_seq; - } - break; - case NEW_S: - if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ - /* special case for data xfer */ - changes = SPECIAL_D; - cp = new_seq; - } - break; - } - deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id); - if(deltaS != 1){ - cp = encode(cp,deltaS); - changes |= NEW_I; - } - if(th->psh) - changes |= TCP_PUSH_BIT; - /* Grab the cksum before we overwrite it below. Then update our - * state with this packet's header. - */ - csum = th->check; - memcpy(&cs->cs_ip,ip,20); - memcpy(&cs->cs_tcp,th,20); - /* We want to use the original packet as our compressed packet. - * (cp - new_seq) is the number of bytes we need for compressed - * sequence numbers. In addition we need one byte for the change - * mask, one for the connection id and two for the tcp checksum. - * So, (cp - new_seq) + 4 bytes of header are needed. - */ - deltaS = cp - new_seq; - if(compress_cid == 0 || comp->xmit_current != cs->cs_this){ - cp = ocp; - *cpp = ocp; - *cp++ = changes | NEW_C; - *cp++ = cs->cs_this; - comp->xmit_current = cs->cs_this; - } else { - cp = ocp; - *cpp = ocp; - *cp++ = changes; - } - *(__sum16 *)cp = csum; - cp += 2; -/* deltaS is now the size of the change section of the compressed header */ - memcpy(cp,new_seq,deltaS); /* Write list of deltas */ - memcpy(cp+deltaS,icp+hlen,isize-hlen); - comp->sls_o_compressed++; - ocp[0] |= SL_TYPE_COMPRESSED_TCP; - return isize - hlen + deltaS + (cp - ocp); - - /* Update connection state cs & send uncompressed packet (i.e., - * a regular ip/tcp packet but with the 'conversation id' we hope - * to use on future compressed packets in the protocol field). - */ -uncompressed: - memcpy(&cs->cs_ip,ip,20); - memcpy(&cs->cs_tcp,th,20); - if (ip->ihl > 5) - memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4); - if (th->doff > 5) - memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4); - comp->xmit_current = cs->cs_this; - comp->sls_o_uncompressed++; - memcpy(ocp, icp, isize); - *cpp = ocp; - ocp[9] = cs->cs_this; - ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP; - return isize; -} - - -int -slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) -{ - register int changes; - long x; - register struct tcphdr *thp; - register struct iphdr *ip; - register struct cstate *cs; - int len, hdrlen; - unsigned char *cp = icp; - - /* We've got a compressed packet; read the change byte */ - comp->sls_i_compressed++; - if(isize < 3){ - comp->sls_i_error++; - return 0; - } - changes = *cp++; - if(changes & NEW_C){ - /* Make sure the state index is in range, then grab the state. - * If we have a good state index, clear the 'discard' flag. - */ - x = *cp++; /* Read conn index */ - if(x < 0 || x > comp->rslot_limit) - goto bad; - - comp->flags &=~ SLF_TOSS; - comp->recv_current = x; - } else { - /* this packet has an implicit state index. If we've - * had a line error since the last time we got an - * explicit state index, we have to toss the packet. */ - if(comp->flags & SLF_TOSS){ - comp->sls_i_tossed++; - return 0; - } - } - cs = &comp->rstate[comp->recv_current]; - thp = &cs->cs_tcp; - ip = &cs->cs_ip; - - thp->check = *(__sum16 *)cp; - cp += 2; - - thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0; -/* - * we can use the same number for the length of the saved header and - * the current one, because the packet wouldn't have been sent - * as compressed unless the options were the same as the previous one - */ - - hdrlen = ip->ihl * 4 + thp->doff * 4; - - switch(changes & SPECIALS_MASK){ - case SPECIAL_I: /* Echoed terminal traffic */ - { - register short i; - i = ntohs(ip->tot_len) - hdrlen; - thp->ack_seq = htonl( ntohl(thp->ack_seq) + i); - thp->seq = htonl( ntohl(thp->seq) + i); - } - break; - - case SPECIAL_D: /* Unidirectional data */ - thp->seq = htonl( ntohl(thp->seq) + - ntohs(ip->tot_len) - hdrlen); - break; - - default: - if(changes & NEW_U){ - thp->urg = 1; - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->urg_ptr = htons(x); - } else - thp->urg = 0; - if(changes & NEW_W){ - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->window = htons( ntohs(thp->window) + x); - } - if(changes & NEW_A){ - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->ack_seq = htonl( ntohl(thp->ack_seq) + x); - } - if(changes & NEW_S){ - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->seq = htonl( ntohl(thp->seq) + x); - } - break; - } - if(changes & NEW_I){ - if((x = decode(&cp)) == -1) { - goto bad; - } - ip->id = htons (ntohs (ip->id) + x); - } else - ip->id = htons (ntohs (ip->id) + 1); - - /* - * At this point, cp points to the first byte of data in the - * packet. Put the reconstructed TCP and IP headers back on the - * packet. Recalculate IP checksum (but not TCP checksum). - */ - - len = isize - (cp - icp); - if (len < 0) - goto bad; - len += hdrlen; - ip->tot_len = htons(len); - ip->check = 0; - - memmove(icp + hdrlen, cp, len - hdrlen); - - cp = icp; - memcpy(cp, ip, 20); - cp += 20; - - if (ip->ihl > 5) { - memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4); - cp += (ip->ihl - 5) * 4; - } - - put_unaligned(ip_fast_csum(icp, ip->ihl), - &((struct iphdr *)icp)->check); - - memcpy(cp, thp, 20); - cp += 20; - - if (thp->doff > 5) { - memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4); - cp += ((thp->doff) - 5) * 4; - } - - return len; -bad: - comp->sls_i_error++; - return slhc_toss( comp ); -} - - -int -slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) -{ - register struct cstate *cs; - unsigned ihl; - - unsigned char index; - - if(isize < 20) { - /* The packet is shorter than a legal IP header */ - comp->sls_i_runt++; - return slhc_toss( comp ); - } - /* Peek at the IP header's IHL field to find its length */ - ihl = icp[0] & 0xf; - if(ihl < 20 / 4){ - /* The IP header length field is too small */ - comp->sls_i_runt++; - return slhc_toss( comp ); - } - index = icp[9]; - icp[9] = IPPROTO_TCP; - - if (ip_fast_csum(icp, ihl)) { - /* Bad IP header checksum; discard */ - comp->sls_i_badcheck++; - return slhc_toss( comp ); - } - if(index > comp->rslot_limit) { - comp->sls_i_error++; - return slhc_toss(comp); - } - - /* Update local state */ - cs = &comp->rstate[comp->recv_current = index]; - comp->flags &=~ SLF_TOSS; - memcpy(&cs->cs_ip,icp,20); - memcpy(&cs->cs_tcp,icp + ihl*4,20); - if (ihl > 5) - memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4); - if (cs->cs_tcp.doff > 5) - memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4); - cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2; - /* Put headers back on packet - * Neither header checksum is recalculated - */ - comp->sls_i_uncompressed++; - return isize; -} - -int -slhc_toss(struct slcompress *comp) -{ - if ( comp == NULLSLCOMPR ) - return 0; - - comp->flags |= SLF_TOSS; - return 0; -} - -#else /* CONFIG_INET */ - -int -slhc_toss(struct slcompress *comp) -{ - printk(KERN_DEBUG "Called IP function on non IP-system: slhc_toss"); - return -EINVAL; -} -int -slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) -{ - printk(KERN_DEBUG "Called IP function on non IP-system: slhc_uncompress"); - return -EINVAL; -} -int -slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, - unsigned char *ocp, unsigned char **cpp, int compress_cid) -{ - printk(KERN_DEBUG "Called IP function on non IP-system: slhc_compress"); - return -EINVAL; -} - -int -slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) -{ - printk(KERN_DEBUG "Called IP function on non IP-system: slhc_remember"); - return -EINVAL; -} - -void -slhc_free(struct slcompress *comp) -{ - printk(KERN_DEBUG "Called IP function on non IP-system: slhc_free"); -} -struct slcompress * -slhc_init(int rslots, int tslots) -{ - printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init"); - return NULL; -} - -#endif /* CONFIG_INET */ - -/* VJ header compression */ -EXPORT_SYMBOL(slhc_init); -EXPORT_SYMBOL(slhc_free); -EXPORT_SYMBOL(slhc_remember); -EXPORT_SYMBOL(slhc_compress); -EXPORT_SYMBOL(slhc_uncompress); -EXPORT_SYMBOL(slhc_toss); - -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/slip.c b/drivers/net/slip.c deleted file mode 100644 index ba08341..0000000 --- a/drivers/net/slip.c +++ /dev/null @@ -1,1444 +0,0 @@ -/* - * 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, - * Fred N. van Kempen, - * - * 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 - * 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "slip.h" -#ifdef CONFIG_INET -#include -#include -#include -#endif - -#define SLIP_VERSION "0.8.4-NET3.019-NEWTTY" - -static struct net_device **slip_devs; - -static int slip_maxdev = SL_NRUNIT; -module_param(slip_maxdev, int, 0); -MODULE_PARM_DESC(slip_maxdev, "Maximum number of slip devices"); - -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_SLIP6 -static 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_SMART -static 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 int sl_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); -#endif -#ifdef CONFIG_SLIP_MODE_SLIP6 - sl->xdata = 0; - sl->xbits = 0; -#endif - spin_unlock_bh(&sl->lock); - err = 0; - - /* Cleanup */ -err_exit: -#ifdef SL_INCLUDE_CSLIP - kfree(cbuff); - slhc_free(slcomp); -#endif - kfree(xbuff); - kfree(rbuff); - return err; -} - -/* Free a SLIP channel buffers. */ -static void sl_free_bufs(struct slip *sl) -{ - /* Free all SLIP frame buffers. */ - kfree(xchg(&sl->rbuff, NULL)); - kfree(xchg(&sl->xbuff, NULL)); -#ifdef SL_INCLUDE_CSLIP - kfree(xchg(&sl->cbuff, NULL)); - slhc_free(xchg(&sl->slcomp, NULL)); -#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 = kmalloc(len + 4, GFP_ATOMIC); - rbuff = kmalloc(len + 4, GFP_ATOMIC); -#ifdef SL_INCLUDE_CSLIP - cbuff = 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(KERN_WARNING "%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; - dev->stats.tx_dropped++; - } - } - sl->xhead = sl->xbuff; - - if (sl->rcount) { - if (sl->rcount <= len) { - memcpy(sl->rbuff, rbuff, sl->rcount); - } else { - sl->rcount = 0; - dev->stats.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: - kfree(xbuff); - kfree(rbuff); -#ifdef SL_INCLUDE_CSLIP - kfree(cbuff); -#endif - return err; -} - - -/* Set the "sending" flag. This must be atomic hence the set_bit. */ -static inline void sl_lock(struct slip *sl) -{ - netif_stop_queue(sl->dev); -} - - -/* Clear the "sending" flag. This must be atomic, hence the ASM. */ -static inline void sl_unlock(struct slip *sl) -{ - netif_wake_queue(sl->dev); -} - -/* Send one completely decapsulated IP datagram to the IP layer. */ -static void sl_bump(struct slip *sl) -{ - struct net_device *dev = sl->dev; - 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 = sl->rbuff[0]; - if (c & SL_TYPE_COMPRESSED_TCP) { - /* ignore compressed packets when CSLIP is off */ - if (!(sl->mode & SL_MODE_CSLIP)) { - printk(KERN_WARNING "%s: compressed packet ignored\n", dev->name); - return; - } - /* make sure we've reserved enough space for uncompress - to use */ - if (count + 80 > sl->buffsize) { - dev->stats.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(KERN_INFO "%s: header compression turned on\n", dev->name); - } - sl->rbuff[0] &= 0x4f; - if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) - return; - } - } -#endif /* SL_INCLUDE_CSLIP */ - - dev->stats.rx_bytes += count; - - skb = dev_alloc_skb(count); - if (skb == NULL) { - printk(KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name); - dev->stats.rx_dropped++; - return; - } - skb->dev = dev; - memcpy(skb_put(skb, count), sl->rbuff, count); - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IP); - netif_rx_ni(skb); - dev->stats.rx_packets++; -} - -/* Encapsulate one IP datagram and stuff into a TTY queue. */ -static void sl_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(KERN_WARNING "%s: truncating oversized transmit packet!\n", sl->dev->name); - sl->dev->stats.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 the ops->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. - */ - set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); - actual = sl->tty->ops->write(sl->tty, 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 = 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->dev->stats.tx_packets++; - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sl_unlock(sl); - return; - } - - actual = tty->ops->write(tty, sl->xhead, sl->xleft); - sl->xleft -= actual; - sl->xhead += actual; -} - -static void sl_tx_timeout(struct net_device *dev) -{ - struct slip *sl = netdev_priv(dev); - - spin_lock(&sl->lock); - - if (netif_queue_stopped(dev)) { - if (!netif_running(dev)) - goto out; - - /* May be we must check transmitter timeout here ? - * 14 Oct 1994 Dmitry Gorodchanin. - */ -#ifdef SL_CHECK_TRANSMIT - if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) { - /* 20 sec timeout not reached */ - goto out; - } - printk(KERN_WARNING "%s: transmit timed out, %s?\n", - dev->name, - (tty_chars_in_buffer(sl->tty) || sl->xleft) ? - "bad line quality" : "driver error"); - sl->xleft = 0; - clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); - sl_unlock(sl); -#endif - } -out: - spin_unlock(&sl->lock); -} - - -/* Encapsulate an IP datagram and kick it into a TTY queue. */ -static netdev_tx_t -sl_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct slip *sl = netdev_priv(dev); - - spin_lock(&sl->lock); - if (!netif_running(dev)) { - spin_unlock(&sl->lock); - printk(KERN_WARNING "%s: xmit call when iface is down\n", dev->name); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - if (sl->tty == NULL) { - spin_unlock(&sl->lock); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - sl_lock(sl); - dev->stats.tx_bytes += skb->len; - sl_encaps(sl, skb->data, skb->len); - spin_unlock(&sl->lock); - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - - -/****************************************** - * Routines looking at netdevice side. - ******************************************/ - -/* Netdevice UP -> DOWN routine */ - -static int -sl_close(struct net_device *dev) -{ - struct slip *sl = netdev_priv(dev); - - spin_lock_bh(&sl->lock); - if (sl->tty) - /* TTY discipline is running. */ - clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); - netif_stop_queue(dev); - sl->rcount = 0; - sl->xleft = 0; - spin_unlock_bh(&sl->lock); - - return 0; -} - -/* Netdevice DOWN -> UP routine */ - -static int sl_open(struct net_device *dev) -{ - struct slip *sl = netdev_priv(dev); - - if (sl->tty == NULL) - return -ENODEV; - - sl->flags &= (1 << SLF_INUSE); - netif_start_queue(dev); - return 0; -} - -/* Netdevice change MTU request */ - -static int sl_change_mtu(struct net_device *dev, int new_mtu) -{ - struct slip *sl = netdev_priv(dev); - - if (new_mtu < 68 || new_mtu > 65534) - return -EINVAL; - - if (new_mtu != dev->mtu) - return sl_realloc_bufs(sl, new_mtu); - return 0; -} - -/* Netdevice get statistics request */ - -static struct rtnl_link_stats64 * -sl_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) -{ - struct net_device_stats *devstats = &dev->stats; -#ifdef SL_INCLUDE_CSLIP - struct slip *sl = netdev_priv(dev); - struct slcompress *comp = sl->slcomp; -#endif - stats->rx_packets = devstats->rx_packets; - stats->tx_packets = devstats->tx_packets; - stats->rx_bytes = devstats->rx_bytes; - stats->tx_bytes = devstats->tx_bytes; - stats->rx_dropped = devstats->rx_dropped; - stats->tx_dropped = devstats->tx_dropped; - stats->tx_errors = devstats->tx_errors; - stats->rx_errors = devstats->rx_errors; - stats->rx_over_errors = devstats->rx_over_errors; - -#ifdef SL_INCLUDE_CSLIP - if (comp) { - /* Generic compressed statistics */ - stats->rx_compressed = comp->sls_i_compressed; - stats->tx_compressed = comp->sls_o_compressed; - - /* Are we really still needs this? */ - stats->rx_fifo_errors += comp->sls_i_compressed; - stats->rx_dropped += comp->sls_i_tossed; - stats->tx_fifo_errors += comp->sls_o_compressed; - stats->collisions += comp->sls_o_misses; - } -#endif - return stats; -} - -/* Netdevice register callback */ - -static int sl_init(struct net_device *dev) -{ - struct slip *sl = netdev_priv(dev); - - /* - * Finish setting up the DEVICE info. - */ - - dev->mtu = sl->mtu; - dev->type = ARPHRD_SLIP + sl->mode; -#ifdef SL_CHECK_TRANSMIT - dev->watchdog_timeo = 20*HZ; -#endif - return 0; -} - - -static void sl_uninit(struct net_device *dev) -{ - struct slip *sl = netdev_priv(dev); - - sl_free_bufs(sl); -} - -/* Hook the destructor so we can free slip devices at the right point in time */ -static void sl_free_netdev(struct net_device *dev) -{ - int i = dev->base_addr; - free_netdev(dev); - slip_devs[i] = NULL; -} - -static const struct net_device_ops sl_netdev_ops = { - .ndo_init = sl_init, - .ndo_uninit = sl_uninit, - .ndo_open = sl_open, - .ndo_stop = sl_close, - .ndo_start_xmit = sl_xmit, - .ndo_get_stats64 = sl_get_stats64, - .ndo_change_mtu = sl_change_mtu, - .ndo_tx_timeout = sl_tx_timeout, -#ifdef CONFIG_SLIP_SMART - .ndo_do_ioctl = sl_ioctl, -#endif -}; - - -static void sl_setup(struct net_device *dev) -{ - dev->netdev_ops = &sl_netdev_ops; - dev->destructor = sl_free_netdev; - - dev->hard_header_len = 0; - dev->addr_len = 0; - dev->tx_queue_len = 10; - - /* New-style flags. */ - dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST; -} - -/****************************************** - Routines looking at TTY side. - ******************************************/ - - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the 'tty_io' module in the kernel when - * a block of SLIP data has been received, which can now be decapsulated - * and sent on to some IP layer for further processing. This will not - * be re-entered while running but other ldisc functions may be called - * in parallel - */ - -static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct slip *sl = tty->disc_data; - - if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) - return; - - /* Read the characters out of the buffer */ - while (count--) { - if (fp && *fp++) { - if (!test_and_set_bit(SLF_ERROR, &sl->flags)) - sl->dev->stats.rx_errors++; - cp++; - continue; - } -#ifdef CONFIG_SLIP_MODE_SLIP6 - if (sl->mode & SL_MODE_SLIP6) - slip_unesc6(sl, *cp++); - else -#endif - slip_unesc(sl, *cp++); - } -} - -/************************************ - * slip_open helper routines. - ************************************/ - -/* Collect hanged up channels */ -static void sl_sync(void) -{ - int i; - struct net_device *dev; - struct slip *sl; - - for (i = 0; i < slip_maxdev; i++) { - dev = slip_devs[i]; - if (dev == NULL) - break; - - sl = netdev_priv(dev); - if (sl->tty || sl->leased) - continue; - if (dev->flags & IFF_UP) - dev_close(dev); - } -} - - -/* Find a free SLIP channel, and link in this `tty' line. */ -static struct slip *sl_alloc(dev_t line) -{ - int i; - char name[IFNAMSIZ]; - struct net_device *dev = NULL; - struct slip *sl; - - for (i = 0; i < slip_maxdev; i++) { - dev = slip_devs[i]; - if (dev == NULL) - break; - } - /* Sorry, too many, all slots in use */ - if (i >= slip_maxdev) - return NULL; - - sprintf(name, "sl%d", i); - dev = alloc_netdev(sizeof(*sl), name, sl_setup); - if (!dev) - return NULL; - - dev->base_addr = i; - sl = netdev_priv(dev); - - /* Initialize channel control data */ - sl->magic = SLIP_MAGIC; - sl->dev = dev; - spin_lock_init(&sl->lock); - sl->mode = SL_MODE_DEFAULT; -#ifdef CONFIG_SLIP_SMART - /* initialize timer_list struct */ - init_timer(&sl->keepalive_timer); - sl->keepalive_timer.data = (unsigned long)sl; - sl->keepalive_timer.function = sl_keepalive; - init_timer(&sl->outfill_timer); - sl->outfill_timer.data = (unsigned long)sl; - sl->outfill_timer.function = sl_outfill; -#endif - slip_devs[i] = dev; - return sl; -} - -/* - * Open the high-level part of the SLIP channel. - * This function is called by the TTY module when the - * SLIP line discipline is called for. Because we are - * sure the tty line exists, we only have to link it to - * a free SLIP channel... - * - * Called in process context serialized from other ldisc calls. - */ - -static int slip_open(struct tty_struct *tty) -{ - struct slip *sl; - int err; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - - /* RTnetlink lock is misused here to serialize concurrent - opens of slip channels. There are better ways, but it is - the simplest one. - */ - rtnl_lock(); - - /* Collect hanged up channels. */ - sl_sync(); - - sl = tty->disc_data; - - err = -EEXIST; - /* First make sure we're not already connected. */ - if (sl && sl->magic == SLIP_MAGIC) - goto err_exit; - - /* OK. Find a free SLIP channel to use. */ - err = -ENFILE; - sl = sl_alloc(tty_devnum(tty)); - if (sl == NULL) - goto err_exit; - - sl->tty = tty; - tty->disc_data = sl; - sl->pid = current->pid; - - if (!test_bit(SLF_INUSE, &sl->flags)) { - /* Perform the low-level SLIP initialization. */ - err = sl_alloc_bufs(sl, SL_MTU); - if (err) - goto err_free_chan; - - set_bit(SLF_INUSE, &sl->flags); - - err = register_netdevice(sl->dev); - if (err) - goto err_free_bufs; - } - -#ifdef CONFIG_SLIP_SMART - if (sl->keepalive) { - sl->keepalive_timer.expires = jiffies + sl->keepalive * HZ; - add_timer(&sl->keepalive_timer); - } - if (sl->outfill) { - sl->outfill_timer.expires = jiffies + sl->outfill * HZ; - add_timer(&sl->outfill_timer); - } -#endif - - /* Done. We have linked the TTY line to a channel. */ - rtnl_unlock(); - tty->receive_room = 65536; /* We don't flow control */ - - /* TTY layer expects 0 on success */ - return 0; - -err_free_bufs: - sl_free_bufs(sl); - -err_free_chan: - sl->tty = NULL; - tty->disc_data = NULL; - clear_bit(SLF_INUSE, &sl->flags); - -err_exit: - rtnl_unlock(); - - /* Count references from TTY module */ - return err; -} - -/* - * Close down a SLIP channel. - * This means flushing out any pending queues, and then returning. This - * call is serialized against other ldisc functions. - * - * We also use this method fo a hangup event - */ - -static void slip_close(struct tty_struct *tty) -{ - struct slip *sl = tty->disc_data; - - /* First make sure we're connected. */ - if (!sl || sl->magic != SLIP_MAGIC || sl->tty != tty) - return; - - tty->disc_data = NULL; - sl->tty = NULL; - - /* VSV = very important to remove timers */ -#ifdef CONFIG_SLIP_SMART - del_timer_sync(&sl->keepalive_timer); - del_timer_sync(&sl->outfill_timer); -#endif - /* Flush network side */ - unregister_netdev(sl->dev); - /* This will complete via sl_free_netdev */ -} - -static int slip_hangup(struct tty_struct *tty) -{ - slip_close(tty); - return 0; -} - /************************************************************************ - * STANDARD SLIP ENCAPSULATION * - ************************************************************************/ - -static int slip_esc(unsigned char *s, unsigned char *d, int len) -{ - unsigned char *ptr = d; - unsigned char c; - - /* - * Send an initial END character to flush out any - * data that may have accumulated in the receiver - * due to line noise. - */ - - *ptr++ = END; - - /* - * For each byte in the packet, send the appropriate - * character sequence, according to the SLIP protocol. - */ - - while (len-- > 0) { - switch (c = *s++) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - *ptr++ = END; - return ptr - d; -} - -static void slip_unesc(struct slip *sl, unsigned char s) -{ - - switch (s) { - case END: -#ifdef CONFIG_SLIP_SMART - /* drop keeptest bit = VSV */ - if (test_bit(SLF_KEEPTEST, &sl->flags)) - clear_bit(SLF_KEEPTEST, &sl->flags); -#endif - - if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && - (sl->rcount > 2)) - sl_bump(sl); - clear_bit(SLF_ESCAPE, &sl->flags); - sl->rcount = 0; - return; - - case ESC: - set_bit(SLF_ESCAPE, &sl->flags); - return; - case ESC_ESC: - if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) - s = ESC; - break; - case ESC_END: - if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) - s = END; - break; - } - if (!test_bit(SLF_ERROR, &sl->flags)) { - if (sl->rcount < sl->buffsize) { - sl->rbuff[sl->rcount++] = s; - return; - } - sl->dev->stats.rx_over_errors++; - set_bit(SLF_ERROR, &sl->flags); - } -} - - -#ifdef CONFIG_SLIP_MODE_SLIP6 -/************************************************************************ - * 6 BIT SLIP ENCAPSULATION * - ************************************************************************/ - -static int slip_esc6(unsigned char *s, unsigned char *d, int len) -{ - unsigned char *ptr = d; - unsigned char c; - int i; - unsigned short v = 0; - short bits = 0; - - /* - * Send an initial END character to flush out any - * data that may have accumulated in the receiver - * due to line noise. - */ - - *ptr++ = 0x70; - - /* - * Encode the packet into printable ascii characters - */ - - for (i = 0; i < len; ++i) { - v = (v << 8) | s[i]; - bits += 8; - while (bits >= 6) { - bits -= 6; - c = 0x30 + ((v >> bits) & 0x3F); - *ptr++ = c; - } - } - if (bits) { - c = 0x30 + ((v << (6 - bits)) & 0x3F); - *ptr++ = c; - } - *ptr++ = 0x70; - return ptr - d; -} - -static void slip_unesc6(struct slip *sl, unsigned char s) -{ - unsigned char c; - - if (s == 0x70) { -#ifdef CONFIG_SLIP_SMART - /* drop keeptest bit = VSV */ - if (test_bit(SLF_KEEPTEST, &sl->flags)) - clear_bit(SLF_KEEPTEST, &sl->flags); -#endif - - if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && - (sl->rcount > 2)) - sl_bump(sl); - sl->rcount = 0; - sl->xbits = 0; - sl->xdata = 0; - } else if (s >= 0x30 && s < 0x70) { - sl->xdata = (sl->xdata << 6) | ((s - 0x30) & 0x3F); - sl->xbits += 6; - if (sl->xbits >= 8) { - sl->xbits -= 8; - c = (unsigned char)(sl->xdata >> sl->xbits); - if (!test_bit(SLF_ERROR, &sl->flags)) { - if (sl->rcount < sl->buffsize) { - sl->rbuff[sl->rcount++] = c; - return; - } - sl->dev->stats.rx_over_errors++; - set_bit(SLF_ERROR, &sl->flags); - } - } - } -} -#endif /* CONFIG_SLIP_MODE_SLIP6 */ - -/* Perform I/O control on an active SLIP channel. */ -static int slip_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct slip *sl = tty->disc_data; - unsigned int tmp; - int __user *p = (int __user *)arg; - - /* First make sure we're connected. */ - if (!sl || sl->magic != SLIP_MAGIC) - return -EINVAL; - - switch (cmd) { - case SIOCGIFNAME: - tmp = strlen(sl->dev->name) + 1; - if (copy_to_user((void __user *)arg, sl->dev->name, tmp)) - return -EFAULT; - return 0; - - case SIOCGIFENCAP: - if (put_user(sl->mode, p)) - return -EFAULT; - return 0; - - case SIOCSIFENCAP: - if (get_user(tmp, p)) - return -EFAULT; -#ifndef SL_INCLUDE_CSLIP - if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE)) - return -EINVAL; -#else - if ((tmp & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) == - (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) - /* return -EINVAL; */ - tmp &= ~SL_MODE_ADAPTIVE; -#endif -#ifndef CONFIG_SLIP_MODE_SLIP6 - if (tmp & SL_MODE_SLIP6) - return -EINVAL; -#endif - sl->mode = tmp; - sl->dev->type = ARPHRD_SLIP + sl->mode; - return 0; - - case SIOCSIFHWADDR: - return -EINVAL; - -#ifdef CONFIG_SLIP_SMART - /* VSV changes start here */ - case SIOCSKEEPALIVE: - if (get_user(tmp, p)) - return -EFAULT; - if (tmp > 255) /* max for unchar */ - return -EINVAL; - - spin_lock_bh(&sl->lock); - if (!sl->tty) { - spin_unlock_bh(&sl->lock); - return -ENODEV; - } - sl->keepalive = (u8)tmp; - if (sl->keepalive != 0) { - mod_timer(&sl->keepalive_timer, - jiffies + sl->keepalive * HZ); - set_bit(SLF_KEEPTEST, &sl->flags); - } else - del_timer(&sl->keepalive_timer); - spin_unlock_bh(&sl->lock); - return 0; - - case SIOCGKEEPALIVE: - if (put_user(sl->keepalive, p)) - return -EFAULT; - return 0; - - case SIOCSOUTFILL: - if (get_user(tmp, p)) - return -EFAULT; - if (tmp > 255) /* max for unchar */ - return -EINVAL; - spin_lock_bh(&sl->lock); - if (!sl->tty) { - spin_unlock_bh(&sl->lock); - return -ENODEV; - } - sl->outfill = (u8)tmp; - if (sl->outfill != 0) { - mod_timer(&sl->outfill_timer, - jiffies + sl->outfill * HZ); - set_bit(SLF_OUTWAIT, &sl->flags); - } else - del_timer(&sl->outfill_timer); - spin_unlock_bh(&sl->lock); - return 0; - - case SIOCGOUTFILL: - if (put_user(sl->outfill, p)) - return -EFAULT; - return 0; - /* VSV changes end */ -#endif - default: - return tty_mode_ioctl(tty, file, cmd, arg); - } -} - -#ifdef CONFIG_COMPAT -static long slip_compat_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case SIOCGIFNAME: - case SIOCGIFENCAP: - case SIOCSIFENCAP: - case SIOCSIFHWADDR: - case SIOCSKEEPALIVE: - case SIOCGKEEPALIVE: - case SIOCSOUTFILL: - case SIOCGOUTFILL: - return slip_ioctl(tty, file, cmd, - (unsigned long)compat_ptr(arg)); - } - - return -ENOIOCTLCMD; -} -#endif - -/* VSV changes start here */ -#ifdef CONFIG_SLIP_SMART -/* function do_ioctl called from net/core/dev.c - to allow get/set outfill/keepalive parameter - by ifconfig */ - -static int sl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct slip *sl = netdev_priv(dev); - unsigned long *p = (unsigned long *)&rq->ifr_ifru; - - if (sl == NULL) /* Allocation failed ?? */ - return -ENODEV; - - spin_lock_bh(&sl->lock); - - if (!sl->tty) { - spin_unlock_bh(&sl->lock); - return -ENODEV; - } - - switch (cmd) { - case SIOCSKEEPALIVE: - /* max for unchar */ - if ((unsigned)*p > 255) { - spin_unlock_bh(&sl->lock); - return -EINVAL; - } - sl->keepalive = (u8)*p; - if (sl->keepalive != 0) { - sl->keepalive_timer.expires = - jiffies + sl->keepalive * HZ; - mod_timer(&sl->keepalive_timer, - jiffies + sl->keepalive * HZ); - set_bit(SLF_KEEPTEST, &sl->flags); - } else - del_timer(&sl->keepalive_timer); - break; - - case SIOCGKEEPALIVE: - *p = sl->keepalive; - break; - - case SIOCSOUTFILL: - if ((unsigned)*p > 255) { /* max for unchar */ - spin_unlock_bh(&sl->lock); - return -EINVAL; - } - sl->outfill = (u8)*p; - if (sl->outfill != 0) { - mod_timer(&sl->outfill_timer, - jiffies + sl->outfill * HZ); - set_bit(SLF_OUTWAIT, &sl->flags); - } else - del_timer(&sl->outfill_timer); - break; - - case SIOCGOUTFILL: - *p = sl->outfill; - break; - - case SIOCSLEASE: - /* Resolve race condition, when ioctl'ing hanged up - and opened by another process device. - */ - if (sl->tty != current->signal->tty && - sl->pid != current->pid) { - spin_unlock_bh(&sl->lock); - return -EPERM; - } - sl->leased = 0; - if (*p) - sl->leased = 1; - break; - - case SIOCGLEASE: - *p = sl->leased; - } - spin_unlock_bh(&sl->lock); - return 0; -} -#endif -/* VSV changes end */ - -static struct tty_ldisc_ops sl_ldisc = { - .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, - .name = "slip", - .open = slip_open, - .close = slip_close, - .hangup = slip_hangup, - .ioctl = slip_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = slip_compat_ioctl, -#endif - .receive_buf = slip_receive_buf, - .write_wakeup = slip_write_wakeup, -}; - -static int __init slip_init(void) -{ - int status; - - if (slip_maxdev < 4) - slip_maxdev = 4; /* Sanity */ - - printk(KERN_INFO "SLIP: version %s (dynamic channels, max=%d)" -#ifdef CONFIG_SLIP_MODE_SLIP6 - " (6 bit encapsulation enabled)" -#endif - ".\n", - SLIP_VERSION, slip_maxdev); -#if defined(SL_INCLUDE_CSLIP) - printk(KERN_INFO "CSLIP: code copyright 1989 Regents of the University of California.\n"); -#endif -#ifdef CONFIG_SLIP_SMART - printk(KERN_INFO "SLIP linefill/keepalive option.\n"); -#endif - - slip_devs = kzalloc(sizeof(struct net_device *)*slip_maxdev, - GFP_KERNEL); - if (!slip_devs) { - printk(KERN_ERR "SLIP: Can't allocate slip devices array.\n"); - return -ENOMEM; - } - - /* Fill in our line protocol discipline, and register it */ - status = tty_register_ldisc(N_SLIP, &sl_ldisc); - if (status != 0) { - printk(KERN_ERR "SLIP: can't register line discipline (err = %d)\n", status); - kfree(slip_devs); - } - return status; -} - -static void __exit slip_exit(void) -{ - int i; - struct net_device *dev; - struct slip *sl; - unsigned long timeout = jiffies + HZ; - int busy = 0; - - if (slip_devs == NULL) - return; - - /* First of all: check for active disciplines and hangup them. - */ - do { - if (busy) - msleep_interruptible(100); - - busy = 0; - for (i = 0; i < slip_maxdev; i++) { - dev = slip_devs[i]; - if (!dev) - continue; - sl = netdev_priv(dev); - spin_lock_bh(&sl->lock); - if (sl->tty) { - busy++; - tty_hangup(sl->tty); - } - spin_unlock_bh(&sl->lock); - } - } while (busy && time_before(jiffies, timeout)); - - /* FIXME: hangup is async so we should wait when doing this second - phase */ - - for (i = 0; i < slip_maxdev; i++) { - dev = slip_devs[i]; - if (!dev) - continue; - slip_devs[i] = NULL; - - sl = netdev_priv(dev); - if (sl->tty) { - printk(KERN_ERR "%s: tty discipline still running\n", - dev->name); - /* Intentionally leak the control block. */ - dev->destructor = NULL; - } - - unregister_netdev(dev); - } - - kfree(slip_devs); - slip_devs = NULL; - - i = tty_unregister_ldisc(N_SLIP); - if (i != 0) - printk(KERN_ERR "SLIP: can't unregister line discipline (err = %d)\n", i); -} - -module_init(slip_init); -module_exit(slip_exit); - -#ifdef CONFIG_SLIP_SMART -/* - * This is start of the code for multislip style line checking - * added by Stanislav Voronyi. All changes before marked VSV - */ - -static void sl_outfill(unsigned long sls) -{ - struct slip *sl = (struct slip *)sls; - - spin_lock(&sl->lock); - - if (sl->tty == NULL) - goto out; - - if (sl->outfill) { - if (test_bit(SLF_OUTWAIT, &sl->flags)) { - /* no packets were transmitted, do outfill */ -#ifdef CONFIG_SLIP_MODE_SLIP6 - unsigned char s = (sl->mode & SL_MODE_SLIP6)?0x70:END; -#else - unsigned char s = END; -#endif - /* put END into tty queue. Is it right ??? */ - if (!netif_queue_stopped(sl->dev)) { - /* if device busy no outfill */ - sl->tty->ops->write(sl->tty, &s, 1); - } - } else - set_bit(SLF_OUTWAIT, &sl->flags); - - mod_timer(&sl->outfill_timer, jiffies+sl->outfill*HZ); - } -out: - spin_unlock(&sl->lock); -} - -static void sl_keepalive(unsigned long sls) -{ - struct slip *sl = (struct slip *)sls; - - spin_lock(&sl->lock); - - if (sl->tty == NULL) - goto out; - - if (sl->keepalive) { - if (test_bit(SLF_KEEPTEST, &sl->flags)) { - /* keepalive still high :(, we must hangup */ - if (sl->outfill) - /* outfill timer must be deleted too */ - (void)del_timer(&sl->outfill_timer); - printk(KERN_DEBUG "%s: no packets received during keepalive timeout, hangup.\n", sl->dev->name); - /* this must hangup tty & close slip */ - tty_hangup(sl->tty); - /* I think we need not something else */ - goto out; - } else - set_bit(SLF_KEEPTEST, &sl->flags); - - mod_timer(&sl->keepalive_timer, jiffies+sl->keepalive*HZ); - } -out: - spin_unlock(&sl->lock); -} - -#endif -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_SLIP); diff --git a/drivers/net/slip.h b/drivers/net/slip.h deleted file mode 100644 index 67673cf..0000000 --- a/drivers/net/slip.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * slip.h Define the SLIP device driver interface and constants. - * - * NOTE: THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY - * AS SOON AS POSSIBLE! - * - * Version: @(#)slip.h 1.2.0 03/28/93 - * - * Fixes: - * Alan Cox : Added slip mtu field. - * Matt Dillon : Printable slip (borrowed from net2e) - * Alan Cox : Added SL_SLIP_LOTS - * Dmitry Gorodchanin : A lot of changes in the 'struct slip' - * Dmitry Gorodchanin : Added CSLIP statistics. - * Stanislav Voronyi : Make line checking as created by - * Igor Chechik, RELCOM Corp. - * Craig Schlenter : Fixed #define bug that caused - * CSLIP telnets to hang in 1.3.61-6 - * - * Author: Fred N. van Kempen, - */ -#ifndef _LINUX_SLIP_H -#define _LINUX_SLIP_H - - -#if defined(CONFIG_INET) && defined(CONFIG_SLIP_COMPRESSED) -# define SL_INCLUDE_CSLIP -#endif - -#ifdef SL_INCLUDE_CSLIP -# define SL_MODE_DEFAULT SL_MODE_ADAPTIVE -#else -# define SL_MODE_DEFAULT SL_MODE_SLIP -#endif - -/* SLIP configuration. */ -#define SL_NRUNIT 256 /* MAX number of SLIP channels; - This can be overridden with - insmod -oslip_maxdev=nnn */ -#define SL_MTU 296 /* 296; I am used to 600- FvK */ - -/* SLIP protocol characters. */ -#define END 0300 /* indicates end of frame */ -#define ESC 0333 /* indicates byte stuffing */ -#define ESC_END 0334 /* ESC ESC_END means END 'data' */ -#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ - - -struct slip { - int magic; - - /* Various fields. */ - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - spinlock_t lock; - -#ifdef SL_INCLUDE_CSLIP - struct slcompress *slcomp; /* for header compression */ - unsigned char *cbuff; /* compression buffer */ -#endif - - /* These are pointers to the malloc()ed frame buffers. */ - unsigned char *rbuff; /* receiver buffer */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* pointer to next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ - -#ifdef CONFIG_SLIP_MODE_SLIP6 - int xdata, xbits; /* 6 bit slip controls */ -#endif - - unsigned long flags; /* Flag values/ mode etc */ -#define SLF_INUSE 0 /* Channel in use */ -#define SLF_ESCAPE 1 /* ESC received */ -#define SLF_ERROR 2 /* Parity, etc. error */ -#define SLF_KEEPTEST 3 /* Keepalive test flag */ -#define SLF_OUTWAIT 4 /* is outpacket was flag */ - - unsigned char mode; /* SLIP mode */ - unsigned char leased; - pid_t pid; -#define SL_MODE_SLIP 0 -#define SL_MODE_CSLIP 1 -#define SL_MODE_SLIP6 2 /* Matt Dillon's printable slip */ -#define SL_MODE_CSLIP6 (SL_MODE_SLIP6|SL_MODE_CSLIP) -#define SL_MODE_AX25 4 -#define SL_MODE_ADAPTIVE 8 -#ifdef CONFIG_SLIP_SMART - unsigned char outfill; /* # of sec between outfill packet */ - unsigned char keepalive; /* keepalive seconds */ - struct timer_list outfill_timer; - struct timer_list keepalive_timer; -#endif -}; - -#define SLIP_MAGIC 0x5302 - -#endif /* _LINUX_SLIP.H */ diff --git a/drivers/net/slip/Kconfig b/drivers/net/slip/Kconfig new file mode 100644 index 0000000..211b160 --- /dev/null +++ b/drivers/net/slip/Kconfig @@ -0,0 +1,79 @@ +# +# SLIP network device configuration +# + +config SLIP + tristate "SLIP (serial line) support" + ---help--- + Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to + connect to your Internet service provider or to connect to some + other local Unix box or if you want to configure your Linux box as a + Slip/CSlip server for other people to dial in. SLIP (Serial Line + Internet Protocol) is a protocol used to send Internet traffic over + serial connections such as telephone lines or null modem cables; + nowadays, the protocol PPP is more commonly used for this same + purpose. + + Normally, your access provider has to support SLIP in order for you + to be able to use it, but there is now a SLIP emulator called SLiRP + around (available from + ) which + allows you to use SLIP over a regular dial up shell connection. If + you plan to use SLiRP, make sure to say Y to CSLIP, below. The + NET-3-HOWTO, available from + , explains how to + configure SLIP. Note that you don't need this option if you just + want to run term (term is a program which gives you almost full + Internet connectivity if you have a regular dial up shell account on + some Internet connected Unix computer. Read + ). SLIP + support will enlarge your kernel by about 4 KB. If unsure, say N. + + To compile this driver as a module, choose M here. The module + will be called slip. + +config SLHC + tristate + ---help--- + This option enables Van Jacobsen serial line header compression + routines. + +if SLIP + +config SLIP_COMPRESSED + bool "CSLIP compressed headers" + depends on SLIP + select SLHC + ---help--- + This protocol is faster than SLIP because it uses compression on the + TCP/IP headers (not on the data itself), but it has to be supported + on both ends. Ask your access provider if you are not sure and + answer Y, just in case. You will still be able to use plain SLIP. If + you plan to use SLiRP, the SLIP emulator (available from + ) which + allows you to use SLIP over a regular dial up shell connection, you + definitely want to say Y here. The NET-3-HOWTO, available from + , explains how to configure + CSLIP. This won't enlarge your kernel. + +config SLIP_SMART + bool "Keepalive and linefill" + depends on SLIP + ---help--- + Adds additional capabilities to the SLIP driver to support the + RELCOM line fill and keepalive monitoring. Ideal on poor quality + analogue lines. + +config SLIP_MODE_SLIP6 + bool "Six bit SLIP encapsulation" + depends on SLIP + ---help--- + Just occasionally you may need to run IP over hostile serial + networks that don't pass all control characters or are only seven + bit. Saying Y here adds an extra mode you can use with SLIP: + "slip6". In this mode, SLIP will only send normal ASCII symbols over + the serial device. Naturally, this has to be supported at the other + end of the link as well. It's good enough, for example, to run IP + over the async ports of a Camtec JNT Pad. If unsure, say N. + +endif # SLIP diff --git a/drivers/net/slip/Makefile b/drivers/net/slip/Makefile new file mode 100644 index 0000000..e3ebc59 --- /dev/null +++ b/drivers/net/slip/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the SLIP network device drivers. +# + +obj-$(CONFIG_SLIP) += slip.o +obj-$(CONFIG_SLHC) += slhc.o diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c new file mode 100644 index 0000000..0a0a664 --- /dev/null +++ b/drivers/net/slip/slhc.c @@ -0,0 +1,742 @@ +/* + * Routines to compress and uncompress tcp packets (for transmission + * over low speed serial lines). + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + * + * + * modified for KA9Q Internet Software Package by + * Katie Stevens (dkstevens@ucdavis.edu) + * University of California, Davis + * Computing Services + * - 01-31-90 initial adaptation (from 1.19) + * PPP.05 02-15-90 [ks] + * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression + * PPP.15 09-90 [ks] improve mbuf handling + * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities + * + * - Feb 1991 Bill_Simpson@um.cc.umich.edu + * variable number of conversation slots + * allow zero or one slots + * separate routines + * status display + * - Jul 1994 Dmitry Gorodchanin + * Fixes for memory leaks. + * - Oct 1994 Dmitry Gorodchanin + * Modularization. + * - Jan 1995 Bjorn Ekwall + * Use ip_fast_csum from ip.h + * - July 1995 Christos A. Polyzols + * Spotted bug in tcp option checking + * + * + * This module is a difficult issue. It's clearly inet code but it's also clearly + * driver code belonging close to PPP and SLIP + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_INET +/* Entire module is for IP only */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned char *encode(unsigned char *cp, unsigned short n); +static long decode(unsigned char **cpp); +static unsigned char * put16(unsigned char *cp, unsigned short x); +static unsigned short pull16(unsigned char **cpp); + +/* Initialize compression data structure + * slots must be in range 0 to 255 (zero meaning no compression) + */ +struct slcompress * +slhc_init(int rslots, int tslots) +{ + register short i; + register struct cstate *ts; + struct slcompress *comp; + + comp = kzalloc(sizeof(struct slcompress), GFP_KERNEL); + if (! comp) + goto out_fail; + + if ( rslots > 0 && rslots < 256 ) { + size_t rsize = rslots * sizeof(struct cstate); + comp->rstate = kzalloc(rsize, GFP_KERNEL); + if (! comp->rstate) + goto out_free; + comp->rslot_limit = rslots - 1; + } + + if ( tslots > 0 && tslots < 256 ) { + size_t tsize = tslots * sizeof(struct cstate); + comp->tstate = kzalloc(tsize, GFP_KERNEL); + if (! comp->tstate) + goto out_free2; + comp->tslot_limit = tslots - 1; + } + + comp->xmit_oldest = 0; + comp->xmit_current = 255; + comp->recv_current = 255; + /* + * don't accept any packets with implicit index until we get + * one with an explicit index. Otherwise the uncompress code + * will try to use connection 255, which is almost certainly + * out of range + */ + comp->flags |= SLF_TOSS; + + if ( tslots > 0 ) { + ts = comp->tstate; + for(i = comp->tslot_limit; i > 0; --i){ + ts[i].cs_this = i; + ts[i].next = &(ts[i - 1]); + } + ts[0].next = &(ts[comp->tslot_limit]); + ts[0].cs_this = 0; + } + return comp; + +out_free2: + kfree(comp->rstate); +out_free: + kfree(comp); +out_fail: + return NULL; +} + + +/* Free a compression data structure */ +void +slhc_free(struct slcompress *comp) +{ + if ( comp == NULLSLCOMPR ) + return; + + if ( comp->tstate != NULLSLSTATE ) + kfree( comp->tstate ); + + if ( comp->rstate != NULLSLSTATE ) + kfree( comp->rstate ); + + kfree( comp ); +} + + +/* Put a short in host order into a char array in network order */ +static inline unsigned char * +put16(unsigned char *cp, unsigned short x) +{ + *cp++ = x >> 8; + *cp++ = x; + + return cp; +} + + +/* Encode a number */ +static unsigned char * +encode(unsigned char *cp, unsigned short n) +{ + if(n >= 256 || n == 0){ + *cp++ = 0; + cp = put16(cp,n); + } else { + *cp++ = n; + } + return cp; +} + +/* Pull a 16-bit integer in host order from buffer in network byte order */ +static unsigned short +pull16(unsigned char **cpp) +{ + short rval; + + rval = *(*cpp)++; + rval <<= 8; + rval |= *(*cpp)++; + return rval; +} + +/* Decode a number */ +static long +decode(unsigned char **cpp) +{ + register int x; + + x = *(*cpp)++; + if(x == 0){ + return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ + } else { + return x & 0xff; /* -1 if PULLCHAR returned error */ + } +} + +/* + * icp and isize are the original packet. + * ocp is a place to put a copy if necessary. + * cpp is initially a pointer to icp. If the copy is used, + * change it to ocp. + */ + +int +slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, + unsigned char *ocp, unsigned char **cpp, int compress_cid) +{ + register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]); + register struct cstate *lcs = ocs; + register struct cstate *cs = lcs->next; + register unsigned long deltaS, deltaA; + register short changes = 0; + int hlen; + unsigned char new_seq[16]; + register unsigned char *cp = new_seq; + struct iphdr *ip; + struct tcphdr *th, *oth; + __sum16 csum; + + + /* + * Don't play with runt packets. + */ + + if(isizeprotocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) { + /* Send as regular IP */ + if(ip->protocol != IPPROTO_TCP) + comp->sls_o_nontcp++; + else + comp->sls_o_tcp++; + return isize; + } + /* Extract TCP header */ + + th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4); + hlen = ip->ihl*4 + th->doff*4; + + /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or + * some other control bit is set). Also uncompressible if + * it's a runt. + */ + if(hlen > isize || th->syn || th->fin || th->rst || + ! (th->ack)){ + /* TCP connection stuff; send as regular IP */ + comp->sls_o_tcp++; + return isize; + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way, + * we need to locate (or create) the connection state. + * + * States are kept in a circularly linked list with + * xmit_oldest pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + for ( ; ; ) { + if( ip->saddr == cs->cs_ip.saddr + && ip->daddr == cs->cs_ip.daddr + && th->source == cs->cs_tcp.source + && th->dest == cs->cs_tcp.dest) + goto found; + + /* if current equal oldest, at end of list */ + if ( cs == ocs ) + break; + lcs = cs; + cs = cs->next; + comp->sls_o_searches++; + } + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * xmit_oldest to update the lru linkage. + */ + comp->sls_o_misses++; + comp->xmit_oldest = lcs->cs_this; + goto uncompressed; + +found: + /* + * Found it -- move to the front on the connection list. + */ + if(lcs == ocs) { + /* found at most recently used */ + } else if (cs == ocs) { + /* found at least recently used */ + comp->xmit_oldest = lcs->cs_this; + } else { + /* more than 2 elements */ + lcs->next = cs->next; + cs->next = ocs->next; + ocs->next = cs; + } + + /* + * Make sure that only what we expect to change changed. + * Check the following: + * IP protocol version, header length & type of service. + * The "Don't fragment" bit. + * The time-to-live field. + * The TCP header length. + * IP options, if any. + * TCP options, if any. + * If any of these things are different between the previous & + * current datagram, we send the current datagram `uncompressed'. + */ + oth = &cs->cs_tcp; + + if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl + || ip->tos != cs->cs_ip.tos + || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)) + || ip->ttl != cs->cs_ip.ttl + || th->doff != cs->cs_tcp.doff + || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) + || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){ + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if(th->urg){ + deltaS = ntohs(th->urg_ptr); + cp = encode(cp,deltaS); + changes |= NEW_U; + } else if(th->urg_ptr != oth->urg_ptr){ + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){ + cp = encode(cp,deltaS); + changes |= NEW_W; + } + if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){ + if(deltaA > 0x0000ffff) + goto uncompressed; + cp = encode(cp,deltaA); + changes |= NEW_A; + } + if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){ + if(deltaS > 0x0000ffff) + goto uncompressed; + cp = encode(cp,deltaS); + changes |= NEW_S; + } + + switch(changes){ + case 0: /* Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if(ip->tot_len != cs->cs_ip.tot_len && + ntohs(cs->cs_ip.tot_len) == hlen) + break; + goto uncompressed; + break; + case SPECIAL_I: + case SPECIAL_D: + /* actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + case NEW_S|NEW_A: + if(deltaS == deltaA && + deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + case NEW_S: + if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id); + if(deltaS != 1){ + cp = encode(cp,deltaS); + changes |= NEW_I; + } + if(th->psh) + changes |= TCP_PUSH_BIT; + /* Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + csum = th->check; + memcpy(&cs->cs_ip,ip,20); + memcpy(&cs->cs_tcp,th,20); + /* We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. + */ + deltaS = cp - new_seq; + if(compress_cid == 0 || comp->xmit_current != cs->cs_this){ + cp = ocp; + *cpp = ocp; + *cp++ = changes | NEW_C; + *cp++ = cs->cs_this; + comp->xmit_current = cs->cs_this; + } else { + cp = ocp; + *cpp = ocp; + *cp++ = changes; + } + *(__sum16 *)cp = csum; + cp += 2; +/* deltaS is now the size of the change section of the compressed header */ + memcpy(cp,new_seq,deltaS); /* Write list of deltas */ + memcpy(cp+deltaS,icp+hlen,isize-hlen); + comp->sls_o_compressed++; + ocp[0] |= SL_TYPE_COMPRESSED_TCP; + return isize - hlen + deltaS + (cp - ocp); + + /* Update connection state cs & send uncompressed packet (i.e., + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + memcpy(&cs->cs_ip,ip,20); + memcpy(&cs->cs_tcp,th,20); + if (ip->ihl > 5) + memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4); + if (th->doff > 5) + memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4); + comp->xmit_current = cs->cs_this; + comp->sls_o_uncompressed++; + memcpy(ocp, icp, isize); + *cpp = ocp; + ocp[9] = cs->cs_this; + ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP; + return isize; +} + + +int +slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) +{ + register int changes; + long x; + register struct tcphdr *thp; + register struct iphdr *ip; + register struct cstate *cs; + int len, hdrlen; + unsigned char *cp = icp; + + /* We've got a compressed packet; read the change byte */ + comp->sls_i_compressed++; + if(isize < 3){ + comp->sls_i_error++; + return 0; + } + changes = *cp++; + if(changes & NEW_C){ + /* Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + x = *cp++; /* Read conn index */ + if(x < 0 || x > comp->rslot_limit) + goto bad; + + comp->flags &=~ SLF_TOSS; + comp->recv_current = x; + } else { + /* this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. */ + if(comp->flags & SLF_TOSS){ + comp->sls_i_tossed++; + return 0; + } + } + cs = &comp->rstate[comp->recv_current]; + thp = &cs->cs_tcp; + ip = &cs->cs_ip; + + thp->check = *(__sum16 *)cp; + cp += 2; + + thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0; +/* + * we can use the same number for the length of the saved header and + * the current one, because the packet wouldn't have been sent + * as compressed unless the options were the same as the previous one + */ + + hdrlen = ip->ihl * 4 + thp->doff * 4; + + switch(changes & SPECIALS_MASK){ + case SPECIAL_I: /* Echoed terminal traffic */ + { + register short i; + i = ntohs(ip->tot_len) - hdrlen; + thp->ack_seq = htonl( ntohl(thp->ack_seq) + i); + thp->seq = htonl( ntohl(thp->seq) + i); + } + break; + + case SPECIAL_D: /* Unidirectional data */ + thp->seq = htonl( ntohl(thp->seq) + + ntohs(ip->tot_len) - hdrlen); + break; + + default: + if(changes & NEW_U){ + thp->urg = 1; + if((x = decode(&cp)) == -1) { + goto bad; + } + thp->urg_ptr = htons(x); + } else + thp->urg = 0; + if(changes & NEW_W){ + if((x = decode(&cp)) == -1) { + goto bad; + } + thp->window = htons( ntohs(thp->window) + x); + } + if(changes & NEW_A){ + if((x = decode(&cp)) == -1) { + goto bad; + } + thp->ack_seq = htonl( ntohl(thp->ack_seq) + x); + } + if(changes & NEW_S){ + if((x = decode(&cp)) == -1) { + goto bad; + } + thp->seq = htonl( ntohl(thp->seq) + x); + } + break; + } + if(changes & NEW_I){ + if((x = decode(&cp)) == -1) { + goto bad; + } + ip->id = htons (ntohs (ip->id) + x); + } else + ip->id = htons (ntohs (ip->id) + 1); + + /* + * At this point, cp points to the first byte of data in the + * packet. Put the reconstructed TCP and IP headers back on the + * packet. Recalculate IP checksum (but not TCP checksum). + */ + + len = isize - (cp - icp); + if (len < 0) + goto bad; + len += hdrlen; + ip->tot_len = htons(len); + ip->check = 0; + + memmove(icp + hdrlen, cp, len - hdrlen); + + cp = icp; + memcpy(cp, ip, 20); + cp += 20; + + if (ip->ihl > 5) { + memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4); + cp += (ip->ihl - 5) * 4; + } + + put_unaligned(ip_fast_csum(icp, ip->ihl), + &((struct iphdr *)icp)->check); + + memcpy(cp, thp, 20); + cp += 20; + + if (thp->doff > 5) { + memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4); + cp += ((thp->doff) - 5) * 4; + } + + return len; +bad: + comp->sls_i_error++; + return slhc_toss( comp ); +} + + +int +slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) +{ + register struct cstate *cs; + unsigned ihl; + + unsigned char index; + + if(isize < 20) { + /* The packet is shorter than a legal IP header */ + comp->sls_i_runt++; + return slhc_toss( comp ); + } + /* Peek at the IP header's IHL field to find its length */ + ihl = icp[0] & 0xf; + if(ihl < 20 / 4){ + /* The IP header length field is too small */ + comp->sls_i_runt++; + return slhc_toss( comp ); + } + index = icp[9]; + icp[9] = IPPROTO_TCP; + + if (ip_fast_csum(icp, ihl)) { + /* Bad IP header checksum; discard */ + comp->sls_i_badcheck++; + return slhc_toss( comp ); + } + if(index > comp->rslot_limit) { + comp->sls_i_error++; + return slhc_toss(comp); + } + + /* Update local state */ + cs = &comp->rstate[comp->recv_current = index]; + comp->flags &=~ SLF_TOSS; + memcpy(&cs->cs_ip,icp,20); + memcpy(&cs->cs_tcp,icp + ihl*4,20); + if (ihl > 5) + memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4); + if (cs->cs_tcp.doff > 5) + memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4); + cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2; + /* Put headers back on packet + * Neither header checksum is recalculated + */ + comp->sls_i_uncompressed++; + return isize; +} + +int +slhc_toss(struct slcompress *comp) +{ + if ( comp == NULLSLCOMPR ) + return 0; + + comp->flags |= SLF_TOSS; + return 0; +} + +#else /* CONFIG_INET */ + +int +slhc_toss(struct slcompress *comp) +{ + printk(KERN_DEBUG "Called IP function on non IP-system: slhc_toss"); + return -EINVAL; +} +int +slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) +{ + printk(KERN_DEBUG "Called IP function on non IP-system: slhc_uncompress"); + return -EINVAL; +} +int +slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, + unsigned char *ocp, unsigned char **cpp, int compress_cid) +{ + printk(KERN_DEBUG "Called IP function on non IP-system: slhc_compress"); + return -EINVAL; +} + +int +slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) +{ + printk(KERN_DEBUG "Called IP function on non IP-system: slhc_remember"); + return -EINVAL; +} + +void +slhc_free(struct slcompress *comp) +{ + printk(KERN_DEBUG "Called IP function on non IP-system: slhc_free"); +} +struct slcompress * +slhc_init(int rslots, int tslots) +{ + printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init"); + return NULL; +} + +#endif /* CONFIG_INET */ + +/* VJ header compression */ +EXPORT_SYMBOL(slhc_init); +EXPORT_SYMBOL(slhc_free); +EXPORT_SYMBOL(slhc_remember); +EXPORT_SYMBOL(slhc_compress); +EXPORT_SYMBOL(slhc_uncompress); +EXPORT_SYMBOL(slhc_toss); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c new file mode 100644 index 0000000..ba08341 --- /dev/null +++ b/drivers/net/slip/slip.c @@ -0,0 +1,1444 @@ +/* + * 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, + * Fred N. van Kempen, + * + * 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 + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "slip.h" +#ifdef CONFIG_INET +#include +#include +#include +#endif + +#define SLIP_VERSION "0.8.4-NET3.019-NEWTTY" + +static struct net_device **slip_devs; + +static int slip_maxdev = SL_NRUNIT; +module_param(slip_maxdev, int, 0); +MODULE_PARM_DESC(slip_maxdev, "Maximum number of slip devices"); + +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_SLIP6 +static 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_SMART +static 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 int sl_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); +#endif +#ifdef CONFIG_SLIP_MODE_SLIP6 + sl->xdata = 0; + sl->xbits = 0; +#endif + spin_unlock_bh(&sl->lock); + err = 0; + + /* Cleanup */ +err_exit: +#ifdef SL_INCLUDE_CSLIP + kfree(cbuff); + slhc_free(slcomp); +#endif + kfree(xbuff); + kfree(rbuff); + return err; +} + +/* Free a SLIP channel buffers. */ +static void sl_free_bufs(struct slip *sl) +{ + /* Free all SLIP frame buffers. */ + kfree(xchg(&sl->rbuff, NULL)); + kfree(xchg(&sl->xbuff, NULL)); +#ifdef SL_INCLUDE_CSLIP + kfree(xchg(&sl->cbuff, NULL)); + slhc_free(xchg(&sl->slcomp, NULL)); +#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 = kmalloc(len + 4, GFP_ATOMIC); + rbuff = kmalloc(len + 4, GFP_ATOMIC); +#ifdef SL_INCLUDE_CSLIP + cbuff = 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(KERN_WARNING "%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; + dev->stats.tx_dropped++; + } + } + sl->xhead = sl->xbuff; + + if (sl->rcount) { + if (sl->rcount <= len) { + memcpy(sl->rbuff, rbuff, sl->rcount); + } else { + sl->rcount = 0; + dev->stats.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: + kfree(xbuff); + kfree(rbuff); +#ifdef SL_INCLUDE_CSLIP + kfree(cbuff); +#endif + return err; +} + + +/* Set the "sending" flag. This must be atomic hence the set_bit. */ +static inline void sl_lock(struct slip *sl) +{ + netif_stop_queue(sl->dev); +} + + +/* Clear the "sending" flag. This must be atomic, hence the ASM. */ +static inline void sl_unlock(struct slip *sl) +{ + netif_wake_queue(sl->dev); +} + +/* Send one completely decapsulated IP datagram to the IP layer. */ +static void sl_bump(struct slip *sl) +{ + struct net_device *dev = sl->dev; + 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 = sl->rbuff[0]; + if (c & SL_TYPE_COMPRESSED_TCP) { + /* ignore compressed packets when CSLIP is off */ + if (!(sl->mode & SL_MODE_CSLIP)) { + printk(KERN_WARNING "%s: compressed packet ignored\n", dev->name); + return; + } + /* make sure we've reserved enough space for uncompress + to use */ + if (count + 80 > sl->buffsize) { + dev->stats.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(KERN_INFO "%s: header compression turned on\n", dev->name); + } + sl->rbuff[0] &= 0x4f; + if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) + return; + } + } +#endif /* SL_INCLUDE_CSLIP */ + + dev->stats.rx_bytes += count; + + skb = dev_alloc_skb(count); + if (skb == NULL) { + printk(KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name); + dev->stats.rx_dropped++; + return; + } + skb->dev = dev; + memcpy(skb_put(skb, count), sl->rbuff, count); + skb_reset_mac_header(skb); + skb->protocol = htons(ETH_P_IP); + netif_rx_ni(skb); + dev->stats.rx_packets++; +} + +/* Encapsulate one IP datagram and stuff into a TTY queue. */ +static void sl_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(KERN_WARNING "%s: truncating oversized transmit packet!\n", sl->dev->name); + sl->dev->stats.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 the ops->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. + */ + set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); + actual = sl->tty->ops->write(sl->tty, 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 = 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->dev->stats.tx_packets++; + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + sl_unlock(sl); + return; + } + + actual = tty->ops->write(tty, sl->xhead, sl->xleft); + sl->xleft -= actual; + sl->xhead += actual; +} + +static void sl_tx_timeout(struct net_device *dev) +{ + struct slip *sl = netdev_priv(dev); + + spin_lock(&sl->lock); + + if (netif_queue_stopped(dev)) { + if (!netif_running(dev)) + goto out; + + /* May be we must check transmitter timeout here ? + * 14 Oct 1994 Dmitry Gorodchanin. + */ +#ifdef SL_CHECK_TRANSMIT + if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) { + /* 20 sec timeout not reached */ + goto out; + } + printk(KERN_WARNING "%s: transmit timed out, %s?\n", + dev->name, + (tty_chars_in_buffer(sl->tty) || sl->xleft) ? + "bad line quality" : "driver error"); + sl->xleft = 0; + clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); + sl_unlock(sl); +#endif + } +out: + spin_unlock(&sl->lock); +} + + +/* Encapsulate an IP datagram and kick it into a TTY queue. */ +static netdev_tx_t +sl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct slip *sl = netdev_priv(dev); + + spin_lock(&sl->lock); + if (!netif_running(dev)) { + spin_unlock(&sl->lock); + printk(KERN_WARNING "%s: xmit call when iface is down\n", dev->name); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + if (sl->tty == NULL) { + spin_unlock(&sl->lock); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + sl_lock(sl); + dev->stats.tx_bytes += skb->len; + sl_encaps(sl, skb->data, skb->len); + spin_unlock(&sl->lock); + + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + + +/****************************************** + * Routines looking at netdevice side. + ******************************************/ + +/* Netdevice UP -> DOWN routine */ + +static int +sl_close(struct net_device *dev) +{ + struct slip *sl = netdev_priv(dev); + + spin_lock_bh(&sl->lock); + if (sl->tty) + /* TTY discipline is running. */ + clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); + netif_stop_queue(dev); + sl->rcount = 0; + sl->xleft = 0; + spin_unlock_bh(&sl->lock); + + return 0; +} + +/* Netdevice DOWN -> UP routine */ + +static int sl_open(struct net_device *dev) +{ + struct slip *sl = netdev_priv(dev); + + if (sl->tty == NULL) + return -ENODEV; + + sl->flags &= (1 << SLF_INUSE); + netif_start_queue(dev); + return 0; +} + +/* Netdevice change MTU request */ + +static int sl_change_mtu(struct net_device *dev, int new_mtu) +{ + struct slip *sl = netdev_priv(dev); + + if (new_mtu < 68 || new_mtu > 65534) + return -EINVAL; + + if (new_mtu != dev->mtu) + return sl_realloc_bufs(sl, new_mtu); + return 0; +} + +/* Netdevice get statistics request */ + +static struct rtnl_link_stats64 * +sl_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + struct net_device_stats *devstats = &dev->stats; +#ifdef SL_INCLUDE_CSLIP + struct slip *sl = netdev_priv(dev); + struct slcompress *comp = sl->slcomp; +#endif + stats->rx_packets = devstats->rx_packets; + stats->tx_packets = devstats->tx_packets; + stats->rx_bytes = devstats->rx_bytes; + stats->tx_bytes = devstats->tx_bytes; + stats->rx_dropped = devstats->rx_dropped; + stats->tx_dropped = devstats->tx_dropped; + stats->tx_errors = devstats->tx_errors; + stats->rx_errors = devstats->rx_errors; + stats->rx_over_errors = devstats->rx_over_errors; + +#ifdef SL_INCLUDE_CSLIP + if (comp) { + /* Generic compressed statistics */ + stats->rx_compressed = comp->sls_i_compressed; + stats->tx_compressed = comp->sls_o_compressed; + + /* Are we really still needs this? */ + stats->rx_fifo_errors += comp->sls_i_compressed; + stats->rx_dropped += comp->sls_i_tossed; + stats->tx_fifo_errors += comp->sls_o_compressed; + stats->collisions += comp->sls_o_misses; + } +#endif + return stats; +} + +/* Netdevice register callback */ + +static int sl_init(struct net_device *dev) +{ + struct slip *sl = netdev_priv(dev); + + /* + * Finish setting up the DEVICE info. + */ + + dev->mtu = sl->mtu; + dev->type = ARPHRD_SLIP + sl->mode; +#ifdef SL_CHECK_TRANSMIT + dev->watchdog_timeo = 20*HZ; +#endif + return 0; +} + + +static void sl_uninit(struct net_device *dev) +{ + struct slip *sl = netdev_priv(dev); + + sl_free_bufs(sl); +} + +/* Hook the destructor so we can free slip devices at the right point in time */ +static void sl_free_netdev(struct net_device *dev) +{ + int i = dev->base_addr; + free_netdev(dev); + slip_devs[i] = NULL; +} + +static const struct net_device_ops sl_netdev_ops = { + .ndo_init = sl_init, + .ndo_uninit = sl_uninit, + .ndo_open = sl_open, + .ndo_stop = sl_close, + .ndo_start_xmit = sl_xmit, + .ndo_get_stats64 = sl_get_stats64, + .ndo_change_mtu = sl_change_mtu, + .ndo_tx_timeout = sl_tx_timeout, +#ifdef CONFIG_SLIP_SMART + .ndo_do_ioctl = sl_ioctl, +#endif +}; + + +static void sl_setup(struct net_device *dev) +{ + dev->netdev_ops = &sl_netdev_ops; + dev->destructor = sl_free_netdev; + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 10; + + /* New-style flags. */ + dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST; +} + +/****************************************** + Routines looking at TTY side. + ******************************************/ + + +/* + * Handle the 'receiver data ready' interrupt. + * This function is called by the 'tty_io' module in the kernel when + * a block of SLIP data has been received, which can now be decapsulated + * and sent on to some IP layer for further processing. This will not + * be re-entered while running but other ldisc functions may be called + * in parallel + */ + +static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct slip *sl = tty->disc_data; + + if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) + return; + + /* Read the characters out of the buffer */ + while (count--) { + if (fp && *fp++) { + if (!test_and_set_bit(SLF_ERROR, &sl->flags)) + sl->dev->stats.rx_errors++; + cp++; + continue; + } +#ifdef CONFIG_SLIP_MODE_SLIP6 + if (sl->mode & SL_MODE_SLIP6) + slip_unesc6(sl, *cp++); + else +#endif + slip_unesc(sl, *cp++); + } +} + +/************************************ + * slip_open helper routines. + ************************************/ + +/* Collect hanged up channels */ +static void sl_sync(void) +{ + int i; + struct net_device *dev; + struct slip *sl; + + for (i = 0; i < slip_maxdev; i++) { + dev = slip_devs[i]; + if (dev == NULL) + break; + + sl = netdev_priv(dev); + if (sl->tty || sl->leased) + continue; + if (dev->flags & IFF_UP) + dev_close(dev); + } +} + + +/* Find a free SLIP channel, and link in this `tty' line. */ +static struct slip *sl_alloc(dev_t line) +{ + int i; + char name[IFNAMSIZ]; + struct net_device *dev = NULL; + struct slip *sl; + + for (i = 0; i < slip_maxdev; i++) { + dev = slip_devs[i]; + if (dev == NULL) + break; + } + /* Sorry, too many, all slots in use */ + if (i >= slip_maxdev) + return NULL; + + sprintf(name, "sl%d", i); + dev = alloc_netdev(sizeof(*sl), name, sl_setup); + if (!dev) + return NULL; + + dev->base_addr = i; + sl = netdev_priv(dev); + + /* Initialize channel control data */ + sl->magic = SLIP_MAGIC; + sl->dev = dev; + spin_lock_init(&sl->lock); + sl->mode = SL_MODE_DEFAULT; +#ifdef CONFIG_SLIP_SMART + /* initialize timer_list struct */ + init_timer(&sl->keepalive_timer); + sl->keepalive_timer.data = (unsigned long)sl; + sl->keepalive_timer.function = sl_keepalive; + init_timer(&sl->outfill_timer); + sl->outfill_timer.data = (unsigned long)sl; + sl->outfill_timer.function = sl_outfill; +#endif + slip_devs[i] = dev; + return sl; +} + +/* + * Open the high-level part of the SLIP channel. + * This function is called by the TTY module when the + * SLIP line discipline is called for. Because we are + * sure the tty line exists, we only have to link it to + * a free SLIP channel... + * + * Called in process context serialized from other ldisc calls. + */ + +static int slip_open(struct tty_struct *tty) +{ + struct slip *sl; + int err; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + + /* RTnetlink lock is misused here to serialize concurrent + opens of slip channels. There are better ways, but it is + the simplest one. + */ + rtnl_lock(); + + /* Collect hanged up channels. */ + sl_sync(); + + sl = tty->disc_data; + + err = -EEXIST; + /* First make sure we're not already connected. */ + if (sl && sl->magic == SLIP_MAGIC) + goto err_exit; + + /* OK. Find a free SLIP channel to use. */ + err = -ENFILE; + sl = sl_alloc(tty_devnum(tty)); + if (sl == NULL) + goto err_exit; + + sl->tty = tty; + tty->disc_data = sl; + sl->pid = current->pid; + + if (!test_bit(SLF_INUSE, &sl->flags)) { + /* Perform the low-level SLIP initialization. */ + err = sl_alloc_bufs(sl, SL_MTU); + if (err) + goto err_free_chan; + + set_bit(SLF_INUSE, &sl->flags); + + err = register_netdevice(sl->dev); + if (err) + goto err_free_bufs; + } + +#ifdef CONFIG_SLIP_SMART + if (sl->keepalive) { + sl->keepalive_timer.expires = jiffies + sl->keepalive * HZ; + add_timer(&sl->keepalive_timer); + } + if (sl->outfill) { + sl->outfill_timer.expires = jiffies + sl->outfill * HZ; + add_timer(&sl->outfill_timer); + } +#endif + + /* Done. We have linked the TTY line to a channel. */ + rtnl_unlock(); + tty->receive_room = 65536; /* We don't flow control */ + + /* TTY layer expects 0 on success */ + return 0; + +err_free_bufs: + sl_free_bufs(sl); + +err_free_chan: + sl->tty = NULL; + tty->disc_data = NULL; + clear_bit(SLF_INUSE, &sl->flags); + +err_exit: + rtnl_unlock(); + + /* Count references from TTY module */ + return err; +} + +/* + * Close down a SLIP channel. + * This means flushing out any pending queues, and then returning. This + * call is serialized against other ldisc functions. + * + * We also use this method fo a hangup event + */ + +static void slip_close(struct tty_struct *tty) +{ + struct slip *sl = tty->disc_data; + + /* First make sure we're connected. */ + if (!sl || sl->magic != SLIP_MAGIC || sl->tty != tty) + return; + + tty->disc_data = NULL; + sl->tty = NULL; + + /* VSV = very important to remove timers */ +#ifdef CONFIG_SLIP_SMART + del_timer_sync(&sl->keepalive_timer); + del_timer_sync(&sl->outfill_timer); +#endif + /* Flush network side */ + unregister_netdev(sl->dev); + /* This will complete via sl_free_netdev */ +} + +static int slip_hangup(struct tty_struct *tty) +{ + slip_close(tty); + return 0; +} + /************************************************************************ + * STANDARD SLIP ENCAPSULATION * + ************************************************************************/ + +static int slip_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = END; + + /* + * For each byte in the packet, send the appropriate + * character sequence, according to the SLIP protocol. + */ + + while (len-- > 0) { + switch (c = *s++) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return ptr - d; +} + +static void slip_unesc(struct slip *sl, unsigned char s) +{ + + switch (s) { + case END: +#ifdef CONFIG_SLIP_SMART + /* drop keeptest bit = VSV */ + if (test_bit(SLF_KEEPTEST, &sl->flags)) + clear_bit(SLF_KEEPTEST, &sl->flags); +#endif + + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && + (sl->rcount > 2)) + sl_bump(sl); + clear_bit(SLF_ESCAPE, &sl->flags); + sl->rcount = 0; + return; + + case ESC: + set_bit(SLF_ESCAPE, &sl->flags); + return; + case ESC_ESC: + if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) + s = ESC; + break; + case ESC_END: + if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) + s = END; + break; + } + if (!test_bit(SLF_ERROR, &sl->flags)) { + if (sl->rcount < sl->buffsize) { + sl->rbuff[sl->rcount++] = s; + return; + } + sl->dev->stats.rx_over_errors++; + set_bit(SLF_ERROR, &sl->flags); + } +} + + +#ifdef CONFIG_SLIP_MODE_SLIP6 +/************************************************************************ + * 6 BIT SLIP ENCAPSULATION * + ************************************************************************/ + +static int slip_esc6(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + int i; + unsigned short v = 0; + short bits = 0; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = 0x70; + + /* + * Encode the packet into printable ascii characters + */ + + for (i = 0; i < len; ++i) { + v = (v << 8) | s[i]; + bits += 8; + while (bits >= 6) { + bits -= 6; + c = 0x30 + ((v >> bits) & 0x3F); + *ptr++ = c; + } + } + if (bits) { + c = 0x30 + ((v << (6 - bits)) & 0x3F); + *ptr++ = c; + } + *ptr++ = 0x70; + return ptr - d; +} + +static void slip_unesc6(struct slip *sl, unsigned char s) +{ + unsigned char c; + + if (s == 0x70) { +#ifdef CONFIG_SLIP_SMART + /* drop keeptest bit = VSV */ + if (test_bit(SLF_KEEPTEST, &sl->flags)) + clear_bit(SLF_KEEPTEST, &sl->flags); +#endif + + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && + (sl->rcount > 2)) + sl_bump(sl); + sl->rcount = 0; + sl->xbits = 0; + sl->xdata = 0; + } else if (s >= 0x30 && s < 0x70) { + sl->xdata = (sl->xdata << 6) | ((s - 0x30) & 0x3F); + sl->xbits += 6; + if (sl->xbits >= 8) { + sl->xbits -= 8; + c = (unsigned char)(sl->xdata >> sl->xbits); + if (!test_bit(SLF_ERROR, &sl->flags)) { + if (sl->rcount < sl->buffsize) { + sl->rbuff[sl->rcount++] = c; + return; + } + sl->dev->stats.rx_over_errors++; + set_bit(SLF_ERROR, &sl->flags); + } + } + } +} +#endif /* CONFIG_SLIP_MODE_SLIP6 */ + +/* Perform I/O control on an active SLIP channel. */ +static int slip_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct slip *sl = tty->disc_data; + unsigned int tmp; + int __user *p = (int __user *)arg; + + /* First make sure we're connected. */ + if (!sl || sl->magic != SLIP_MAGIC) + return -EINVAL; + + switch (cmd) { + case SIOCGIFNAME: + tmp = strlen(sl->dev->name) + 1; + if (copy_to_user((void __user *)arg, sl->dev->name, tmp)) + return -EFAULT; + return 0; + + case SIOCGIFENCAP: + if (put_user(sl->mode, p)) + return -EFAULT; + return 0; + + case SIOCSIFENCAP: + if (get_user(tmp, p)) + return -EFAULT; +#ifndef SL_INCLUDE_CSLIP + if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE)) + return -EINVAL; +#else + if ((tmp & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) == + (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) + /* return -EINVAL; */ + tmp &= ~SL_MODE_ADAPTIVE; +#endif +#ifndef CONFIG_SLIP_MODE_SLIP6 + if (tmp & SL_MODE_SLIP6) + return -EINVAL; +#endif + sl->mode = tmp; + sl->dev->type = ARPHRD_SLIP + sl->mode; + return 0; + + case SIOCSIFHWADDR: + return -EINVAL; + +#ifdef CONFIG_SLIP_SMART + /* VSV changes start here */ + case SIOCSKEEPALIVE: + if (get_user(tmp, p)) + return -EFAULT; + if (tmp > 255) /* max for unchar */ + return -EINVAL; + + spin_lock_bh(&sl->lock); + if (!sl->tty) { + spin_unlock_bh(&sl->lock); + return -ENODEV; + } + sl->keepalive = (u8)tmp; + if (sl->keepalive != 0) { + mod_timer(&sl->keepalive_timer, + jiffies + sl->keepalive * HZ); + set_bit(SLF_KEEPTEST, &sl->flags); + } else + del_timer(&sl->keepalive_timer); + spin_unlock_bh(&sl->lock); + return 0; + + case SIOCGKEEPALIVE: + if (put_user(sl->keepalive, p)) + return -EFAULT; + return 0; + + case SIOCSOUTFILL: + if (get_user(tmp, p)) + return -EFAULT; + if (tmp > 255) /* max for unchar */ + return -EINVAL; + spin_lock_bh(&sl->lock); + if (!sl->tty) { + spin_unlock_bh(&sl->lock); + return -ENODEV; + } + sl->outfill = (u8)tmp; + if (sl->outfill != 0) { + mod_timer(&sl->outfill_timer, + jiffies + sl->outfill * HZ); + set_bit(SLF_OUTWAIT, &sl->flags); + } else + del_timer(&sl->outfill_timer); + spin_unlock_bh(&sl->lock); + return 0; + + case SIOCGOUTFILL: + if (put_user(sl->outfill, p)) + return -EFAULT; + return 0; + /* VSV changes end */ +#endif + default: + return tty_mode_ioctl(tty, file, cmd, arg); + } +} + +#ifdef CONFIG_COMPAT +static long slip_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCGIFNAME: + case SIOCGIFENCAP: + case SIOCSIFENCAP: + case SIOCSIFHWADDR: + case SIOCSKEEPALIVE: + case SIOCGKEEPALIVE: + case SIOCSOUTFILL: + case SIOCGOUTFILL: + return slip_ioctl(tty, file, cmd, + (unsigned long)compat_ptr(arg)); + } + + return -ENOIOCTLCMD; +} +#endif + +/* VSV changes start here */ +#ifdef CONFIG_SLIP_SMART +/* function do_ioctl called from net/core/dev.c + to allow get/set outfill/keepalive parameter + by ifconfig */ + +static int sl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct slip *sl = netdev_priv(dev); + unsigned long *p = (unsigned long *)&rq->ifr_ifru; + + if (sl == NULL) /* Allocation failed ?? */ + return -ENODEV; + + spin_lock_bh(&sl->lock); + + if (!sl->tty) { + spin_unlock_bh(&sl->lock); + return -ENODEV; + } + + switch (cmd) { + case SIOCSKEEPALIVE: + /* max for unchar */ + if ((unsigned)*p > 255) { + spin_unlock_bh(&sl->lock); + return -EINVAL; + } + sl->keepalive = (u8)*p; + if (sl->keepalive != 0) { + sl->keepalive_timer.expires = + jiffies + sl->keepalive * HZ; + mod_timer(&sl->keepalive_timer, + jiffies + sl->keepalive * HZ); + set_bit(SLF_KEEPTEST, &sl->flags); + } else + del_timer(&sl->keepalive_timer); + break; + + case SIOCGKEEPALIVE: + *p = sl->keepalive; + break; + + case SIOCSOUTFILL: + if ((unsigned)*p > 255) { /* max for unchar */ + spin_unlock_bh(&sl->lock); + return -EINVAL; + } + sl->outfill = (u8)*p; + if (sl->outfill != 0) { + mod_timer(&sl->outfill_timer, + jiffies + sl->outfill * HZ); + set_bit(SLF_OUTWAIT, &sl->flags); + } else + del_timer(&sl->outfill_timer); + break; + + case SIOCGOUTFILL: + *p = sl->outfill; + break; + + case SIOCSLEASE: + /* Resolve race condition, when ioctl'ing hanged up + and opened by another process device. + */ + if (sl->tty != current->signal->tty && + sl->pid != current->pid) { + spin_unlock_bh(&sl->lock); + return -EPERM; + } + sl->leased = 0; + if (*p) + sl->leased = 1; + break; + + case SIOCGLEASE: + *p = sl->leased; + } + spin_unlock_bh(&sl->lock); + return 0; +} +#endif +/* VSV changes end */ + +static struct tty_ldisc_ops sl_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "slip", + .open = slip_open, + .close = slip_close, + .hangup = slip_hangup, + .ioctl = slip_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = slip_compat_ioctl, +#endif + .receive_buf = slip_receive_buf, + .write_wakeup = slip_write_wakeup, +}; + +static int __init slip_init(void) +{ + int status; + + if (slip_maxdev < 4) + slip_maxdev = 4; /* Sanity */ + + printk(KERN_INFO "SLIP: version %s (dynamic channels, max=%d)" +#ifdef CONFIG_SLIP_MODE_SLIP6 + " (6 bit encapsulation enabled)" +#endif + ".\n", + SLIP_VERSION, slip_maxdev); +#if defined(SL_INCLUDE_CSLIP) + printk(KERN_INFO "CSLIP: code copyright 1989 Regents of the University of California.\n"); +#endif +#ifdef CONFIG_SLIP_SMART + printk(KERN_INFO "SLIP linefill/keepalive option.\n"); +#endif + + slip_devs = kzalloc(sizeof(struct net_device *)*slip_maxdev, + GFP_KERNEL); + if (!slip_devs) { + printk(KERN_ERR "SLIP: Can't allocate slip devices array.\n"); + return -ENOMEM; + } + + /* Fill in our line protocol discipline, and register it */ + status = tty_register_ldisc(N_SLIP, &sl_ldisc); + if (status != 0) { + printk(KERN_ERR "SLIP: can't register line discipline (err = %d)\n", status); + kfree(slip_devs); + } + return status; +} + +static void __exit slip_exit(void) +{ + int i; + struct net_device *dev; + struct slip *sl; + unsigned long timeout = jiffies + HZ; + int busy = 0; + + if (slip_devs == NULL) + return; + + /* First of all: check for active disciplines and hangup them. + */ + do { + if (busy) + msleep_interruptible(100); + + busy = 0; + for (i = 0; i < slip_maxdev; i++) { + dev = slip_devs[i]; + if (!dev) + continue; + sl = netdev_priv(dev); + spin_lock_bh(&sl->lock); + if (sl->tty) { + busy++; + tty_hangup(sl->tty); + } + spin_unlock_bh(&sl->lock); + } + } while (busy && time_before(jiffies, timeout)); + + /* FIXME: hangup is async so we should wait when doing this second + phase */ + + for (i = 0; i < slip_maxdev; i++) { + dev = slip_devs[i]; + if (!dev) + continue; + slip_devs[i] = NULL; + + sl = netdev_priv(dev); + if (sl->tty) { + printk(KERN_ERR "%s: tty discipline still running\n", + dev->name); + /* Intentionally leak the control block. */ + dev->destructor = NULL; + } + + unregister_netdev(dev); + } + + kfree(slip_devs); + slip_devs = NULL; + + i = tty_unregister_ldisc(N_SLIP); + if (i != 0) + printk(KERN_ERR "SLIP: can't unregister line discipline (err = %d)\n", i); +} + +module_init(slip_init); +module_exit(slip_exit); + +#ifdef CONFIG_SLIP_SMART +/* + * This is start of the code for multislip style line checking + * added by Stanislav Voronyi. All changes before marked VSV + */ + +static void sl_outfill(unsigned long sls) +{ + struct slip *sl = (struct slip *)sls; + + spin_lock(&sl->lock); + + if (sl->tty == NULL) + goto out; + + if (sl->outfill) { + if (test_bit(SLF_OUTWAIT, &sl->flags)) { + /* no packets were transmitted, do outfill */ +#ifdef CONFIG_SLIP_MODE_SLIP6 + unsigned char s = (sl->mode & SL_MODE_SLIP6)?0x70:END; +#else + unsigned char s = END; +#endif + /* put END into tty queue. Is it right ??? */ + if (!netif_queue_stopped(sl->dev)) { + /* if device busy no outfill */ + sl->tty->ops->write(sl->tty, &s, 1); + } + } else + set_bit(SLF_OUTWAIT, &sl->flags); + + mod_timer(&sl->outfill_timer, jiffies+sl->outfill*HZ); + } +out: + spin_unlock(&sl->lock); +} + +static void sl_keepalive(unsigned long sls) +{ + struct slip *sl = (struct slip *)sls; + + spin_lock(&sl->lock); + + if (sl->tty == NULL) + goto out; + + if (sl->keepalive) { + if (test_bit(SLF_KEEPTEST, &sl->flags)) { + /* keepalive still high :(, we must hangup */ + if (sl->outfill) + /* outfill timer must be deleted too */ + (void)del_timer(&sl->outfill_timer); + printk(KERN_DEBUG "%s: no packets received during keepalive timeout, hangup.\n", sl->dev->name); + /* this must hangup tty & close slip */ + tty_hangup(sl->tty); + /* I think we need not something else */ + goto out; + } else + set_bit(SLF_KEEPTEST, &sl->flags); + + mod_timer(&sl->keepalive_timer, jiffies+sl->keepalive*HZ); + } +out: + spin_unlock(&sl->lock); +} + +#endif +MODULE_LICENSE("GPL"); +MODULE_ALIAS_LDISC(N_SLIP); diff --git a/drivers/net/slip/slip.h b/drivers/net/slip/slip.h new file mode 100644 index 0000000..67673cf --- /dev/null +++ b/drivers/net/slip/slip.h @@ -0,0 +1,101 @@ +/* + * slip.h Define the SLIP device driver interface and constants. + * + * NOTE: THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY + * AS SOON AS POSSIBLE! + * + * Version: @(#)slip.h 1.2.0 03/28/93 + * + * Fixes: + * Alan Cox : Added slip mtu field. + * Matt Dillon : Printable slip (borrowed from net2e) + * Alan Cox : Added SL_SLIP_LOTS + * Dmitry Gorodchanin : A lot of changes in the 'struct slip' + * Dmitry Gorodchanin : Added CSLIP statistics. + * Stanislav Voronyi : Make line checking as created by + * Igor Chechik, RELCOM Corp. + * Craig Schlenter : Fixed #define bug that caused + * CSLIP telnets to hang in 1.3.61-6 + * + * Author: Fred N. van Kempen, + */ +#ifndef _LINUX_SLIP_H +#define _LINUX_SLIP_H + + +#if defined(CONFIG_INET) && defined(CONFIG_SLIP_COMPRESSED) +# define SL_INCLUDE_CSLIP +#endif + +#ifdef SL_INCLUDE_CSLIP +# define SL_MODE_DEFAULT SL_MODE_ADAPTIVE +#else +# define SL_MODE_DEFAULT SL_MODE_SLIP +#endif + +/* SLIP configuration. */ +#define SL_NRUNIT 256 /* MAX number of SLIP channels; + This can be overridden with + insmod -oslip_maxdev=nnn */ +#define SL_MTU 296 /* 296; I am used to 600- FvK */ + +/* SLIP protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + + +struct slip { + int magic; + + /* Various fields. */ + struct tty_struct *tty; /* ptr to TTY structure */ + struct net_device *dev; /* easy for intr handling */ + spinlock_t lock; + +#ifdef SL_INCLUDE_CSLIP + struct slcompress *slcomp; /* for header compression */ + unsigned char *cbuff; /* compression buffer */ +#endif + + /* These are pointers to the malloc()ed frame buffers. */ + unsigned char *rbuff; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char *xbuff; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next byte to XMIT */ + int xleft; /* bytes left in XMIT queue */ + int mtu; /* Our mtu (to spot changes!) */ + int buffsize; /* Max buffers sizes */ + +#ifdef CONFIG_SLIP_MODE_SLIP6 + int xdata, xbits; /* 6 bit slip controls */ +#endif + + unsigned long flags; /* Flag values/ mode etc */ +#define SLF_INUSE 0 /* Channel in use */ +#define SLF_ESCAPE 1 /* ESC received */ +#define SLF_ERROR 2 /* Parity, etc. error */ +#define SLF_KEEPTEST 3 /* Keepalive test flag */ +#define SLF_OUTWAIT 4 /* is outpacket was flag */ + + unsigned char mode; /* SLIP mode */ + unsigned char leased; + pid_t pid; +#define SL_MODE_SLIP 0 +#define SL_MODE_CSLIP 1 +#define SL_MODE_SLIP6 2 /* Matt Dillon's printable slip */ +#define SL_MODE_CSLIP6 (SL_MODE_SLIP6|SL_MODE_CSLIP) +#define SL_MODE_AX25 4 +#define SL_MODE_ADAPTIVE 8 +#ifdef CONFIG_SLIP_SMART + unsigned char outfill; /* # of sec between outfill packet */ + unsigned char keepalive; /* keepalive seconds */ + struct timer_list outfill_timer; + struct timer_list keepalive_timer; +#endif +}; + +#define SLIP_MAGIC 0x5302 + +#endif /* _LINUX_SLIP.H */ -- cgit v0.10.2