📄 vjcomp.c
字号:
{
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.
//
// Line 1: checks the IP protocol version, header length & TOS
// Line 2: checks the "Don't fragment" bit.
// Line 3: checks the time-to-live and protocol - unnecessary but free.
// Line 4: checks the TCP header length.
// Line 5: checks IP options, if any. '
// Line 6: checks TCP options, if any.
//
// If any of these things are different between the previous & current
// datagram, we send the current datagram `uncompressed'.
if (((USHORT *)ip)[ 0 ] != ((USHORT *)&cs->cs_ip)[ 0 ] ||
((USHORT *)ip)[ 3 ] != ((USHORT *)&cs->cs_ip)[ 3 ] ||
((USHORT *)ip)[ 4 ] != ((USHORT *)&cs->cs_ip)[ 4 ] ||
(tcpLen != cs->cs_tcpLen) ||
((ipLen > 20) && memcmp(ip+1, &(cs->cs_ip) +1, ipLen - 20)) ||
((tcpLen > 20) && memcmp(tcp+1, cs->cs_tcp +1, tcpLen - 20)) )
{
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ slot %x Header changed, uncompressed\n", cs->cs_id));
goto uncompressed;
}
// Figure out which of the changing fields changed. The receiver
// expects changes in the order: urgent, window, ack, seq.
if (tcp->tcp_flags & TCP_FLAG_URG)
{
deltaS = ntohs(tcp->tcp_urgent);
ENCODEZ(deltaS);
changes |= NEW_U;
}
else if (tcp->tcp_urgent != cs->cs_tcp->tcp_urgent)
{
// 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.
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ slot %x URG not set but urp changed, uncompressed\n", cs->cs_id));
goto uncompressed;
}
// Window size change
deltaW = ntohs(tcp->tcp_window) - ntohs(cs->cs_tcp->tcp_window);
if (deltaW)
{
ENCODE(deltaW);
changes |= NEW_W;
}
// Ack changes
deltaA = ntohl(tcp->tcp_ack) - ntohl(cs->cs_tcp->tcp_ack);
if (deltaA > 0xffff)
{
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ slot %x deltaA %x > 0xffff, uncompressed\n", cs->cs_id, deltaA));
goto uncompressed;
}
if (deltaA)
{
ENCODE(deltaA);
changes |= NEW_A;
}
// Sequence # changes
deltaS = ntohl(tcp->tcp_seq) - ntohl(cs->cs_tcp->tcp_seq);
if (deltaS > 0xffff)
{
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ slot %x tcp_seq deltaS %x > 0xffff, uncompressed\n", cs->cs_id, deltaS));
goto uncompressed;
}
if (deltaS)
{
ENCODE(deltaS);
changes |= NEW_S;
}
// 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->iph_length != cs->cs_ip.iph_length) &&
(ntohs(cs->cs_ip.iph_length) == totalLen))
{
break;
}
// (fall through)
case SPECIAL_I:
case SPECIAL_D:
// Actual changes match one of our special case encodings --
// send packet uncompressed.
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ slot %x special case or retransmit (changes=%x), uncompressed\n", cs->cs_id, changes));
goto uncompressed;
break;
case NEW_S | NEW_A:
if (deltaS == deltaA &&
deltaS == (u_int)(ntohs(cs->cs_ip.iph_length) - totalLen))
{
// special case for echoed terminal traffic
changes = SPECIAL_I;
cp = new_seq;
}
break;
case NEW_S:
if (deltaS == (u_int)(ntohs(cs->cs_ip.iph_length) - totalLen))
{
// special case for data xfer
changes = SPECIAL_D;
cp = new_seq;
}
break;
}
deltaS = ntohs(ip->iph_id) - ntohs(cs->cs_ip.iph_id);
if (deltaS != 1)
{
ENCODEZ(deltaS);
changes |= NEW_I;
}
if (tcp->tcp_flags & TCP_FLAG_PUSH)
{
changes |= TCP_PUSH_BIT;
}
// Save the cksum before we overwrite it below.
// Then update our state with this packet's header.
orig_xsum = ntohs(tcp->tcp_xsum);
// Save connection's state
CTEMemCopy(&cs->cs_ip, ip, ipLen + tcpLen);
cs->cs_tcp = (struct TCPHeader *)(cs->slcs_u.hdr + ipLen);
cs->cs_ipLen = ipLen;
cs->cs_tcpLen = tcpLen;
// 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. totalLen is how many bytes
// of the original packet to toss so subtract the two to get the new
// packet size.
newHdrLen = cp - new_seq; // compute new header length
cp = (BYTE *)ip;
if ((0 == (comp->flags & SLF_ENABLE_SLOTID_COMPRESSION_TX)) || (comp->last_xmit != cs->cs_id))
{
// Add compressed header with cid
comp->last_xmit = cs->cs_id;
totalLen -= newHdrLen + 4; // adjust total header size
cp += totalLen; // update start of packet
changes |= NEW_C;
*cp++ = changes; // add change mask + cid flag
*cp++ = cs->cs_id; // add cid
}
else
{
// Add compressed header
totalLen -= newHdrLen + 3; // adjust total header size
cp += totalLen; // update start of packet
*cp++ = changes; // add change mask
}
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ slot %x %c%c%c%c%c%c deltaS=%d deltaA=%d, deltaW=%d\n", cs->cs_id, DEBUG_OUTPUT_CHANGES(changes), deltaS, deltaA, deltaW));
*cp++ = orig_xsum >> 8; // Put back the checksum
*cp++ = orig_xsum;
CTEMemCopy(cp, new_seq, newHdrLen); // Copy in the new header
// Update TX buffer elements
pPacket->CurrentLength -= totalLen;
(BYTE *)pPacket->CurrentBuffer += totalLen;
return PPP_PROTOCOL_COMPRESSED_TCP; // packet is compressed
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).
// Save connection's state
CTEMemCopy(&cs->cs_ip, ip, ipLen + tcpLen);
cs->cs_tcp = (struct TCPHeader *)(cs->slcs_u.hdr + ipLen);
cs->cs_ipLen = ipLen;
cs->cs_tcpLen = tcpLen;
// Update the protocol field with the 'conversation id'
ip->iph_protocol = cs->cs_id;
// Save this id as the last
comp->last_xmit = cs->cs_id;
return PPP_PROTOCOL_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.
//
// NOTE: The received packet is NOT necessarily even aligned. All access
// must take this into account.
BOOL
sl_update_tcp(
IN BYTE *bufp,
IN DWORD len,
IN OUT slcompress_t *comp)
//
// Handle an PPP_PROTOCOL_UNCOMPRESSED_TCP packet header used to update our state.
//
// The TCP/IP header of a VJUIP packet will be unmodified except for the IPHeader
// iph_protocol field which the sender will have changed from 0x06 (TCP) to the
// index of the state element to be updated. This function will
// restore the protocol field to 0x06.
//
// If the packet is invalid return FALSE. If it is valid return TRUE.
//
{
u_int hlen;
struct IPHeader *ip;
cstate_t *cs;
BYTE iState;
// Locate the saved state for this connection. If the state
// index is legal, clear the 'discard' flag.
if (len < sizeof(struct IPHeader))
return FALSE;
ip = (struct IPHeader *)bufp;
hlen = IpHdrLen(ip);
if (hlen + sizeof(TCPHeader) > len)
return FALSE;
hlen += TcpHdrLen((TCPHeader *)(((BYTE *)ip) + hlen));
if (hlen > len)
return FALSE;
iState = ip->iph_protocol;
if (iState >= MAX_STATES)
return FALSE;
if (iState >= comp->MaxStatesRx)
{
DEBUGMSG(ZONE_WARN, (L"PPP: WARNING - Peer sent VJ uncompressed packet slot=%u > negotiated max=%u\n", iState, comp->MaxStatesRx - 1));
}
DEBUGMSG(ZONE_VJ, (L"PPP: RX VJ slot %x uncompressed\n", iState));
comp->last_recv = iState;
cs = &comp->rstate[ iState ];
comp->LastRxFrameBad = FALSE;
// 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.
ip->iph_protocol = IPPROTO_TCP;
CTEMemCopy(&cs->cs_ip, ip, hlen);
cs->cs_ip.iph_xsum = 0;
cs->cs_hlen = hlen;
return TRUE;
}
BYTE *
sl_uncompress_tcp(
IN BYTE *bufp,
IN OUT DWORD *lenp,
IN OUT slcompress_t *comp)
//
// Handle an PPP_PROTOCOL_COMPRESSED_TCP packet header.
//
// Returns a pointer to the uncompressed packet header if successful,
// NULL if it fails.
//
// Format of compressed header:
// BYTE Changes
// BYTE StateIndex (present iff NEW_C is set in Changes)
// BYTE[2] TCPChecksum
//
{
BYTE *cp;
cstate_t *cs;
struct TCPHeader *th;
u_int hlen;
u_int changes;
DWORD cbBuf = *lenp;
USHORT cbData,
cbPacket,
cbUserDataInLastPacket;
PBYTE pBufEnd = bufp + cbBuf;
BYTE iState;
USHORT IpIdIncrement;
USHORT tcp_flags;
// Compressed packet - set frame pointer
cp = bufp;
if (cp >= pBufEnd)
goto bad;
changes = *cp++;
if (changes & NEW_C)
{
if (cp >= pBufEnd)
goto bad;
iState = *cp++;
// Make sure the state index is in range, then grab the state.
// If we have a good state index, clear the 'discard' flag.
if (iState >= MAX_STATES)
{
DEBUGMSG(ZONE_WARN, (L"PPP: WARNING - Peer sent VJ compressed packet slot=%u >= MAX_STATES(%u)\n", iState, MAX_STATES));
goto bad;
}
if (iState >= comp->MaxStatesRx)
{
// Since we allocated MAX_STATES slots, we can handle this slotID. However, the peer
// is technically in violation of the negotiated value so log a debug message.
DEBUGMSG(ZONE_WARN, (L"PPP: WARNING - Peer sent VJ compressed packet slot=%u >= negotiated max=%u\n", iState, comp->MaxStatesRx));
}
comp->LastRxFrameBad = FALSE;
comp->last_recv = iState;
}
else
{
DEBUGMSG(ZONE_WARN && (!(comp->flags & SLF_ENABLE_SLOTID_COMPRESSION_RX)), (L"PPP: RX VJ compressed slot ID, but we requested no slot ID compression\n"));
// 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->LastRxFrameBad)
{
DEBUGMSG(ZONE_VJ, (L"PPP: RX VJ implicit state index but no prior state, toss\n"));
return NULL;
}
iState = comp->last_recv;
//
// If no previous packet was received to initialize the last_recv
// state index, then it will be 255 and this packet is invalid. However,
// the LastRxFrameBad flag which was checked above should have been set at
// init time, so we shouldn't get here.
//
ASSERT(iState < comp->MaxStatesRx);
}
DEBUGMSG(ZONE_VJ, (L"PPP: RX VJ slot %x %c%c%c%c%c%c\n", iState, DEBUG_OUTPUT_CHANGES(changes)));
// Find the state then fill in the TCP checksum and PUSH bit.
cs = &comp->rstate[iState];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -