951 lines
29 KiB
C
951 lines
29 KiB
C
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vjslip.c
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Thomas J. Dimitri (TommyD)
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "wan.h"
|
|
|
|
#define __FILE_SIG__ VJ_FILESIG
|
|
|
|
#if 0
|
|
NPAGED_LOOKASIDE_LIST VJCtxList; // List of free vj context descs
|
|
#endif
|
|
|
|
#define INCR(counter) ++comp->counter;
|
|
|
|
// A.2 Compression
|
|
//
|
|
// This routine looks daunting but isn't really. The code splits into four
|
|
// approximately equal sized sections: The first quarter manages a
|
|
// circularly linked, least-recently-used list of `active' TCP
|
|
// connections./47/ The second figures out the sequence/ack/window/urg
|
|
// changes and builds the bulk of the compressed packet. The third handles
|
|
// the special-case encodings. The last quarter does packet ID and
|
|
// connection ID encoding and replaces the original packet header with the
|
|
// compressed header.
|
|
//
|
|
// The arguments to this routine are a pointer to a packet to be
|
|
// compressed, a pointer to the compression state data for the serial line,
|
|
// and a flag which enables or disables connection id (C bit) compression.
|
|
//
|
|
// Compression is done `in-place' so, if a compressed packet is created,
|
|
// both the start address and length of the incoming packet (the off and
|
|
// len fields of m) will be updated to reflect the removal of the original
|
|
// header and its replacement by the compressed header. If either a
|
|
// compressed or uncompressed packet is created, the compression state is
|
|
// updated. This routines returns the packet type for the transmit framer
|
|
// (TYPE_IP, TYPE_UNCOMPRESSED_TCP or TYPE_COMPRESSED_TCP).
|
|
//
|
|
// Because 16 and 32 bit arithmetic is done on various header fields, the
|
|
// incoming IP packet must be aligned appropriately (e.g., on a SPARC, the
|
|
// IP header is aligned on a 32-bit boundary). Substantial changes would
|
|
// have to be made to the code below if this were not true (and it would
|
|
// probably be cheaper to byte copy the incoming header to somewhere
|
|
// correctly aligned than to make those changes).
|
|
//
|
|
// Note that the outgoing packet will be aligned arbitrarily (e.g., it
|
|
// could easily start on an odd-byte boundary).
|
|
//
|
|
|
|
UCHAR
|
|
sl_compress_tcp(
|
|
PUUCHAR UNALIGNED *m_off, // Frame start (points to IP header)
|
|
ULONG *m_len, // Length of entire frame
|
|
ULONG *precomph_len, // Length of TCP/IP header pre-comp
|
|
ULONG *postcomph_len, // Length of TCP/IP header post-comp
|
|
struct slcompress *comp, // Compression struct for this link
|
|
ULONG compress_cid) { // Compress connection id boolean
|
|
|
|
struct cstate *cs = comp->last_cs->cs_next;
|
|
struct ip_v4 UNALIGNED *ip = (struct ip_v4 UNALIGNED *)*m_off;
|
|
struct ip_v4 UNALIGNED *csip;
|
|
ULONG hlen = ip->ip_hl & 0x0F; // last 4 bits are the length
|
|
struct tcphdr UNALIGNED *oth; /* last TCP header */
|
|
struct tcphdr UNALIGNED *th; /* current TCP header */
|
|
|
|
// ----------------------------
|
|
// 47. The two most common operations on the connection list are a `find'
|
|
// that terminates at the first entry (a new packet for the most recently
|
|
// used connection) and moving the last entry on the list to the head of
|
|
// the list (the first packet from a new connection). A circular list
|
|
// efficiently handles these two operations.
|
|
|
|
ULONG deltaS, deltaA; /* general purpose temporaries */
|
|
ULONG changes = 0; /* change mask */
|
|
UCHAR new_seq[16]; /* changes from last to current */
|
|
UCHAR UNALIGNED *cp = new_seq;
|
|
USHORT ip_len;
|
|
|
|
/*
|
|
* Bail if this is an IP fragment or if the TCP packet isn't
|
|
* `compressible' (i.e., ACK isn't set or some other control bit is
|
|
* set). Or if it does not contain the TCP protocol.
|
|
*/
|
|
if ((ip->ip_off & 0xff3f) || *m_len < 40 || ip->ip_p != IPPROTO_TCP)
|
|
return (TYPE_IP);
|
|
|
|
th = (struct tcphdr UNALIGNED *) & ((ULONG UNALIGNED *) ip)[hlen];
|
|
if ((th->th_flags & (TH_SYN | TH_FIN | TH_RST | TH_ACK)) != TH_ACK)
|
|
return (TYPE_IP);
|
|
|
|
//
|
|
// The TCP/IP stack is propagating the padding bytes that it
|
|
// is receiving off of the LAN. This shows up here as a
|
|
// packet that has a length that is greater than the IP datagram
|
|
// length. We will add this work around for now.
|
|
//
|
|
if (*m_len > ntohs(ip->ip_len)) {
|
|
*m_len = ntohs(ip->ip_len);
|
|
}
|
|
|
|
/*
|
|
* 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. Special case the most
|
|
* recently used connection since it's most likely to be used again &
|
|
* we don't have to do any reordering if it's used.
|
|
*/
|
|
|
|
//
|
|
// Keep stats here
|
|
//
|
|
INCR(OutPackets);
|
|
|
|
csip = (struct ip_v4 UNALIGNED*)&cs->cs_ip;
|
|
|
|
if (ip->ip_src.s_addr != csip->ip_src.s_addr ||
|
|
ip->ip_dst.s_addr != csip->ip_dst.s_addr ||
|
|
*(ULONG UNALIGNED *) th != ((ULONG UNALIGNED *) csip)[csip->ip_hl & 0x0F]) {
|
|
|
|
/*
|
|
* Wasn't the first -- search for it.
|
|
*
|
|
* States are kept in a circularly linked list with last_cs
|
|
* 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.
|
|
*/
|
|
struct cstate *lcs;
|
|
struct cstate *lastcs = comp->last_cs;
|
|
|
|
do {
|
|
lcs = cs;
|
|
cs = cs->cs_next;
|
|
INCR(OutSearches);
|
|
|
|
csip = (struct ip_v4 UNALIGNED*)&cs->cs_ip;
|
|
|
|
if (ip->ip_src.s_addr == csip->ip_src.s_addr &&
|
|
ip->ip_dst.s_addr == csip->ip_dst.s_addr &&
|
|
*(ULONG UNALIGNED *) th == ((ULONG UNALIGNED *) csip)[cs->cs_ip.ip_hl & 0x0F])
|
|
|
|
goto found;
|
|
|
|
} while (cs != lastcs);
|
|
|
|
/*
|
|
* 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 last_cs to
|
|
* update the lru linkage.
|
|
*/
|
|
|
|
INCR(OutMisses);
|
|
|
|
//
|
|
// A miss!
|
|
//
|
|
comp->last_cs = lcs;
|
|
hlen += (th->th_off >> 4);
|
|
hlen <<= 2;
|
|
|
|
if (hlen > *m_len) {
|
|
return(TYPE_IP);
|
|
}
|
|
|
|
goto uncompressed;
|
|
|
|
found:
|
|
/* Found it -- move to the front on the connection list. */
|
|
if (cs == lastcs)
|
|
comp->last_cs = lcs;
|
|
else {
|
|
lcs->cs_next = cs->cs_next;
|
|
cs->cs_next = lastcs->cs_next;
|
|
lastcs->cs_next = cs;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure that only what we expect to change changed. The first
|
|
* line of the `if' checks the IP protocol version, header length &
|
|
* type of service. The 2nd line checks the "Don't fragment" bit.
|
|
* The 3rd line checks the time-to-live and protocol (the protocol
|
|
* check is unnecessary but costless). The 4th line checks the TCP
|
|
* header length. The 5th line checks IP options, if any. The 6th
|
|
* line checks TCP options, if any. If any of these things are
|
|
* different between the previous & current datagram, we send the
|
|
* current datagram `uncompressed'.
|
|
*/
|
|
oth = (struct tcphdr UNALIGNED *) & ((ULONG UNALIGNED *) csip)[hlen];
|
|
deltaS = hlen;
|
|
hlen += (th->th_off >> 4);
|
|
hlen <<= 2;
|
|
|
|
//
|
|
// Bug fix? It's in cslip.tar.Z
|
|
//
|
|
if (hlen > *m_len) {
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("Bad TCP packet length"));
|
|
return(TYPE_IP);
|
|
}
|
|
|
|
if (((USHORT UNALIGNED *) ip)[0] != ((USHORT UNALIGNED *) csip)[0] ||
|
|
((USHORT UNALIGNED *) ip)[3] != ((USHORT UNALIGNED *) csip)[3] ||
|
|
((USHORT UNALIGNED *) ip)[4] != ((USHORT UNALIGNED *) csip)[4] ||
|
|
(th->th_off >> 4) != (oth->th_off >> 4) ||
|
|
(deltaS > 5 &&
|
|
memcmp((UCHAR UNALIGNED *)(ip + 1), (UCHAR UNALIGNED *)(csip + 1), (deltaS - 5) << 2)) ||
|
|
((th->th_off >> 4) > 5 &&
|
|
memcmp((UCHAR UNALIGNED *)(th + 1), (UCHAR UNALIGNED *)(oth + 1), ((th->th_off >> 4) - 5) << 2))) {
|
|
|
|
goto uncompressed;
|
|
}
|
|
|
|
/*
|
|
* Figure out which of the changing fields changed. The receiver
|
|
* expects changes in the order: urgent, window, ack, seq.
|
|
*/
|
|
if (th->th_flags & TH_URG) {
|
|
deltaS = ntohs(th->th_urp);
|
|
ENCODEZ(deltaS);
|
|
changes |= NEW_U;
|
|
} else if (th->th_urp != oth->th_urp) {
|
|
|
|
/*
|
|
* 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 = (USHORT) (ntohs(th->th_win) - ntohs(oth->th_win))) {
|
|
ENCODE(deltaS);
|
|
changes |= NEW_W;
|
|
}
|
|
if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) {
|
|
if (deltaA > 0xffff) {
|
|
goto uncompressed;
|
|
}
|
|
|
|
ENCODE(deltaA);
|
|
changes |= NEW_A;
|
|
}
|
|
if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) {
|
|
if (deltaS > 0xffff) {
|
|
goto uncompressed;
|
|
}
|
|
|
|
ENCODE(deltaS);
|
|
changes |= NEW_S;
|
|
}
|
|
|
|
ip_len = ntohs(csip->ip_len);
|
|
|
|
/*
|
|
* Look for the special-case encodings.
|
|
*/
|
|
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->ip_len != csip->ip_len &&
|
|
ip_len == hlen)
|
|
|
|
break;
|
|
|
|
/* (fall through) */
|
|
|
|
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 == (ip_len - hlen)) {
|
|
/* special case for echoed terminal traffic */
|
|
changes = SPECIAL_I;
|
|
cp = new_seq;
|
|
}
|
|
break;
|
|
|
|
case NEW_S:
|
|
if (deltaS == (ip_len - hlen)) {
|
|
/* special case for data xfer */
|
|
changes = SPECIAL_D;
|
|
cp = new_seq;
|
|
}
|
|
break;
|
|
}
|
|
|
|
deltaS = ntohs(ip->ip_id) - ntohs(csip->ip_id);
|
|
|
|
if (deltaS != 1) {
|
|
ENCODEZ(deltaS);
|
|
changes |= NEW_I;
|
|
}
|
|
|
|
if (th->th_flags & TH_PUSH)
|
|
changes |= TCP_PUSH_BIT;
|
|
/*
|
|
* Grab the cksum before we overwrite it below. Then update our
|
|
* state with this packet's header.
|
|
*/
|
|
deltaA = (th->th_sumhi << 8) + th->th_sumlo;
|
|
|
|
NdisMoveMemory((UCHAR UNALIGNED *)csip,
|
|
(UCHAR UNALIGNED *)ip,
|
|
hlen);
|
|
|
|
/*
|
|
* 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. hlen is how many bytes
|
|
* of the original packet to toss so subtract the two to get the new
|
|
* packet size.
|
|
*/
|
|
deltaS = (ULONG)(cp - new_seq);
|
|
cp = (UCHAR UNALIGNED *) ip;
|
|
*precomph_len = hlen;
|
|
|
|
if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
|
|
comp->last_xmit = cs->cs_id;
|
|
hlen -= deltaS + 4;
|
|
*postcomph_len = deltaS + 4;
|
|
cp += hlen;
|
|
*cp++ = (UCHAR)(changes | NEW_C);
|
|
*cp++ = cs->cs_id;
|
|
} else {
|
|
hlen -= deltaS + 3;
|
|
*postcomph_len = deltaS + 3;
|
|
cp += hlen;
|
|
*cp++ = (UCHAR)changes;
|
|
}
|
|
|
|
*m_len -= hlen;
|
|
*m_off += hlen;
|
|
*cp++ = (UCHAR)(deltaA >> 8);
|
|
*cp++ = (UCHAR)(deltaA);
|
|
|
|
NdisMoveMemory((UCHAR UNALIGNED *)cp,
|
|
(UCHAR UNALIGNED *)new_seq,
|
|
deltaS);
|
|
|
|
INCR(OutCompressed);
|
|
return (TYPE_COMPRESSED_TCP);
|
|
|
|
uncompressed:
|
|
/*
|
|
* Update connection state cs & send uncompressed packet
|
|
* ('uncompressed' means a regular ip/tcp packet but with the
|
|
* 'conversation id' we hope to use on future compressed packets in
|
|
* the protocol field).
|
|
*/
|
|
|
|
NdisMoveMemory((UCHAR UNALIGNED *)csip,
|
|
(UCHAR UNALIGNED *)ip,
|
|
hlen);
|
|
|
|
ip->ip_p = cs->cs_id;
|
|
comp->last_xmit = cs->cs_id;
|
|
return (TYPE_UNCOMPRESSED_TCP);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// A.3 Decompression
|
|
//
|
|
// This routine decompresses a received packet. It is called with a
|
|
// pointer to the packet, the packet length and type, and a pointer to the
|
|
// compression state structure for the incoming serial line. It returns a
|
|
// pointer to the resulting packet or zero if there were errors in the
|
|
// incoming packet. If the packet is COMPRESSED_TCP or UNCOMPRESSED_TCP,
|
|
// the compression state will be updated.
|
|
//
|
|
// The new packet will be constructed in-place. That means that there must
|
|
// be 128 bytes of free space in front of bufp to allow room for the
|
|
// reconstructed IP and TCP headers. The reconstructed packet will be
|
|
// aligned on a 32-bit boundary.
|
|
//
|
|
|
|
//LONG
|
|
//sl_uncompress_tcp(
|
|
// PUUCHAR UNALIGNED *bufp,
|
|
// LONG len,
|
|
// UCHAR type,
|
|
// struct slcompress *comp) {
|
|
LONG
|
|
sl_uncompress_tcp(
|
|
PUUCHAR UNALIGNED *InBuffer,
|
|
PLONG InLength,
|
|
UCHAR UNALIGNED *OutBuffer,
|
|
PLONG OutLength,
|
|
UCHAR type,
|
|
struct slcompress *comp
|
|
)
|
|
{
|
|
UCHAR UNALIGNED *cp;
|
|
LONG inlen;
|
|
LONG hlen, changes;
|
|
struct tcphdr UNALIGNED *th;
|
|
struct cstate *cs;
|
|
struct ip_v4 UNALIGNED *ip;
|
|
|
|
inlen = *InLength;
|
|
|
|
switch (type) {
|
|
|
|
case TYPE_ERROR:
|
|
default:
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("Packet transmission error type 0x%.2x",type));
|
|
goto bad;
|
|
|
|
case TYPE_IP:
|
|
break;
|
|
|
|
case TYPE_UNCOMPRESSED_TCP:
|
|
/*
|
|
* Locate the saved state for this connection. If the state
|
|
* index is legal, clear the 'discard' flag.
|
|
*/
|
|
ip = (struct ip_v4 UNALIGNED *) *InBuffer;
|
|
if (ip->ip_p >= comp->MaxStates) {
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("Max state exceeded %u", ip->ip_p));
|
|
goto bad;
|
|
}
|
|
|
|
cs = &comp->rstate[comp->last_recv = ip->ip_p];
|
|
comp->flags &= ~SLF_TOSS;
|
|
|
|
/*
|
|
* Restore the IP protocol field then save a copy of this
|
|
* packet header. (The checksum is zeroed in the copy so we
|
|
* don't have to zero it each time we process a compressed
|
|
* packet.
|
|
*/
|
|
hlen = ip->ip_hl & 0x0F;
|
|
hlen += ((struct tcphdr UNALIGNED *) & ((ULONG UNALIGNED *) ip)[hlen])->th_off >> 4;
|
|
hlen <<= 2;
|
|
|
|
if (hlen > inlen) {
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("recv'd runt uncompressed packet %d %d", hlen, inlen));
|
|
goto bad;
|
|
}
|
|
|
|
NdisMoveMemory((PUCHAR)&cs->cs_ip,
|
|
(PUCHAR)ip,
|
|
hlen);
|
|
|
|
cs->cs_ip.ip_p = IPPROTO_TCP;
|
|
|
|
NdisMoveMemory((PUCHAR)OutBuffer,
|
|
(PUCHAR)&cs->cs_ip,
|
|
hlen);
|
|
|
|
cs->cs_ip.ip_sum = 0;
|
|
cs->cs_hlen = (USHORT)hlen;
|
|
|
|
*InBuffer = (PUCHAR)ip + hlen;
|
|
*InLength = inlen - hlen;
|
|
*OutLength = hlen;
|
|
|
|
INCR(InUncompressed);
|
|
return (inlen);
|
|
|
|
case TYPE_COMPRESSED_TCP:
|
|
break;
|
|
}
|
|
|
|
/* We've got a compressed packet. */
|
|
INCR(InCompressed);
|
|
cp = *InBuffer;
|
|
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.
|
|
*/
|
|
if (*cp >= comp->MaxStates) {
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("MaxState of %u too big", *cp));
|
|
goto bad;
|
|
}
|
|
|
|
comp->flags &= ~SLF_TOSS;
|
|
comp->last_recv = *cp++;
|
|
} 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) {
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("Packet has state index, have to toss it"));
|
|
INCR(InTossed);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the state then fill in the TCP checksum and PUSH bit.
|
|
*/
|
|
|
|
cs = &comp->rstate[comp->last_recv];
|
|
|
|
//
|
|
// If there was a line error and we did not get notified we could
|
|
// miss a TYPE_UNCOMPRESSED_TCP which would leave us with an
|
|
// un-init'd cs!
|
|
//
|
|
if (cs->cs_hlen == 0) {
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("Un-Init'd state!"));
|
|
goto bad;
|
|
}
|
|
|
|
hlen = (cs->cs_ip.ip_hl & 0x0F) << 2;
|
|
th = (struct tcphdr UNALIGNED *) & ((UCHAR UNALIGNED *) &cs->cs_ip)[hlen];
|
|
|
|
th->th_sumhi = cp[0];
|
|
th->th_sumlo = cp[1];
|
|
|
|
cp += 2;
|
|
if (changes & TCP_PUSH_BIT)
|
|
th->th_flags |= TH_PUSH;
|
|
else
|
|
th->th_flags &= ~TH_PUSH;
|
|
|
|
/*
|
|
* Fix up the state's ack, seq, urg and win fields based on the
|
|
* changemask.
|
|
*/
|
|
switch (changes & SPECIALS_MASK) {
|
|
case SPECIAL_I:
|
|
{
|
|
UCHAR UNALIGNED * piplen=(UCHAR UNALIGNED *)&(cs->cs_ip.ip_len);
|
|
UCHAR UNALIGNED * ptcplen;
|
|
ULONG tcplen;
|
|
ULONG i;
|
|
|
|
i = ((piplen[0] << 8) + piplen[1]) - cs->cs_hlen;
|
|
|
|
// th->th_ack = htonl(ntohl(th->th_ack) + i);
|
|
|
|
ptcplen=(UCHAR UNALIGNED *)&(th->th_ack);
|
|
tcplen=(ptcplen[0] << 24) + (ptcplen[1] << 16) +
|
|
(ptcplen[2] << 8) + ptcplen[3] + i;
|
|
ptcplen[3]=(UCHAR)(tcplen);
|
|
ptcplen[2]=(UCHAR)(tcplen >> 8);
|
|
ptcplen[1]=(UCHAR)(tcplen >> 16);
|
|
ptcplen[0]=(UCHAR)(tcplen >> 24);
|
|
|
|
|
|
// th->th_seq = htonl(ntohl(th->th_seq) + i);
|
|
|
|
ptcplen=(UCHAR UNALIGNED *)&(th->th_seq);
|
|
tcplen=(ptcplen[0] << 24) + (ptcplen[1] << 16) +
|
|
(ptcplen[2] << 8) + ptcplen[3] + i;
|
|
ptcplen[3]=(UCHAR)(tcplen);
|
|
ptcplen[2]=(UCHAR)(tcplen >> 8);
|
|
ptcplen[1]=(UCHAR)(tcplen >> 16);
|
|
ptcplen[0]=(UCHAR)(tcplen >> 24);
|
|
|
|
}
|
|
break;
|
|
|
|
case SPECIAL_D:
|
|
{
|
|
// th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
|
|
// - cs->cs_hlen);
|
|
|
|
UCHAR UNALIGNED *piplen=(UCHAR UNALIGNED *)&(cs->cs_ip.ip_len);
|
|
UCHAR UNALIGNED *ptcplen;
|
|
ULONG tcplen;
|
|
ULONG i;
|
|
|
|
i = ((piplen[0] << 8) + piplen[1]) - cs->cs_hlen;
|
|
|
|
ptcplen=(UCHAR UNALIGNED *)&(th->th_seq);
|
|
tcplen=(ptcplen[0] << 24) + (ptcplen[1] << 16) +
|
|
(ptcplen[2] << 8) + ptcplen[3] + i;
|
|
|
|
ptcplen[3]=(UCHAR)(tcplen);
|
|
ptcplen[2]=(UCHAR)(tcplen >> 8);
|
|
ptcplen[1]=(UCHAR)(tcplen >> 16);
|
|
ptcplen[0]=(UCHAR)(tcplen >> 24);
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
if (changes & NEW_U) {
|
|
th->th_flags |= TH_URG;
|
|
DECODEU(th->th_urp)
|
|
} else
|
|
th->th_flags &= ~TH_URG;
|
|
|
|
if (changes & NEW_W)
|
|
DECODES(th->th_win);
|
|
if (changes & NEW_A)
|
|
DECODEL(th->th_ack)
|
|
if (changes & NEW_S)
|
|
DECODEL(th->th_seq)
|
|
|
|
break;
|
|
}
|
|
/* Update the IP ID */
|
|
if (changes & NEW_I) {
|
|
|
|
DECODES(cs->cs_ip.ip_id)
|
|
|
|
} else {
|
|
|
|
USHORT id;
|
|
UCHAR UNALIGNED *pid = (UCHAR UNALIGNED *)&(cs->cs_ip.ip_id);
|
|
|
|
// cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
|
|
id=(pid[0] << 8) + pid[1] + 1;
|
|
pid[0]=(UCHAR)(id >> 8);
|
|
pid[1]=(UCHAR)(id);
|
|
}
|
|
|
|
|
|
/*
|
|
* At this point, cp points to the first byte of data in the packet.
|
|
* If we're not aligned on a 4-byte boundary, copy the data down so
|
|
* the IP & TCP headers will be aligned. Then back up cp by the
|
|
* TCP/IP header length to make room for the reconstructed header (we
|
|
* assume the packet we were handed has enough space to prepend 128
|
|
* bytes of header). Adjust the lenth to account for the new header
|
|
* & fill in the IP total length.
|
|
*/
|
|
// len -= (cp - *bufp);
|
|
inlen -= (ULONG)(cp - *InBuffer);
|
|
|
|
if (inlen < 0) {
|
|
|
|
/*
|
|
* we must have dropped some characters (crc should detect
|
|
* this but the old slip framing won't)
|
|
*/
|
|
NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("len has dropped below 0!"));
|
|
goto bad;
|
|
}
|
|
//
|
|
// Who Cares about 4 byte alignement! It's just a useless big copy!
|
|
//
|
|
// if ((ULONG) cp & 3) {
|
|
// if (len > 0)
|
|
// //
|
|
// // BUG BUG we want OVBCOPY..
|
|
// //
|
|
// NdisMoveMemory(
|
|
// (PUCHAR)((ULONG) cp & ~3),
|
|
// cp,
|
|
// len);
|
|
// cp = (PUCHAR) ((ULONG) cp & ~3);
|
|
// }
|
|
|
|
// cp -= cs->cs_hlen;
|
|
// len += cs->cs_hlen;
|
|
|
|
// cs->cs_ip.ip_len = htons(len);
|
|
cs->cs_ip.ip_len = htons(inlen + cs->cs_hlen);
|
|
|
|
// NdisMoveMemory(
|
|
// (PUCHAR)cp,
|
|
// (PUCHAR)&cs->cs_ip,
|
|
// cs->cs_hlen);
|
|
|
|
NdisMoveMemory((PUCHAR)OutBuffer,
|
|
(PUCHAR)&cs->cs_ip,
|
|
cs->cs_hlen);
|
|
|
|
// *bufp = cp;
|
|
*InBuffer = cp;
|
|
*InLength = inlen;
|
|
*OutLength = cs->cs_hlen;
|
|
|
|
/* recompute the ip header checksum */
|
|
{
|
|
// USHORT UNALIGNED * bp = (USHORT UNALIGNED *) cp;
|
|
USHORT UNALIGNED * bp = (USHORT UNALIGNED *) OutBuffer;
|
|
|
|
for (changes = 0; hlen > 0; hlen -= 2)
|
|
changes += *bp++;
|
|
|
|
changes = (changes & 0xffff) + (changes >> 16);
|
|
changes = (changes & 0xffff) + (changes >> 16);
|
|
// ((struct ip_v4 UNALIGNED *) cp)->ip_sum = (USHORT)~changes;
|
|
((struct ip_v4 UNALIGNED *) OutBuffer)->ip_sum = (USHORT)~changes;
|
|
}
|
|
|
|
return (inlen + cs->cs_hlen);
|
|
|
|
bad:
|
|
comp->flags |= SLF_TOSS;
|
|
INCR(InErrors);
|
|
return (0);
|
|
}
|
|
|
|
|
|
|
|
|
|
// A.4 Initialization
|
|
//
|
|
// This routine initializes the state structure for both the transmit and
|
|
// receive halves of some serial line. It must be called each time the
|
|
// line is brought up.
|
|
//
|
|
|
|
VOID
|
|
WanInitVJ(
|
|
VOID
|
|
)
|
|
{
|
|
#if 0
|
|
NdisInitializeNPagedLookasideList(&VJCtxList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(slcompress),
|
|
VJCTX_TAG,
|
|
0);
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
WanDeleteVJ(
|
|
VOID
|
|
)
|
|
{
|
|
#if 0
|
|
NdisDeleteNPagedLookasideList(&VJCtxList);
|
|
#endif
|
|
}
|
|
|
|
NDIS_STATUS
|
|
sl_compress_init(
|
|
struct slcompress **retcomp,
|
|
UCHAR MaxStates
|
|
)
|
|
{
|
|
ULONG i;
|
|
struct cstate *tstate; // = comp->tstate;
|
|
struct slcompress *comp;
|
|
|
|
comp = *retcomp;
|
|
|
|
//
|
|
// Do we need to allocate memory for this bundle
|
|
//
|
|
|
|
if (comp == NULL) {
|
|
|
|
NdisWanAllocateMemory(&comp, sizeof(slcompress), VJCOMPRESS_TAG);
|
|
|
|
//
|
|
// If there was no memory to allocate
|
|
//
|
|
if (comp == NULL) {
|
|
|
|
return(NDIS_STATUS_RESOURCES);
|
|
}
|
|
}
|
|
|
|
tstate = comp->tstate;
|
|
|
|
/*
|
|
* Clean out any junk left from the last time line was used.
|
|
*/
|
|
NdisZeroMemory(
|
|
(PUCHAR) comp,
|
|
sizeof(*comp));
|
|
|
|
/*
|
|
* Link the transmit states into a circular list.
|
|
*/
|
|
for (i = MaxStates - 1; i > 0; --i) {
|
|
tstate[i].cs_id = (UCHAR)i;
|
|
tstate[i].cs_next = &tstate[i - 1];
|
|
}
|
|
|
|
tstate[0].cs_next = &tstate[MaxStates - 1];
|
|
tstate[0].cs_id = 0;
|
|
comp->last_cs = &tstate[0];
|
|
|
|
/*
|
|
* Make sure we don't accidentally do CID compression
|
|
* (assumes MAX_VJ_STATES < 255).
|
|
*/
|
|
comp->last_recv = 255;
|
|
comp->last_xmit = 255;
|
|
comp->flags = SLF_TOSS;
|
|
comp->MaxStates=MaxStates;
|
|
|
|
*retcomp = comp;
|
|
|
|
return (NDIS_STATUS_SUCCESS);
|
|
}
|
|
|
|
VOID
|
|
sl_compress_terminate(
|
|
struct slcompress **comp
|
|
)
|
|
{
|
|
if (*comp != NULL) {
|
|
NdisWanFreeMemory(*comp);
|
|
*comp = NULL;
|
|
}
|
|
}
|
|
|
|
// A.5 Berkeley Unix dependencies
|
|
//
|
|
// Note: The following is of interest only if you are trying to bring the
|
|
// sample code up on a system that is not derived from 4BSD (Berkeley
|
|
// Unix).
|
|
//
|
|
// The code uses the normal Berkeley Unix header files (from
|
|
// /usr/include/netinet) for definitions of the structure of IP and TCP
|
|
// headers. The structure tags tend to follow the protocol RFCs closely
|
|
// and should be obvious even if you do not have access to a 4BSD
|
|
// system./48/
|
|
//
|
|
// ----------------------------
|
|
// 48. In the event they are not obvious, the header files (and all the
|
|
// Berkeley networking code) can be anonymous ftp'd from host
|
|
//
|
|
//
|
|
// The macro BCOPY(src, dst, amt) is invoked to copy amt bytes from src to
|
|
// dst. In BSD, it translates into a call to BCOPY. If you have the
|
|
// misfortune to be running System-V Unix, it can be translated into a call
|
|
// to memcpy. The macro OVBCOPY(src, dst, amt) is used to copy when src
|
|
// and dst overlap (i.e., when doing the 4-byte alignment copy). In the
|
|
// BSD kernel, it translates into a call to ovbcopy. Since AT&T botched
|
|
// the definition of memcpy, this should probably translate into a copy
|
|
// loop under System-V.
|
|
//
|
|
// The macro BCMP(src, dst, amt) is invoked to compare amt bytes of src and
|
|
// dst for equality. In BSD, it translates into a call to bcmp. In
|
|
// System-V, it can be translated into a call to memcmp or you can write a
|
|
// routine to do the compare. The routine should return zero if all bytes
|
|
// of src and dst are equal and non-zero otherwise.
|
|
//
|
|
// The routine ntohl(dat) converts (4 byte) long dat from network byte
|
|
// order to host byte order. On a reasonable cpu this can be the no-op
|
|
// macro:
|
|
// #define ntohl(dat) (dat)
|
|
//
|
|
// On a Vax or IBM PC (or anything with Intel byte order), you will have to
|
|
// define a macro or routine to rearrange bytes.
|
|
//
|
|
// The routine ntohs(dat) is like ntohl but converts (2 byte) shorts
|
|
// instead of longs. The routines htonl(dat) and htons(dat) do the inverse
|
|
// transform (host to network byte order) for longs and shorts.
|
|
//
|
|
// A struct mbuf is used in the call to sl_compress_tcp because that
|
|
// routine needs to modify both the start address and length if the
|
|
// incoming packet is compressed. In BSD, an mbuf is the kernel's buffer
|
|
// management structure. If other systems, the following definition should
|
|
// be sufficient:
|
|
//
|
|
// struct mbuf {
|
|
// UCHAR *m_off; /* pointer to start of data */
|
|
// int m_len; /* length of data */
|
|
// };
|
|
//
|
|
// #define mtod(m, t) ((t)(m->m_off))
|
|
//
|
|
//
|
|
// B Compatibility with past mistakes
|
|
//
|
|
//
|
|
// When combined with the modern PPP serial line protocol[9], the use of
|
|
// header compression is automatic and invisible to the user.
|
|
// Unfortunately, many sites have existing users of the SLIP described in
|
|
// [12] which doesn't allow for different protocol types to distinguish
|
|
// header compressed packets from IP packets or for version numbers or an
|
|
// option exchange that could be used to automatically negotiate header
|
|
// compression.
|
|
//
|
|
// The author has used the following tricks to allow header compressed SLIP
|
|
// to interoperate with the existing servers and clients. Note that these
|
|
// are hacks for compatibility with past mistakes and should be offensive
|
|
// to any right thinking person. They are offered solely to ease the pain
|
|
// of running SLIP while users wait patiently for vendors to release PPP.
|
|
//
|
|
//
|
|
// B.1 Living without a framing `type' byte
|
|
//
|
|
// The bizarre packet type numbers in sec. A.1 were chosen to allow a
|
|
// `packet type' to be sent on lines where it is undesirable or impossible
|
|
// to add an explicit type byte. Note that the first byte of an IP packet
|
|
// always contains `4' (the IP protocol version) in the top four bits. And
|
|
// that the most significant bit of the first byte of the compressed header
|
|
// is ignored. Using the packet types in sec. A.1, the type can be encoded
|
|
// in the most significant bits of the outgoing packet using the code
|
|
//
|
|
// p->dat[0] |= sl_compress_tcp(p, comp);
|
|
//
|
|
// and decoded on the receive side by
|
|
//
|
|
// if (p->dat[0] & 0x80)
|
|
// type = TYPE_COMPRESSED_TCP;
|
|
// else if (p->dat[0] >= 0x70) {
|
|
// type = TYPE_UNCOMPRESSED_TCP;
|
|
// p->dat[0] &=~ 0x30;
|
|
// } else
|
|
// type = TYPE_IP;
|
|
// status = sl_uncompress_tcp(p, type, comp);
|
|
|
|
|