📄 vjhc.c
字号:
/***********************************************************************//* *//* Module: tcp_ip/ppp/vjhc.c *//* Release: 2001.3 *//* Version: 2001.0 *//* Purpose: Van Jacobson TCP/IP header compression *//* *//*---------------------------------------------------------------------*//* *//* Copyright 2001, Blunk Microsystems *//* ALL RIGHTS RESERVED *//* *//* Licensees have the non-exclusive right to use, modify, or extract *//* this computer program for software development at a single site. *//* This program may be resold or disseminated in executable format *//* only. The source code may not be redistributed or resold. *//* *//*---------------------------------------------------------------------*//* *//* 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. *//* *//***********************************************************************/#include "pppp.h"#include <string.h>#if MAX_PPP_INTF/***********************************************************************//* Symbol Definitions *//***********************************************************************//*** Change Mask Definitions (first byte of compressed 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*/#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)/*** Flag Value*/#define VJHCF_TOSS 1 /* tossing rcvd frames because of input err *//***********************************************************************//* Macro Definitions *//***********************************************************************//*** The following macros are used to encode and decode numbers. They all** assume `cp' points to a buffer where the next byte encoded (decoded)** is to be stored (retrieved). Since the decode routines do arithmetic,** they have to convert from and to network byte order.*//*** ENCODE encodes a number that is known to be non-zero. ENCODEZ checks** for zero (zero has to be encoded in the long, 3 byte form).*/#define ENCODE(n) \ { \ if ((ui16)(n) >= 256) \ { \ *cp++ = 0; \ cp[1] = (n); \ cp[0] = (n) >> 8; \ cp += 2; \ } \ else \ { \ *cp++ = (n); \ } \ }#define ENCODEZ(n) \ { \ if ((ui16)(n) >= 256 || (ui16)(n) == 0) \ { \ *cp++ = 0; \ cp[1] = (n); \ cp[0] = (n) >> 8; \ cp += 2; \ } \ else \ { \ *cp++ = (n); \ } \ }/*** DECODEL takes the (compressed) change at byte cp and adds it to the** current value of packet field 'f' (which must be a 4-byte (long)** integer in network byte order). DECODES does the same for a 2-byte** (short) field. DECODEU takes the change at cp and stuffs it into the** (short) field f. 'cp' is updated to point to the next field in the** compressed header.*/#define DECODEL(f) \ { \ if (*cp == 0) \ { \ (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ cp += 3; \ } \ else \ { \ (f) = htonl(ntohl(f) + (ui32)*cp++); \ } \ }#define DECODES(f) \ { \ if (*cp == 0) \ { \ (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ cp += 3; \ } \ else \ { \ (f) = htons(ntohs(f) + (ui32)*cp++); \ } \ }#define DECODEU(f) \ { \ if (*cp == 0) \ { \ (f) = htons((cp[1] << 8) | cp[2]); \ cp += 3; \ } \ else \ { \ (f) = htons((ui32)*cp++); \ } \ }/***********************************************************************//* Global Function Definitions *//***********************************************************************//***********************************************************************//* vjhc_init: Initialize the state structure for both the transmit *//* and receive halves of a serial line. Must be called *//* each time a line is brought up. *//* *//***********************************************************************/void vjhc_init(struct vjcompress *comp, int rslots, int tslots){ uint i; struct cstate *tstate = comp->tstate; /*-------------------------------------------------------------------*/ /* Clean out any old state information. */ /*-------------------------------------------------------------------*/ memset(comp, 0, sizeof(struct vjcompress)); /*-------------------------------------------------------------------*/ /* Initialize connection's compression constants. */ /*-------------------------------------------------------------------*/ comp->rslot_limit = rslots - 1; comp->tslot_limit = tslots - 1; /*-------------------------------------------------------------------*/ /* Link the transmit states into a circular list. */ /*-------------------------------------------------------------------*/ for (i = comp->tslot_limit; i > 0; --i) { tstate[i].cs_id = i; tstate[i].cs_next = &tstate[i - 1]; } tstate[0].cs_id = 0; tstate[0].cs_next = &tstate[comp->tslot_limit]; comp->last_cs = &tstate[0]; /*-------------------------------------------------------------------*/ /* Make sure we don't accidentally do connection ID compression */ /* (assumes MAX_STATES < 255). */ /*-------------------------------------------------------------------*/ comp->last_recv = 255; comp->last_xmit = 255;}/***********************************************************************//* vjhc_compress: Compresses an outbound TCP packet *//* *//* Inputs: buf = pointer to packet being compressed *//* comp = pointer to line's compression state data *//* comp_cid = flags whether connection id is omitted *//* *//* Returns: Protocol type (PPP_IP_PROTO, PPP_UNCOMP_PROTO, or *//* PPP_COMP_PROTO) *//* *//***********************************************************************/uint vjhc_compress(NetBuf *buf, struct vjcompress *comp, int comp_cid){ struct cstate *cs = comp->last_cs->cs_next; Ip *ip = (Ip *)buf->ip_pkt; uint hlen = ip->ver_len & 0xF; Tcp *oth; /* last TCP header */ Tcp *th; /* current TCP header */ uint deltaS, deltaA; /* general purpose temporaries */ uint changes = 0; /* change mask */ ui8 new_seq[16]; /* changes from last to current */ ui8 *cp = new_seq; /*-------------------------------------------------------------------*/ /* Bail if this is an IP fragment, not a TCP packet, or if the TCP */ /* packet isn't `compressible' (i.e., ACK isn't set or some other */ /* control bit is set). */ /*-------------------------------------------------------------------*/ if ((ip->frag_off & htons(0x3FFF)) || (ip->protocol != IPT_TCP) || buf->length < 40) return PPP_IP_PROTO; th = (Tcp *) &((int *)ip)[hlen]; if ((th->flags & (TCPF_SYN | TCPF_FIN | TCPF_RST | TCPF_ACK)) != TCPF_ACK) return PPP_IP_PROTO; /*-------------------------------------------------------------------*/ /* 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 */ /* and we don't have to do any reordering if it's used. */ /*-------------------------------------------------------------------*/ if ((ip->src_ip != cs->cs_ip.src_ip) || (ip->dst_ip != cs->cs_ip.dst_ip) || (*(int *) th != ((int *) &cs->cs_ip)[cs->cs_ip.ver_len & 0xF])) { /*-----------------------------------------------------------------*/ /* 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 */ /* list head 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; if ((ip->src_ip == cs->cs_ip.src_ip) && (ip->dst_ip == cs->cs_ip.dst_ip) && (*(int *) th == ((int *) &cs->cs_ip)[cs->cs_ip.ver_len & 0xF])) 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. */ /*-----------------------------------------------------------------*/ comp->last_cs = lcs; hlen += (th->offset >> 4); hlen <<= 2; goto uncompressed;found: /*-----------------------------------------------------------------*/ /* Found it -- move to the front on the connection list. */ /*-----------------------------------------------------------------*/ if (lastcs == cs) comp->last_cs = lcs; else { lcs->cs_next = cs->cs_next; cs->cs_next = lastcs->cs_next; lastcs->cs_next = cs; } } /*-------------------------------------------------------------------*/ /* Make sure only what we expect to change changed. The first line */ /* of the `if' checks the IP protocol version, header length, and */ /* 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 and current datagram, we send the */ /* current datagram `uncompressed'. */ /*-------------------------------------------------------------------*/ oth = (Tcp *) &((int *)&cs->cs_ip)[hlen]; deltaS = hlen; deltaA = th->offset >> 4; hlen += deltaA; hlen <<= 2; if (((ui16 *) ip)[0] != ((ui16 *) &cs->cs_ip)[0] || ((ui16 *) ip)[3] != ((ui16 *) &cs->cs_ip)[3] || ((ui16 *) ip)[4] != ((ui16 *) &cs->cs_ip)[4] || th->offset != oth->offset || (deltaA > 5 && memcmp(th + 1, oth + 1, (deltaA - 5) << 2)) || (deltaS > 5 && memcmp(ip + 1, &cs->cs_ip + 1, (deltaS - 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->flags & TCPF_URG) { deltaS = ntohs(th->urg_ptr); ENCODEZ(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; } deltaS = (ui16)(ntohs(th->window) - ntohs(oth->window)); if (deltaS) { ENCODE(deltaS); changes |= NEW_W;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -