*** cslip.c Fri Nov 6 03:54:11 1992 --- cslip.c.new Fri Nov 6 17:07:20 1992 *************** *** 10,15 **** --- 10,19 ---- * time and always left open. To do slattach, a module is pushed * on a tty device and an ioctl done on it to find out which slip * interface should be started up using ifconfig(1). + * + * 92-11-6: + * V1.1 Markus Wild integrated TCP header compression and added code + * to favor interactive traffic over regular traffic */ #include "sys/types.h" *************** *** 34,46 **** --- 38,72 ---- #include "netinet/in.h" #include "netinet/in_var.h" #include "netinet/ip_str.h" + #include "netinet/in_systm.h" + #include "netinet/ip.h" + #include "netinet/tcp.h" extern struct ifstats *ifstats; /* per-interface statistics for inet */ + #include "slcompress.h" #include "slip.h" + /* the code to deal with interactive traffic is from the cslip reference + code, sunos/ directory (for streams) */ + + /* + * The following hack gets around the problem that IP TOS + * can't be set in BSD/Sun OS yet. We want to put "interactive" + * traffic on a high priority queue. To decide if traffic is + * interactive, we check that a) it is TCP and b) one of it's ports + * if telnet, rlogin or ftp control. + */ + static u_short interactive_ports[8] = { + 0, 513, 0, 0, + 0, 21, 0, 23, + }; + #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) + #define SLIP_WHICH_INTERFACE 'slwh' + #define SLIP_GET_CONF_FLAGS 'slgc' + #define SLIP_SET_CONF_FLAGS 'slsc' #define SLIP_DLPI_MID 'SD' /* DLPI Module ID */ #define SLIP_TTY_MID 'ST' /* TTY Module ID */ *************** *** 47,56 **** --- 73,145 ---- #define SLIP_MAXDEV 5 /* Maximum number of simultaneous */ /* active SLIP interfaces. */ + + /* explanatory text to the BSD code. I used the suggested MTU, but am + * for now using a much higher hi/lo-water pair (seems streams-specific) */ + + /* + * SLMAX is a hard limit on input packet size. To simplify the code + * and improve performance, we require that packets fit in an mbuf + * cluster, and if we get a compressed packet, there's enough extra + * room to expand the header into a max length tcp/ip header (128 + * bytes). So, SLMAX can be at most + * MCLBYTES - 128 + * + * SLMTU is a hard limit on output packet size. To insure good + * interactive response, SLMTU wants to be the smallest size that + * amortizes the header cost. (Remember that even with + * type-of-service queuing, we have to wait for any in-progress + * packet to finish. I.e., we wait, on the average, 1/2 * mtu / + * cps, where cps is the line speed in characters per second. + * E.g., 533ms wait for a 1024 byte MTU on a 9600 baud line. The + * average compressed header size is 6-8 bytes so any MTU > 90 + * bytes will give us 90% of the line bandwidth. A 100ms wait is + * tolerable (500ms is not), so want an MTU around 296. (Since TCP + * will send 256 byte segments (to allow for 40 byte headers), the + * typical packet size on the wire will be around 260 bytes). In + * 4.3tahoe+ systems, we can set an MTU in a route so we do that & + * leave the interface MTU relatively high (so we don't IP fragment + * when acting as a gateway to someone using a stupid MTU). + * + * Similar considerations apply to SLIP_HIWAT: It's the amount of + * data that will be queued 'downstream' of us (i.e., in clists + * waiting to be picked up by the tty output interrupt). If we + * queue a lot of data downstream, it's immune to our t.o.s. queuing. + * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed + * telnet/ftp will see a 1 sec wait, independent of the mtu (the + * wait is dependent on the ftp window size but that's typically + * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize + * the cost (in idle time on the wire) of the tty driver running + * off the end of its clists & having to call back slstart for a + * new packet. For a tty interface with any buffering at all, this + * cost will be zero. Even with a totally brain dead interface (like + * the one on a typical workstation), the cost will be <= 1 character + * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose + * at most 1% while maintaining good interactive response. + */ + #if 0 /* BSD implementation constants described in above text */ + #define BUFOFFSET 128 + #define SLMAX (MCLBYTES - BUFOFFSET) + #define SLBUFSIZE (SLMAX + BUFOFFSET) + #define SLMTU 296 + #define SLIP_HIWAT roundup(50,CBSIZE) + #define CLISTRESERVE 1024 /* Can't let clists get too low */ + #else + #ifdef COMMODORE_PARAMETERS #define SLIP_MIN 1 #define SLIP_MAX 1006 /* Is this some sort of standard? */ + #define SLIP_MTU SLIP_MAX #define SLIP_HIWAT 2048 #define SLIP_LOWAT (2*SLIP_HIWAT/3) + #else + #define SLIP_MIN 1 + #define SLIP_MAX 1006 /* Is this some sort of standard? */ + #define SLIP_MTU 296 + #define SLIP_HIWAT 100 + #define SLIP_LOWAT (2*SLIP_HIWAT/3) + #endif + #define BUFOFFSET 128 /* room for decompressed header */ + #endif struct slipdev { *************** *** 62,69 **** --- 151,167 ---- char ifname[20]; mblk_t *curblk; /* Input packet being assembled */ int escaped; /* Last character was ESCAPE */ + + u_int sc_flags; /* see below */ + struct slcompress sc_comp; /* tcp compression data */ + mblk_t *lastpriopkt; /* last priority interactive message */ }; + /* visible flags */ + #define SC_COMPRESS 0x0002 /* compress TCP traffic */ + #define SC_NOICMP 0x0004 /* supress ICMP traffic */ + #define SC_AUTOCOMP 0x0008 /* auto-enable TCP compression */ + /* * DLPI stream data structure definitions *************** *** 75,81 **** --- 173,184 ---- static struct module_info dlpi_minfo = { + #ifdef COMMODORE_PARAMETERS SLIP_DLPI_MID, "sld", SLIP_MIN, SLIP_MAX, SLIP_HIWAT, SLIP_LOWAT, + #else + /* these parameters are from the SunOS streams code, who is right?? */ + SLIP_DLPI_MID, "sld", 0, INFPSZ, 16384, 4096, + #endif }; static struct qinit dlpirinit = *************** *** 101,107 **** --- 204,214 ---- static struct module_info tty_minfo = { + #ifdef COMMODORE_PARAMETERS SLIP_TTY_MID, "slipmod", SLIP_MIN, SLIP_MAX, SLIP_HIWAT, SLIP_LOWAT, + #else + SLIP_TTY_MID, "slipmod", 0, INFPSZ, 16384, 4096, + #endif }; static struct qinit ttyrinit = *************** *** 164,170 **** } slip->stats.ifs_active = 1; ! slip->stats.ifs_mtu = SLIP_MAX; slip->stats.ifs_ipackets = slip->stats.ifs_ierrors = slip->stats.ifs_opackets = --- 271,277 ---- } slip->stats.ifs_active = 1; ! slip->stats.ifs_mtu = SLIP_MTU; slip->stats.ifs_ipackets = slip->stats.ifs_ierrors = slip->stats.ifs_opackets = *************** *** 172,177 **** --- 279,289 ---- WR(rq)->q_ptr = rq->q_ptr = (caddr_t)slip; slip->dlpiq = rq; + + /* seems like a reasonable default to me */ + slip->sc_flags = SC_AUTOCOMP; + sl_compress_init (& slip->sc_comp); + slip->lastpriopkt = 0; } return 0; *************** *** 448,454 **** register struct slipdev *slip; mblk_t *dlpimp; { ! mblk_t *bp, *outmp; if (!canput(WR(slip->ttyq))) return 0; --- 560,568 ---- register struct slipdev *slip; mblk_t *dlpimp; { ! mblk_t *bp, *outmp, *tmpmp=0; ! struct ip *ip; ! int s, p, hiprio; if (!canput(WR(slip->ttyq))) return 0; *************** *** 461,469 **** } outmp->b_datap->db_type = M_DATA; #define putc(ch) (*outmp->b_wptr++ = (ch)) /* Copy input to output, escaping special characters */ ! for ( bp=dlpimp->b_cont ; bp ; bp=bp->b_cont ) while (bp->b_wptr > bp->b_rptr) { unsigned char c = *bp->b_rptr++; --- 575,624 ---- } outmp->b_datap->db_type = M_DATA; + ip = (struct ip *)(dlpimp->b_cont->b_rptr); + hiprio = 0; + if (ip->ip_p == IPPROTO_TCP) + { + if (slip->sc_flags & SC_COMPRESS) + { + /* copy over the chain of mblk_t into one buffer. This is to + make sure the compression code can assume the whole mess + is one packet. It probably would do to just convert the + first buffer, but since I don't have any documentation to + mblk_t internals.. */ + if (!(tmpmp = allocb (SLIP_MAX, BPRI_MED))) + { + ++slip->stats.ifs_oerrors; + freemsg (outmp); + return 0; + } + for (bp = dlpimp->b_cont; bp; bp = bp->b_cont) + while (bp->b_wptr > bp->b_rptr) + *tmpmp->b_wptr++ = *bp->b_rptr++; + + /* ok, compress the sucker */ + ip = (struct ip *) tmpmp->b_rptr; + p = ((int *)ip)[ip->ip_hl]; + hiprio = INTERACTIVE(p & 0xffff) || INTERACTIVE(p >> 16); + p = sl_compress_tcp (tmpmp, ip, &slip->sc_comp, hiprio); + *(u_char *) tmpmp->b_rptr |= p; /* what the ... */ + bp = tmpmp; + } + else if ((slip->sc_flags & SC_NOICMP) && ip->ip_p == IPPROTO_ICMP) + { + freemsg (tmpmp); + freemsg (outmp); + return 0; + } + else + bp = dlpimp->b_cont; + } + else + bp = dlpimp->b_cont; + #define putc(ch) (*outmp->b_wptr++ = (ch)) /* Copy input to output, escaping special characters */ ! for ( ; bp ; bp=bp->b_cont ) while (bp->b_wptr > bp->b_rptr) { unsigned char c = *bp->b_rptr++; *************** *** 489,497 **** #undef putc ++slip->stats.ifs_opackets; putnext(WR(slip->ttyq), outmp); ! freemsg(dlpimp); return 1; } --- 644,694 ---- #undef putc ++slip->stats.ifs_opackets; + #if 1 + /* then queue it for later transmission through the port */ + s = splstr(); + if (WR(slip->ttyq)) + { /* inside splstr() in case WR(slip->ttyq) = 0 */ + if (hiprio) + { /* macchiavelli mode */ + register mblk_t *tp = WR(slip->ttyq)->q_next->q_first; + /* + * We want this packet to come before other + * IP packets but after other TELNET/LOGINSERVER + * packets, ignoring real priority messages. + */ + for (; tp != NULL; tp = tp->b_next) + if (tp == slip->lastpriopkt) + break; + if (tp == NULL) + tp = WR(slip->ttyq)->q_next->q_first; + if (tp == NULL) + /* since insq doesn't schedule the q! */ + putnext(WR(slip->ttyq), outmp); + else + insq(WR(slip->ttyq)->q_next, tp, outmp); + slip->lastpriopkt = outmp; + } + else + putnext(WR(slip->ttyq), outmp); + } + else + { + freemsg(outmp); + outmp = 0; + } + (void) splx(s); + #else putnext(WR(slip->ttyq), outmp); ! #endif ! if (tmpmp) ! freemsg (tmpmp); + if (outmp) + freemsg(dlpimp); + else + return 0; + return 1; } *************** *** 634,639 **** --- 831,837 ---- { register struct slipdev *slip = (struct slipdev *)q->q_ptr; struct iocblk *iocbp = (struct iocblk *)mp->b_rptr; + int s; switch (iocbp->ioc_cmd) { *************** *** 640,645 **** --- 838,868 ---- case SLIP_WHICH_INTERFACE: RETURN(M_IOCACK, slip-sliptab, 0); + case SLIP_GET_CONF_FLAGS: + RETURN(M_IOCACK, slip->sc_flags, 0); + + case SLIP_SET_CONF_FLAGS: + if (iocbp->ioc_count == TRANSPARENT) + { + slip->sc_flags = (*(long *)mp->b_cont->b_rptr) & 0xffff; + /* printf("sl: setting flags 0x%x\n", slip->sc_flags);*/ + freemsg (mp->b_cont); + mp->b_cont = 0; + RETURN(M_IOCACK, 0, 0); + } + else + { + /* fail if I_STR (from streams guide) */ + if (mp->b_cont) + { + freemsg (mp->b_cont); + mp->b_cont = 0; + } + mp->b_datap->db_type = M_IOCNAK; + qreply (q, mp); + break; + } + default: RETURN(M_IOCNAK, 0, EINVAL); } *************** *** 681,702 **** { mblk_t *dlpimp; register dl_unitdata_ind_t *udp; dlpimp = allocb(sizeof *udp, BPRI_MED); if (dlpimp) ! { ! udp = (dl_unitdata_ind_t *)dlpimp->b_wptr; ! udp->dl_primitive = DL_UNITDATA_IND; ! udp->dl_dest_addr_length = 0; ! udp->dl_dest_addr_offset = sizeof *udp; ! udp->dl_src_addr_length = 0; ! udp->dl_src_addr_offset = sizeof *udp; ! udp->dl_reserved = 0; ! ! dlpimp->b_wptr += sizeof *udp; ! dlpimp->b_datap->db_type = M_PROTO; ! dlpimp->b_cont = outmp; ! ++slip->stats.ifs_ipackets; ! putnext(slip->dlpiq, dlpimp); } else freemsg(outmp); --- 904,965 ---- { mblk_t *dlpimp; register dl_unitdata_ind_t *udp; + int c; + dlpimp = allocb(sizeof *udp, BPRI_MED); if (dlpimp) ! { ! int ok = 1; ! ! if ((c = (*outmp->b_rptr & 0xf0)) != ! (IPVERSION << 4)) ! { ! if (c & 0x80) ! c = TYPE_COMPRESSED_TCP; ! else if (c == TYPE_UNCOMPRESSED_TCP) ! *outmp->b_rptr &= 0x4f; /* XXX */ ! /* ! * We've got something that's not an IP packet. ! * If compression is enabled, try to decompress it. ! * Otherwise, if `auto-enable' compression is on and ! * it's a reasonable packet, decompress it and then ! * enable compression. Otherwise, drop it. ! */ ! if (slip->sc_flags & SC_COMPRESS) ! ok = sl_uncompress_tcp (outmp, (u_int)c, ! &slip->sc_comp); ! else if ((slip->sc_flags & SC_AUTOCOMP) && ! c == TYPE_UNCOMPRESSED_TCP && ! outmp->b_wptr - outmp->b_rptr >= 40) ! { ! ok = sl_uncompress_tcp (outmp, (u_int)c, ! &slip->sc_comp); ! if (ok > 0) ! slip->sc_flags |= SC_COMPRESS; ! } ! } ! ! if (ok <= 0) ! { ! freemsg (outmp); ! freemsg (dlpimp); ! } ! else ! { ! udp = (dl_unitdata_ind_t *)dlpimp->b_wptr; ! udp->dl_primitive = DL_UNITDATA_IND; ! udp->dl_dest_addr_length = 0; ! udp->dl_dest_addr_offset = sizeof *udp; ! udp->dl_src_addr_length = 0; ! udp->dl_src_addr_offset = sizeof *udp; ! udp->dl_reserved = 0; ! ! dlpimp->b_wptr += sizeof *udp; ! dlpimp->b_datap->db_type = M_PROTO; ! dlpimp->b_cont = outmp; ! ++slip->stats.ifs_ipackets; ! putnext(slip->dlpiq, dlpimp); ! } } else freemsg(outmp); *************** *** 727,734 **** /* if packet is too large, drop it and start over */ if (!outmp) ! if (outmp=allocb(SLIP_MAX, BPRI_MED)) slip->curblk = outmp; else { ++slip->stats.ifs_ierrors; --- 990,1003 ---- /* if packet is too large, drop it and start over */ if (!outmp) ! if (outmp=allocb(SLIP_MAX+BUFOFFSET, BPRI_MED)) ! { slip->curblk = outmp; + /* make a hole to possibly insert a decompressed + header */ + outmp->b_wptr = outmp->b_rptr = + outmp->b_datap->db_base + BUFOFFSET; + } else { ++slip->stats.ifs_ierrors; *************** *** 739,745 **** { ++slip->stats.ifs_ierrors; outmp->b_wptr = outmp->b_rptr = ! outmp->b_datap->db_base; } *outmp->b_wptr++ = c; --- 1008,1014 ---- { ++slip->stats.ifs_ierrors; outmp->b_wptr = outmp->b_rptr = ! outmp->b_datap->db_base + BUFOFFSET; } *outmp->b_wptr++ = c;