📄 vjcomp.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/*
* Portions 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 MERCHANTIBILITY AND FITNESS FOR A
* PARTICULAR PURPOSE.
*/
/*****************************************************************************
*
*
* @doc
* @module vjcomp.c | Van Jacobsen TCP/IP Header Compression
*
* Date: 11-13-95
*
* @comm Modified originial VJ Header Compression Code for ndis buffer
* input. Performed cleanup on general structure and comments for
* functional clarity.
*
*/
// Include Files
#include "windows.h"
#include "cclib.h"
#include "memory.h"
#include "cxport.h"
// VJ Compression Include Files
#include "winsock.h"
#include "ndis.h"
#include "ndiswan.h"
#include "tcpip.h"
#include "vjcomp.h"
// PPP Include Files
#include "protocol.h"
#include "ppp.h"
#include "lcp.h"
#include "ipcp.h"
#include "mac.h"
#include "ip_intf.h"
#include "ras.h"
// TCP and IP Header Length Access Macros
//
// Note: Both macros incorporate a times 4 factor for int to byte conversion.
// All header arithmetic is always done as bytes.
#define IpHdrLen( x ) ((((x)->iph_verlen) & 0x0f) << 2)
// Bits in first octet of compressed packet
// flag bits for what changed in a packet.
#define NEW_C (0x40)
#define NEW_I (0x20)
#define TCP_PUSH_BIT (0x10)
#define NEW_S (0x08)
#define NEW_A (0x04)
#define NEW_W (0x02)
#define NEW_U (0x01)
// Reserved, special-case values of above
//
// Echoed interactive traffic
#define SPECIAL_I ( NEW_S | NEW_W | NEW_U )
// Unidirectional data
#define SPECIAL_D ( NEW_S | NEW_A | NEW_W | NEW_U )
#define SPECIALS_MASK ( NEW_S | NEW_A | NEW_W | NEW_U )
// Debug macro to help pretty-print changes
#define DEBUG_OUTPUT_CHANGES(c) \
((c) & NEW_C ? 'C' : ' '), \
((c) & NEW_I ? 'I' : ' '), \
((c) & TCP_PUSH_BIT ? 'P' : ' '), \
((c) & NEW_S ? 'S' : ' '), \
((c) & NEW_A ? 'A' : ' '), \
((c) & NEW_W ? 'W' : ' '), \
((c) & NEW_U ? 'U' : ' ')
static PBYTE
EncodeValue(
IN PBYTE pEncode,
IN USHORT value)
{
if (1 <= value && value <= 255)
{
*pEncode++ = (BYTE)value;
}
else
{
pEncode[0] = 0;
pEncode[1] = (BYTE)(value >> 8); // MSB first
pEncode[2] = (BYTE)(value); // LSB second
pEncode += 3;
}
return pEncode;
}
#define ENCODE( n ) cp = EncodeValue(cp, (n))
#define ENCODEZ( n ) cp = EncodeValue(cp, (n))
static USHORT
CompressedValueGet(
IN OUT PBYTE *pcp,
IN PBYTE end)
//
// Value may be encoded as one byte if it is in range 1-255.
// If it is >255, then it will be encoded as 0x00 followed by 2 bytes.
//
{
USHORT Value;
PBYTE cp = *pcp;
if (cp >= end)
{
// No more data available, bad packet.
// We tell the caller of the bad packet by setting cp
// beyond the end of the data.
Value = 0;
cp = end + 1;
}
else
{
Value = *cp++;
if (Value == 0)
{
// Value is in next 2 bytes
if ((cp + 1) < end)
Value = (cp[0] << 8) | cp[1];
cp += 2;
}
*pcp = cp;
}
return Value;
}
static ULONG
NetLongAdd(
IN ULONG OldValue,
IN USHORT Addend)
//
// Add a USHORT (in host byte order) to a ULONG (in net byte order).
// Return the sum as a ULONG (in net byte order).
//
{
ULONG NewValue;
OldValue = ntohl(OldValue);
NewValue = OldValue + Addend;
return htonl(NewValue);
}
static ULONG
NetLongAddCompressed(
IN OUT PBYTE *pcp,
IN PBYTE end,
IN ULONG OldValue)
{
USHORT Addend;
Addend = CompressedValueGet(pcp, end);
return NetLongAdd(OldValue, Addend);
}
static USHORT
NetShortAdd(
IN USHORT OldValue,
IN USHORT Addend)
//
// Add a USHORT (in host byte order) to a USHORT (in net byte order).
// Return the sum as a USHORT (in net byte order).
//
{
USHORT NewValue;
OldValue = ntohs(OldValue);
NewValue = OldValue + Addend;
return htons(NewValue);
}
static USHORT
NetShortAddCompressed(
IN OUT PBYTE *pcp,
IN PBYTE end,
IN USHORT OldValue)
{
USHORT Addend;
Addend = CompressedValueGet(pcp, end);
return NetShortAdd(OldValue, Addend);
}
static BYTE
TcpHdrLen( struct TCPHeader *arg )
//
// Return the length in bytes of the TCP header.
//
{
BYTE bHeaderLength;
// Flags is a USHORT field that looks like:
//
// 4 bit header length in DWORDS
// 6 bits reserved
// 6 bits URG/ACK/PSH/RST/SYN/FIN
//
bHeaderLength = *(PBYTE)(&arg->tcp_flags);
bHeaderLength = (bHeaderLength & 0xF0) >> 2;
return bHeaderLength;
}
// 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).
//
// ----------------------------
// 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.
// ----------------------------
USHORT
sl_compress_tcp(
IN PNDIS_WAN_PACKET pPacket,
IN slcompress_t *comp)
{
cstate_t *cs; // compression state pntr
struct IPHeader *ip; // current ip header
struct TCPHeader *tcp; // current TCP header
BYTE ipLen; // ip header length
BYTE tcpLen; // tcp header length
BYTE totalLen; // total length of headers
u_int orig_xsum; // original checksum
u_int newHdrLen; // new header length
u_int changes = 0; // change mask
BYTE new_seq[ 16 ]; // last to current changes
BYTE *cp = new_seq;
u_int deltaS; // general purpose vars
u_int deltaA; // general purpose vars
u_int deltaW; // general purpose vars
memset(new_seq, 0, 16);
if (pPacket->CurrentLength < sizeof(TCPHeader) + sizeof(IPHeader))
{
// Too small to be a TCP packet, no compression possible
return PPP_PROTOCOL_IP;
}
// Init headers pointers and lengths
ip = (struct IPHeader *)pPacket->CurrentBuffer;
ipLen = IpHdrLen(ip);
//
// Check the protocol type to confirm that it is TCP
// If not TCP, don't try to do TCP header compression!
//
if (ip->iph_protocol != 6) // 6==PROTOCOL_TCP
{
return PPP_PROTOCOL_IP;
}
tcp = (struct TCPHeader *)((BYTE *)ip + ipLen);
tcpLen = TcpHdrLen(tcp);
totalLen = ipLen + tcpLen;
// Exit if this is an ip fragment
if (ip->iph_offset & 0xff3f)
{
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ cannot compress - fragmented (offset=%x)\n", ip->iph_offset));
return PPP_PROTOCOL_IP;
}
// Exit if the TCP packet isn't `compressible' i.e.the ACK isn't set
// or some other control bit is set.
if ((tcp->tcp_flags &
(TCP_FLAG_SYN | TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_ACK))
!= TCP_FLAG_ACK)
{
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ cannot compress - tcp_flags=0x%x\n", tcp->tcp_flags));
return PPP_PROTOCOL_IP;
}
// 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.
// Compare the ip/tcp src and dest fields.
cs = comp->last_cs->cs_next; // access compression state
if ((ip->iph_src != cs->cs_ip.iph_src ) ||
(ip->iph_dest != cs->cs_ip.iph_dest ) ||
(tcp->tcp_src != cs->cs_tcp->tcp_src ) ||
(tcp->tcp_dest != cs->cs_tcp->tcp_dest) )
{
// 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.
cstate_t *lcs;
cstate_t *lastcs = comp->last_cs;
do
{
lcs = cs;
cs = cs->cs_next;
// Compare connection data
if ((ip->iph_src == cs->cs_ip.iph_src ) &&
(ip->iph_dest == cs->cs_ip.iph_dest ) &&
(tcp->tcp_src == cs->cs_tcp->tcp_src ) &&
(tcp->tcp_dest == cs->cs_tcp->tcp_dest) )
{
goto found;
}
}
while(cs != lastcs);
// Didn't find it -- re-use oldest cstate_t. 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.
comp->last_cs = lcs;
DEBUGMSG(ZONE_VJ, (L"PPP: TX VJ slot %x No previous cstate, uncompressed\n", cs->cs_id));
goto uncompressed;
found: //----------------------------------------------------------------------
// Found State -- move to the front on the connection list.
if (lastcs == cs)
{
comp->last_cs = lcs;
}
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -