1195972f6Sopenharmony_ci/* 2195972f6Sopenharmony_ci * Routines to compress and uncompess tcp packets (for transmission 3195972f6Sopenharmony_ci * over low speed serial lines. 4195972f6Sopenharmony_ci * 5195972f6Sopenharmony_ci * Copyright (c) 1989 Regents of the University of California. 6195972f6Sopenharmony_ci * All rights reserved. 7195972f6Sopenharmony_ci * 8195972f6Sopenharmony_ci * Redistribution and use in source and binary forms are permitted 9195972f6Sopenharmony_ci * provided that the above copyright notice and this paragraph are 10195972f6Sopenharmony_ci * duplicated in all such forms and that any documentation, 11195972f6Sopenharmony_ci * advertising materials, and other materials related to such 12195972f6Sopenharmony_ci * distribution and use acknowledge that the software was developed 13195972f6Sopenharmony_ci * by the University of California, Berkeley. The name of the 14195972f6Sopenharmony_ci * University may not be used to endorse or promote products derived 15195972f6Sopenharmony_ci * from this software without specific prior written permission. 16195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17195972f6Sopenharmony_ci * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18195972f6Sopenharmony_ci * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19195972f6Sopenharmony_ci * 20195972f6Sopenharmony_ci * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: 21195972f6Sopenharmony_ci * Initial distribution. 22195972f6Sopenharmony_ci * 23195972f6Sopenharmony_ci * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, 24195972f6Sopenharmony_ci * so that the entire packet being decompressed doesn't have 25195972f6Sopenharmony_ci * to be in contiguous memory (just the compressed header). 26195972f6Sopenharmony_ci * 27195972f6Sopenharmony_ci * Modified March 1998 by Guy Lancaster, glanca@gesn.com, 28195972f6Sopenharmony_ci * for a 16 bit processor. 29195972f6Sopenharmony_ci */ 30195972f6Sopenharmony_ci 31195972f6Sopenharmony_ci#include "netif/ppp/ppp_opts.h" 32195972f6Sopenharmony_ci#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */ 33195972f6Sopenharmony_ci 34195972f6Sopenharmony_ci#include "netif/ppp/ppp_impl.h" 35195972f6Sopenharmony_ci#include "netif/ppp/pppdebug.h" 36195972f6Sopenharmony_ci 37195972f6Sopenharmony_ci#include "netif/ppp/vj.h" 38195972f6Sopenharmony_ci 39195972f6Sopenharmony_ci#include <string.h> 40195972f6Sopenharmony_ci 41195972f6Sopenharmony_ci#if LINK_STATS 42195972f6Sopenharmony_ci#define INCR(counter) ++comp->stats.counter 43195972f6Sopenharmony_ci#else 44195972f6Sopenharmony_ci#define INCR(counter) 45195972f6Sopenharmony_ci#endif 46195972f6Sopenharmony_ci 47195972f6Sopenharmony_civoid 48195972f6Sopenharmony_civj_compress_init(struct vjcompress *comp) 49195972f6Sopenharmony_ci{ 50195972f6Sopenharmony_ci u8_t i; 51195972f6Sopenharmony_ci struct cstate *tstate = comp->tstate; 52195972f6Sopenharmony_ci 53195972f6Sopenharmony_ci#if MAX_SLOTS == 0 54195972f6Sopenharmony_ci memset((char *)comp, 0, sizeof(*comp)); 55195972f6Sopenharmony_ci#endif 56195972f6Sopenharmony_ci comp->maxSlotIndex = MAX_SLOTS - 1; 57195972f6Sopenharmony_ci comp->compressSlot = 0; /* Disable slot ID compression by default. */ 58195972f6Sopenharmony_ci for (i = MAX_SLOTS - 1; i > 0; --i) { 59195972f6Sopenharmony_ci tstate[i].cs_id = i; 60195972f6Sopenharmony_ci tstate[i].cs_next = &tstate[i - 1]; 61195972f6Sopenharmony_ci } 62195972f6Sopenharmony_ci tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; 63195972f6Sopenharmony_ci tstate[0].cs_id = 0; 64195972f6Sopenharmony_ci comp->last_cs = &tstate[0]; 65195972f6Sopenharmony_ci comp->last_recv = 255; 66195972f6Sopenharmony_ci comp->last_xmit = 255; 67195972f6Sopenharmony_ci comp->flags = VJF_TOSS; 68195972f6Sopenharmony_ci} 69195972f6Sopenharmony_ci 70195972f6Sopenharmony_ci 71195972f6Sopenharmony_ci/* ENCODE encodes a number that is known to be non-zero. ENCODEZ 72195972f6Sopenharmony_ci * checks for zero (since zero has to be encoded in the long, 3 byte 73195972f6Sopenharmony_ci * form). 74195972f6Sopenharmony_ci */ 75195972f6Sopenharmony_ci#define ENCODE(n) { \ 76195972f6Sopenharmony_ci if ((u16_t)(n) >= 256) { \ 77195972f6Sopenharmony_ci *cp++ = 0; \ 78195972f6Sopenharmony_ci cp[1] = (u8_t)(n); \ 79195972f6Sopenharmony_ci cp[0] = (u8_t)((n) >> 8); \ 80195972f6Sopenharmony_ci cp += 2; \ 81195972f6Sopenharmony_ci } else { \ 82195972f6Sopenharmony_ci *cp++ = (u8_t)(n); \ 83195972f6Sopenharmony_ci } \ 84195972f6Sopenharmony_ci} 85195972f6Sopenharmony_ci#define ENCODEZ(n) { \ 86195972f6Sopenharmony_ci if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \ 87195972f6Sopenharmony_ci *cp++ = 0; \ 88195972f6Sopenharmony_ci cp[1] = (u8_t)(n); \ 89195972f6Sopenharmony_ci cp[0] = (u8_t)((n) >> 8); \ 90195972f6Sopenharmony_ci cp += 2; \ 91195972f6Sopenharmony_ci } else { \ 92195972f6Sopenharmony_ci *cp++ = (u8_t)(n); \ 93195972f6Sopenharmony_ci } \ 94195972f6Sopenharmony_ci} 95195972f6Sopenharmony_ci 96195972f6Sopenharmony_ci#define DECODEL(f) { \ 97195972f6Sopenharmony_ci if (*cp == 0) {\ 98195972f6Sopenharmony_ci u32_t tmp_ = lwip_ntohl(f) + ((cp[1] << 8) | cp[2]); \ 99195972f6Sopenharmony_ci (f) = lwip_htonl(tmp_); \ 100195972f6Sopenharmony_ci cp += 3; \ 101195972f6Sopenharmony_ci } else { \ 102195972f6Sopenharmony_ci u32_t tmp_ = lwip_ntohl(f) + (u32_t)*cp++; \ 103195972f6Sopenharmony_ci (f) = lwip_htonl(tmp_); \ 104195972f6Sopenharmony_ci } \ 105195972f6Sopenharmony_ci} 106195972f6Sopenharmony_ci 107195972f6Sopenharmony_ci#define DECODES(f) { \ 108195972f6Sopenharmony_ci if (*cp == 0) {\ 109195972f6Sopenharmony_ci u16_t tmp_ = lwip_ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \ 110195972f6Sopenharmony_ci (f) = lwip_htons(tmp_); \ 111195972f6Sopenharmony_ci cp += 3; \ 112195972f6Sopenharmony_ci } else { \ 113195972f6Sopenharmony_ci u16_t tmp_ = lwip_ntohs(f) + (u16_t)*cp++; \ 114195972f6Sopenharmony_ci (f) = lwip_htons(tmp_); \ 115195972f6Sopenharmony_ci } \ 116195972f6Sopenharmony_ci} 117195972f6Sopenharmony_ci 118195972f6Sopenharmony_ci#define DECODEU(f) { \ 119195972f6Sopenharmony_ci if (*cp == 0) {\ 120195972f6Sopenharmony_ci (f) = lwip_htons(((u16_t)cp[1] << 8) | cp[2]); \ 121195972f6Sopenharmony_ci cp += 3; \ 122195972f6Sopenharmony_ci } else { \ 123195972f6Sopenharmony_ci (f) = lwip_htons((u16_t)*cp++); \ 124195972f6Sopenharmony_ci } \ 125195972f6Sopenharmony_ci} 126195972f6Sopenharmony_ci 127195972f6Sopenharmony_ci/* Helper structures for unaligned *u32_t and *u16_t accesses */ 128195972f6Sopenharmony_ci#ifdef PACK_STRUCT_USE_INCLUDES 129195972f6Sopenharmony_ci# include "arch/bpstruct.h" 130195972f6Sopenharmony_ci#endif 131195972f6Sopenharmony_ciPACK_STRUCT_BEGIN 132195972f6Sopenharmony_cistruct vj_u32_t { 133195972f6Sopenharmony_ci PACK_STRUCT_FIELD(u32_t v); 134195972f6Sopenharmony_ci} PACK_STRUCT_STRUCT; 135195972f6Sopenharmony_ciPACK_STRUCT_END 136195972f6Sopenharmony_ci#ifdef PACK_STRUCT_USE_INCLUDES 137195972f6Sopenharmony_ci# include "arch/epstruct.h" 138195972f6Sopenharmony_ci#endif 139195972f6Sopenharmony_ci 140195972f6Sopenharmony_ci#ifdef PACK_STRUCT_USE_INCLUDES 141195972f6Sopenharmony_ci# include "arch/bpstruct.h" 142195972f6Sopenharmony_ci#endif 143195972f6Sopenharmony_ciPACK_STRUCT_BEGIN 144195972f6Sopenharmony_cistruct vj_u16_t { 145195972f6Sopenharmony_ci PACK_STRUCT_FIELD(u16_t v); 146195972f6Sopenharmony_ci} PACK_STRUCT_STRUCT; 147195972f6Sopenharmony_ciPACK_STRUCT_END 148195972f6Sopenharmony_ci#ifdef PACK_STRUCT_USE_INCLUDES 149195972f6Sopenharmony_ci# include "arch/epstruct.h" 150195972f6Sopenharmony_ci#endif 151195972f6Sopenharmony_ci 152195972f6Sopenharmony_ci/* 153195972f6Sopenharmony_ci * vj_compress_tcp - Attempt to do Van Jacobson header compression on a 154195972f6Sopenharmony_ci * packet. This assumes that nb and comp are not null and that the first 155195972f6Sopenharmony_ci * buffer of the chain contains a valid IP header. 156195972f6Sopenharmony_ci * Return the VJ type code indicating whether or not the packet was 157195972f6Sopenharmony_ci * compressed. 158195972f6Sopenharmony_ci */ 159195972f6Sopenharmony_ciu8_t 160195972f6Sopenharmony_civj_compress_tcp(struct vjcompress *comp, struct pbuf **pb) 161195972f6Sopenharmony_ci{ 162195972f6Sopenharmony_ci struct pbuf *np = *pb; 163195972f6Sopenharmony_ci struct ip_hdr *ip = (struct ip_hdr *)np->payload; 164195972f6Sopenharmony_ci struct cstate *cs = comp->last_cs->cs_next; 165195972f6Sopenharmony_ci u16_t ilen = IPH_HL(ip); 166195972f6Sopenharmony_ci u16_t hlen; 167195972f6Sopenharmony_ci struct tcp_hdr *oth; 168195972f6Sopenharmony_ci struct tcp_hdr *th; 169195972f6Sopenharmony_ci u16_t deltaS, deltaA = 0; 170195972f6Sopenharmony_ci u32_t deltaL; 171195972f6Sopenharmony_ci u32_t changes = 0; 172195972f6Sopenharmony_ci u8_t new_seq[16]; 173195972f6Sopenharmony_ci u8_t *cp = new_seq; 174195972f6Sopenharmony_ci 175195972f6Sopenharmony_ci /* 176195972f6Sopenharmony_ci * Check that the packet is IP proto TCP. 177195972f6Sopenharmony_ci */ 178195972f6Sopenharmony_ci if (IPH_PROTO(ip) != IP_PROTO_TCP) { 179195972f6Sopenharmony_ci return (TYPE_IP); 180195972f6Sopenharmony_ci } 181195972f6Sopenharmony_ci 182195972f6Sopenharmony_ci /* 183195972f6Sopenharmony_ci * Bail if this is an IP fragment or if the TCP packet isn't 184195972f6Sopenharmony_ci * `compressible' (i.e., ACK isn't set or some other control bit is 185195972f6Sopenharmony_ci * set). 186195972f6Sopenharmony_ci */ 187195972f6Sopenharmony_ci if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || np->tot_len < 40) { 188195972f6Sopenharmony_ci return (TYPE_IP); 189195972f6Sopenharmony_ci } 190195972f6Sopenharmony_ci th = (struct tcp_hdr *)&((struct vj_u32_t*)ip)[ilen]; 191195972f6Sopenharmony_ci if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { 192195972f6Sopenharmony_ci return (TYPE_IP); 193195972f6Sopenharmony_ci } 194195972f6Sopenharmony_ci 195195972f6Sopenharmony_ci /* Check that the TCP/IP headers are contained in the first buffer. */ 196195972f6Sopenharmony_ci hlen = ilen + TCPH_HDRLEN(th); 197195972f6Sopenharmony_ci hlen <<= 2; 198195972f6Sopenharmony_ci if (np->len < hlen) { 199195972f6Sopenharmony_ci PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen)); 200195972f6Sopenharmony_ci return (TYPE_IP); 201195972f6Sopenharmony_ci } 202195972f6Sopenharmony_ci 203195972f6Sopenharmony_ci /* TCP stack requires that we don't change the packet payload, therefore we copy 204195972f6Sopenharmony_ci * the whole packet before compression. */ 205195972f6Sopenharmony_ci np = pbuf_clone(PBUF_RAW, PBUF_RAM, *pb); 206195972f6Sopenharmony_ci if (!np) { 207195972f6Sopenharmony_ci return (TYPE_IP); 208195972f6Sopenharmony_ci } 209195972f6Sopenharmony_ci 210195972f6Sopenharmony_ci *pb = np; 211195972f6Sopenharmony_ci ip = (struct ip_hdr *)np->payload; 212195972f6Sopenharmony_ci 213195972f6Sopenharmony_ci /* 214195972f6Sopenharmony_ci * Packet is compressible -- we're going to send either a 215195972f6Sopenharmony_ci * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need 216195972f6Sopenharmony_ci * to locate (or create) the connection state. Special case the 217195972f6Sopenharmony_ci * most recently used connection since it's most likely to be used 218195972f6Sopenharmony_ci * again & we don't have to do any reordering if it's used. 219195972f6Sopenharmony_ci */ 220195972f6Sopenharmony_ci INCR(vjs_packets); 221195972f6Sopenharmony_ci if (!ip4_addr_cmp(&ip->src, &cs->cs_ip.src) 222195972f6Sopenharmony_ci || !ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest) 223195972f6Sopenharmony_ci || (*(struct vj_u32_t*)th).v != (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) { 224195972f6Sopenharmony_ci /* 225195972f6Sopenharmony_ci * Wasn't the first -- search for it. 226195972f6Sopenharmony_ci * 227195972f6Sopenharmony_ci * States are kept in a circularly linked list with 228195972f6Sopenharmony_ci * last_cs pointing to the end of the list. The 229195972f6Sopenharmony_ci * list is kept in lru order by moving a state to the 230195972f6Sopenharmony_ci * head of the list whenever it is referenced. Since 231195972f6Sopenharmony_ci * the list is short and, empirically, the connection 232195972f6Sopenharmony_ci * we want is almost always near the front, we locate 233195972f6Sopenharmony_ci * states via linear search. If we don't find a state 234195972f6Sopenharmony_ci * for the datagram, the oldest state is (re-)used. 235195972f6Sopenharmony_ci */ 236195972f6Sopenharmony_ci struct cstate *lcs; 237195972f6Sopenharmony_ci struct cstate *lastcs = comp->last_cs; 238195972f6Sopenharmony_ci 239195972f6Sopenharmony_ci do { 240195972f6Sopenharmony_ci lcs = cs; cs = cs->cs_next; 241195972f6Sopenharmony_ci INCR(vjs_searches); 242195972f6Sopenharmony_ci if (ip4_addr_cmp(&ip->src, &cs->cs_ip.src) 243195972f6Sopenharmony_ci && ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest) 244195972f6Sopenharmony_ci && (*(struct vj_u32_t*)th).v == (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) { 245195972f6Sopenharmony_ci goto found; 246195972f6Sopenharmony_ci } 247195972f6Sopenharmony_ci } while (cs != lastcs); 248195972f6Sopenharmony_ci 249195972f6Sopenharmony_ci /* 250195972f6Sopenharmony_ci * Didn't find it -- re-use oldest cstate. Send an 251195972f6Sopenharmony_ci * uncompressed packet that tells the other side what 252195972f6Sopenharmony_ci * connection number we're using for this conversation. 253195972f6Sopenharmony_ci * Note that since the state list is circular, the oldest 254195972f6Sopenharmony_ci * state points to the newest and we only need to set 255195972f6Sopenharmony_ci * last_cs to update the lru linkage. 256195972f6Sopenharmony_ci */ 257195972f6Sopenharmony_ci INCR(vjs_misses); 258195972f6Sopenharmony_ci comp->last_cs = lcs; 259195972f6Sopenharmony_ci goto uncompressed; 260195972f6Sopenharmony_ci 261195972f6Sopenharmony_ci found: 262195972f6Sopenharmony_ci /* 263195972f6Sopenharmony_ci * Found it -- move to the front on the connection list. 264195972f6Sopenharmony_ci */ 265195972f6Sopenharmony_ci if (cs == lastcs) { 266195972f6Sopenharmony_ci comp->last_cs = lcs; 267195972f6Sopenharmony_ci } else { 268195972f6Sopenharmony_ci lcs->cs_next = cs->cs_next; 269195972f6Sopenharmony_ci cs->cs_next = lastcs->cs_next; 270195972f6Sopenharmony_ci lastcs->cs_next = cs; 271195972f6Sopenharmony_ci } 272195972f6Sopenharmony_ci } 273195972f6Sopenharmony_ci 274195972f6Sopenharmony_ci oth = (struct tcp_hdr *)&((struct vj_u32_t*)&cs->cs_ip)[ilen]; 275195972f6Sopenharmony_ci deltaS = ilen; 276195972f6Sopenharmony_ci 277195972f6Sopenharmony_ci /* 278195972f6Sopenharmony_ci * Make sure that only what we expect to change changed. The first 279195972f6Sopenharmony_ci * line of the `if' checks the IP protocol version, header length & 280195972f6Sopenharmony_ci * type of service. The 2nd line checks the "Don't fragment" bit. 281195972f6Sopenharmony_ci * The 3rd line checks the time-to-live and protocol (the protocol 282195972f6Sopenharmony_ci * check is unnecessary but costless). The 4th line checks the TCP 283195972f6Sopenharmony_ci * header length. The 5th line checks IP options, if any. The 6th 284195972f6Sopenharmony_ci * line checks TCP options, if any. If any of these things are 285195972f6Sopenharmony_ci * different between the previous & current datagram, we send the 286195972f6Sopenharmony_ci * current datagram `uncompressed'. 287195972f6Sopenharmony_ci */ 288195972f6Sopenharmony_ci if ((((struct vj_u16_t*)ip)[0]).v != (((struct vj_u16_t*)&cs->cs_ip)[0]).v 289195972f6Sopenharmony_ci || (((struct vj_u16_t*)ip)[3]).v != (((struct vj_u16_t*)&cs->cs_ip)[3]).v 290195972f6Sopenharmony_ci || (((struct vj_u16_t*)ip)[4]).v != (((struct vj_u16_t*)&cs->cs_ip)[4]).v 291195972f6Sopenharmony_ci || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth) 292195972f6Sopenharmony_ci || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) 293195972f6Sopenharmony_ci || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) { 294195972f6Sopenharmony_ci goto uncompressed; 295195972f6Sopenharmony_ci } 296195972f6Sopenharmony_ci 297195972f6Sopenharmony_ci /* 298195972f6Sopenharmony_ci * Figure out which of the changing fields changed. The 299195972f6Sopenharmony_ci * receiver expects changes in the order: urgent, window, 300195972f6Sopenharmony_ci * ack, seq (the order minimizes the number of temporaries 301195972f6Sopenharmony_ci * needed in this section of code). 302195972f6Sopenharmony_ci */ 303195972f6Sopenharmony_ci if (TCPH_FLAGS(th) & TCP_URG) { 304195972f6Sopenharmony_ci deltaS = lwip_ntohs(th->urgp); 305195972f6Sopenharmony_ci ENCODEZ(deltaS); 306195972f6Sopenharmony_ci changes |= NEW_U; 307195972f6Sopenharmony_ci } else if (th->urgp != oth->urgp) { 308195972f6Sopenharmony_ci /* argh! URG not set but urp changed -- a sensible 309195972f6Sopenharmony_ci * implementation should never do this but RFC793 310195972f6Sopenharmony_ci * doesn't prohibit the change so we have to deal 311195972f6Sopenharmony_ci * with it. */ 312195972f6Sopenharmony_ci goto uncompressed; 313195972f6Sopenharmony_ci } 314195972f6Sopenharmony_ci 315195972f6Sopenharmony_ci if ((deltaS = (u16_t)(lwip_ntohs(th->wnd) - lwip_ntohs(oth->wnd))) != 0) { 316195972f6Sopenharmony_ci ENCODE(deltaS); 317195972f6Sopenharmony_ci changes |= NEW_W; 318195972f6Sopenharmony_ci } 319195972f6Sopenharmony_ci 320195972f6Sopenharmony_ci if ((deltaL = lwip_ntohl(th->ackno) - lwip_ntohl(oth->ackno)) != 0) { 321195972f6Sopenharmony_ci if (deltaL > 0xffff) { 322195972f6Sopenharmony_ci goto uncompressed; 323195972f6Sopenharmony_ci } 324195972f6Sopenharmony_ci deltaA = (u16_t)deltaL; 325195972f6Sopenharmony_ci ENCODE(deltaA); 326195972f6Sopenharmony_ci changes |= NEW_A; 327195972f6Sopenharmony_ci } 328195972f6Sopenharmony_ci 329195972f6Sopenharmony_ci if ((deltaL = lwip_ntohl(th->seqno) - lwip_ntohl(oth->seqno)) != 0) { 330195972f6Sopenharmony_ci if (deltaL > 0xffff) { 331195972f6Sopenharmony_ci goto uncompressed; 332195972f6Sopenharmony_ci } 333195972f6Sopenharmony_ci deltaS = (u16_t)deltaL; 334195972f6Sopenharmony_ci ENCODE(deltaS); 335195972f6Sopenharmony_ci changes |= NEW_S; 336195972f6Sopenharmony_ci } 337195972f6Sopenharmony_ci 338195972f6Sopenharmony_ci switch(changes) { 339195972f6Sopenharmony_ci case 0: 340195972f6Sopenharmony_ci /* 341195972f6Sopenharmony_ci * Nothing changed. If this packet contains data and the 342195972f6Sopenharmony_ci * last one didn't, this is probably a data packet following 343195972f6Sopenharmony_ci * an ack (normal on an interactive connection) and we send 344195972f6Sopenharmony_ci * it compressed. Otherwise it's probably a retransmit, 345195972f6Sopenharmony_ci * retransmitted ack or window probe. Send it uncompressed 346195972f6Sopenharmony_ci * in case the other side missed the compressed version. 347195972f6Sopenharmony_ci */ 348195972f6Sopenharmony_ci if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) && 349195972f6Sopenharmony_ci lwip_ntohs(IPH_LEN(&cs->cs_ip)) == hlen) { 350195972f6Sopenharmony_ci break; 351195972f6Sopenharmony_ci } 352195972f6Sopenharmony_ci /* no break */ 353195972f6Sopenharmony_ci /* fall through */ 354195972f6Sopenharmony_ci 355195972f6Sopenharmony_ci case SPECIAL_I: 356195972f6Sopenharmony_ci case SPECIAL_D: 357195972f6Sopenharmony_ci /* 358195972f6Sopenharmony_ci * actual changes match one of our special case encodings -- 359195972f6Sopenharmony_ci * send packet uncompressed. 360195972f6Sopenharmony_ci */ 361195972f6Sopenharmony_ci goto uncompressed; 362195972f6Sopenharmony_ci 363195972f6Sopenharmony_ci case NEW_S|NEW_A: 364195972f6Sopenharmony_ci if (deltaS == deltaA && deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { 365195972f6Sopenharmony_ci /* special case for echoed terminal traffic */ 366195972f6Sopenharmony_ci changes = SPECIAL_I; 367195972f6Sopenharmony_ci cp = new_seq; 368195972f6Sopenharmony_ci } 369195972f6Sopenharmony_ci break; 370195972f6Sopenharmony_ci 371195972f6Sopenharmony_ci case NEW_S: 372195972f6Sopenharmony_ci if (deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { 373195972f6Sopenharmony_ci /* special case for data xfer */ 374195972f6Sopenharmony_ci changes = SPECIAL_D; 375195972f6Sopenharmony_ci cp = new_seq; 376195972f6Sopenharmony_ci } 377195972f6Sopenharmony_ci break; 378195972f6Sopenharmony_ci default: 379195972f6Sopenharmony_ci break; 380195972f6Sopenharmony_ci } 381195972f6Sopenharmony_ci 382195972f6Sopenharmony_ci deltaS = (u16_t)(lwip_ntohs(IPH_ID(ip)) - lwip_ntohs(IPH_ID(&cs->cs_ip))); 383195972f6Sopenharmony_ci if (deltaS != 1) { 384195972f6Sopenharmony_ci ENCODEZ(deltaS); 385195972f6Sopenharmony_ci changes |= NEW_I; 386195972f6Sopenharmony_ci } 387195972f6Sopenharmony_ci if (TCPH_FLAGS(th) & TCP_PSH) { 388195972f6Sopenharmony_ci changes |= TCP_PUSH_BIT; 389195972f6Sopenharmony_ci } 390195972f6Sopenharmony_ci /* 391195972f6Sopenharmony_ci * Grab the cksum before we overwrite it below. Then update our 392195972f6Sopenharmony_ci * state with this packet's header. 393195972f6Sopenharmony_ci */ 394195972f6Sopenharmony_ci deltaA = lwip_ntohs(th->chksum); 395195972f6Sopenharmony_ci MEMCPY(&cs->cs_ip, ip, hlen); 396195972f6Sopenharmony_ci 397195972f6Sopenharmony_ci /* 398195972f6Sopenharmony_ci * We want to use the original packet as our compressed packet. 399195972f6Sopenharmony_ci * (cp - new_seq) is the number of bytes we need for compressed 400195972f6Sopenharmony_ci * sequence numbers. In addition we need one byte for the change 401195972f6Sopenharmony_ci * mask, one for the connection id and two for the tcp checksum. 402195972f6Sopenharmony_ci * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how 403195972f6Sopenharmony_ci * many bytes of the original packet to toss so subtract the two to 404195972f6Sopenharmony_ci * get the new packet size. 405195972f6Sopenharmony_ci */ 406195972f6Sopenharmony_ci deltaS = (u16_t)(cp - new_seq); 407195972f6Sopenharmony_ci if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { 408195972f6Sopenharmony_ci comp->last_xmit = cs->cs_id; 409195972f6Sopenharmony_ci hlen -= deltaS + 4; 410195972f6Sopenharmony_ci if (pbuf_remove_header(np, hlen)){ 411195972f6Sopenharmony_ci /* Can we cope with this failing? Just assert for now */ 412195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_remove_header failed\n", 0); 413195972f6Sopenharmony_ci } 414195972f6Sopenharmony_ci cp = (u8_t*)np->payload; 415195972f6Sopenharmony_ci *cp++ = (u8_t)(changes | NEW_C); 416195972f6Sopenharmony_ci *cp++ = cs->cs_id; 417195972f6Sopenharmony_ci } else { 418195972f6Sopenharmony_ci hlen -= deltaS + 3; 419195972f6Sopenharmony_ci if (pbuf_remove_header(np, hlen)) { 420195972f6Sopenharmony_ci /* Can we cope with this failing? Just assert for now */ 421195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_remove_header failed\n", 0); 422195972f6Sopenharmony_ci } 423195972f6Sopenharmony_ci cp = (u8_t*)np->payload; 424195972f6Sopenharmony_ci *cp++ = (u8_t)changes; 425195972f6Sopenharmony_ci } 426195972f6Sopenharmony_ci *cp++ = (u8_t)(deltaA >> 8); 427195972f6Sopenharmony_ci *cp++ = (u8_t)deltaA; 428195972f6Sopenharmony_ci MEMCPY(cp, new_seq, deltaS); 429195972f6Sopenharmony_ci INCR(vjs_compressed); 430195972f6Sopenharmony_ci return (TYPE_COMPRESSED_TCP); 431195972f6Sopenharmony_ci 432195972f6Sopenharmony_ci /* 433195972f6Sopenharmony_ci * Update connection state cs & send uncompressed packet (that is, 434195972f6Sopenharmony_ci * a regular ip/tcp packet but with the 'conversation id' we hope 435195972f6Sopenharmony_ci * to use on future compressed packets in the protocol field). 436195972f6Sopenharmony_ci */ 437195972f6Sopenharmony_ciuncompressed: 438195972f6Sopenharmony_ci MEMCPY(&cs->cs_ip, ip, hlen); 439195972f6Sopenharmony_ci IPH_PROTO_SET(ip, cs->cs_id); 440195972f6Sopenharmony_ci comp->last_xmit = cs->cs_id; 441195972f6Sopenharmony_ci return (TYPE_UNCOMPRESSED_TCP); 442195972f6Sopenharmony_ci} 443195972f6Sopenharmony_ci 444195972f6Sopenharmony_ci/* 445195972f6Sopenharmony_ci * Called when we may have missed a packet. 446195972f6Sopenharmony_ci */ 447195972f6Sopenharmony_civoid 448195972f6Sopenharmony_civj_uncompress_err(struct vjcompress *comp) 449195972f6Sopenharmony_ci{ 450195972f6Sopenharmony_ci comp->flags |= VJF_TOSS; 451195972f6Sopenharmony_ci INCR(vjs_errorin); 452195972f6Sopenharmony_ci} 453195972f6Sopenharmony_ci 454195972f6Sopenharmony_ci/* 455195972f6Sopenharmony_ci * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. 456195972f6Sopenharmony_ci * Return 0 on success, -1 on failure. 457195972f6Sopenharmony_ci */ 458195972f6Sopenharmony_ciint 459195972f6Sopenharmony_civj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) 460195972f6Sopenharmony_ci{ 461195972f6Sopenharmony_ci u32_t hlen; 462195972f6Sopenharmony_ci struct cstate *cs; 463195972f6Sopenharmony_ci struct ip_hdr *ip; 464195972f6Sopenharmony_ci 465195972f6Sopenharmony_ci ip = (struct ip_hdr *)nb->payload; 466195972f6Sopenharmony_ci hlen = IPH_HL(ip) << 2; 467195972f6Sopenharmony_ci if (IPH_PROTO(ip) >= MAX_SLOTS 468195972f6Sopenharmony_ci || hlen + sizeof(struct tcp_hdr) > nb->len 469195972f6Sopenharmony_ci || (hlen += TCPH_HDRLEN_BYTES((struct tcp_hdr *)&((char *)ip)[hlen])) 470195972f6Sopenharmony_ci > nb->len 471195972f6Sopenharmony_ci || hlen > MAX_HDR) { 472195972f6Sopenharmony_ci PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", 473195972f6Sopenharmony_ci IPH_PROTO(ip), hlen, nb->len)); 474195972f6Sopenharmony_ci vj_uncompress_err(comp); 475195972f6Sopenharmony_ci return -1; 476195972f6Sopenharmony_ci } 477195972f6Sopenharmony_ci cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)]; 478195972f6Sopenharmony_ci comp->flags &=~ VJF_TOSS; 479195972f6Sopenharmony_ci IPH_PROTO_SET(ip, IP_PROTO_TCP); 480195972f6Sopenharmony_ci /* copy from/to bigger buffers checked above instead of cs->cs_ip and ip 481195972f6Sopenharmony_ci just to help static code analysis to see this is correct ;-) */ 482195972f6Sopenharmony_ci MEMCPY(&cs->cs_hdr, nb->payload, hlen); 483195972f6Sopenharmony_ci cs->cs_hlen = (u16_t)hlen; 484195972f6Sopenharmony_ci INCR(vjs_uncompressedin); 485195972f6Sopenharmony_ci return 0; 486195972f6Sopenharmony_ci} 487195972f6Sopenharmony_ci 488195972f6Sopenharmony_ci/* 489195972f6Sopenharmony_ci * Uncompress a packet of type TYPE_COMPRESSED_TCP. 490195972f6Sopenharmony_ci * The packet is composed of a buffer chain and the first buffer 491195972f6Sopenharmony_ci * must contain an accurate chain length. 492195972f6Sopenharmony_ci * The first buffer must include the entire compressed TCP/IP header. 493195972f6Sopenharmony_ci * This procedure replaces the compressed header with the uncompressed 494195972f6Sopenharmony_ci * header and returns the length of the VJ header. 495195972f6Sopenharmony_ci */ 496195972f6Sopenharmony_ciint 497195972f6Sopenharmony_civj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) 498195972f6Sopenharmony_ci{ 499195972f6Sopenharmony_ci u8_t *cp; 500195972f6Sopenharmony_ci struct tcp_hdr *th; 501195972f6Sopenharmony_ci struct cstate *cs; 502195972f6Sopenharmony_ci struct vj_u16_t *bp; 503195972f6Sopenharmony_ci struct pbuf *n0 = *nb; 504195972f6Sopenharmony_ci u32_t tmp; 505195972f6Sopenharmony_ci u32_t vjlen, hlen, changes; 506195972f6Sopenharmony_ci 507195972f6Sopenharmony_ci INCR(vjs_compressedin); 508195972f6Sopenharmony_ci cp = (u8_t*)n0->payload; 509195972f6Sopenharmony_ci changes = *cp++; 510195972f6Sopenharmony_ci if (changes & NEW_C) { 511195972f6Sopenharmony_ci /* 512195972f6Sopenharmony_ci * Make sure the state index is in range, then grab the state. 513195972f6Sopenharmony_ci * If we have a good state index, clear the 'discard' flag. 514195972f6Sopenharmony_ci */ 515195972f6Sopenharmony_ci if (*cp >= MAX_SLOTS) { 516195972f6Sopenharmony_ci PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp)); 517195972f6Sopenharmony_ci goto bad; 518195972f6Sopenharmony_ci } 519195972f6Sopenharmony_ci 520195972f6Sopenharmony_ci comp->flags &=~ VJF_TOSS; 521195972f6Sopenharmony_ci comp->last_recv = *cp++; 522195972f6Sopenharmony_ci } else { 523195972f6Sopenharmony_ci /* 524195972f6Sopenharmony_ci * this packet has an implicit state index. If we've 525195972f6Sopenharmony_ci * had a line error since the last time we got an 526195972f6Sopenharmony_ci * explicit state index, we have to toss the packet. 527195972f6Sopenharmony_ci */ 528195972f6Sopenharmony_ci if (comp->flags & VJF_TOSS) { 529195972f6Sopenharmony_ci PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n")); 530195972f6Sopenharmony_ci INCR(vjs_tossed); 531195972f6Sopenharmony_ci return (-1); 532195972f6Sopenharmony_ci } 533195972f6Sopenharmony_ci } 534195972f6Sopenharmony_ci cs = &comp->rstate[comp->last_recv]; 535195972f6Sopenharmony_ci hlen = IPH_HL(&cs->cs_ip) << 2; 536195972f6Sopenharmony_ci th = (struct tcp_hdr *)&((u8_t*)&cs->cs_ip)[hlen]; 537195972f6Sopenharmony_ci th->chksum = lwip_htons((*cp << 8) | cp[1]); 538195972f6Sopenharmony_ci cp += 2; 539195972f6Sopenharmony_ci if (changes & TCP_PUSH_BIT) { 540195972f6Sopenharmony_ci TCPH_SET_FLAG(th, TCP_PSH); 541195972f6Sopenharmony_ci } else { 542195972f6Sopenharmony_ci TCPH_UNSET_FLAG(th, TCP_PSH); 543195972f6Sopenharmony_ci } 544195972f6Sopenharmony_ci 545195972f6Sopenharmony_ci switch (changes & SPECIALS_MASK) { 546195972f6Sopenharmony_ci case SPECIAL_I: 547195972f6Sopenharmony_ci { 548195972f6Sopenharmony_ci u32_t i = lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; 549195972f6Sopenharmony_ci /* some compilers can't nest inline assembler.. */ 550195972f6Sopenharmony_ci tmp = lwip_ntohl(th->ackno) + i; 551195972f6Sopenharmony_ci th->ackno = lwip_htonl(tmp); 552195972f6Sopenharmony_ci tmp = lwip_ntohl(th->seqno) + i; 553195972f6Sopenharmony_ci th->seqno = lwip_htonl(tmp); 554195972f6Sopenharmony_ci } 555195972f6Sopenharmony_ci break; 556195972f6Sopenharmony_ci 557195972f6Sopenharmony_ci case SPECIAL_D: 558195972f6Sopenharmony_ci /* some compilers can't nest inline assembler.. */ 559195972f6Sopenharmony_ci tmp = lwip_ntohl(th->seqno) + lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; 560195972f6Sopenharmony_ci th->seqno = lwip_htonl(tmp); 561195972f6Sopenharmony_ci break; 562195972f6Sopenharmony_ci 563195972f6Sopenharmony_ci default: 564195972f6Sopenharmony_ci if (changes & NEW_U) { 565195972f6Sopenharmony_ci TCPH_SET_FLAG(th, TCP_URG); 566195972f6Sopenharmony_ci DECODEU(th->urgp); 567195972f6Sopenharmony_ci } else { 568195972f6Sopenharmony_ci TCPH_UNSET_FLAG(th, TCP_URG); 569195972f6Sopenharmony_ci } 570195972f6Sopenharmony_ci if (changes & NEW_W) { 571195972f6Sopenharmony_ci DECODES(th->wnd); 572195972f6Sopenharmony_ci } 573195972f6Sopenharmony_ci if (changes & NEW_A) { 574195972f6Sopenharmony_ci DECODEL(th->ackno); 575195972f6Sopenharmony_ci } 576195972f6Sopenharmony_ci if (changes & NEW_S) { 577195972f6Sopenharmony_ci DECODEL(th->seqno); 578195972f6Sopenharmony_ci } 579195972f6Sopenharmony_ci break; 580195972f6Sopenharmony_ci } 581195972f6Sopenharmony_ci if (changes & NEW_I) { 582195972f6Sopenharmony_ci DECODES(cs->cs_ip._id); 583195972f6Sopenharmony_ci } else { 584195972f6Sopenharmony_ci IPH_ID_SET(&cs->cs_ip, lwip_ntohs(IPH_ID(&cs->cs_ip)) + 1); 585195972f6Sopenharmony_ci IPH_ID_SET(&cs->cs_ip, lwip_htons(IPH_ID(&cs->cs_ip))); 586195972f6Sopenharmony_ci } 587195972f6Sopenharmony_ci 588195972f6Sopenharmony_ci /* 589195972f6Sopenharmony_ci * At this point, cp points to the first byte of data in the 590195972f6Sopenharmony_ci * packet. Fill in the IP total length and update the IP 591195972f6Sopenharmony_ci * header checksum. 592195972f6Sopenharmony_ci */ 593195972f6Sopenharmony_ci vjlen = (u16_t)(cp - (u8_t*)n0->payload); 594195972f6Sopenharmony_ci if (n0->len < vjlen) { 595195972f6Sopenharmony_ci /* 596195972f6Sopenharmony_ci * We must have dropped some characters (crc should detect 597195972f6Sopenharmony_ci * this but the old slip framing won't) 598195972f6Sopenharmony_ci */ 599195972f6Sopenharmony_ci PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n", 600195972f6Sopenharmony_ci n0->len, vjlen)); 601195972f6Sopenharmony_ci goto bad; 602195972f6Sopenharmony_ci } 603195972f6Sopenharmony_ci 604195972f6Sopenharmony_ci#if BYTE_ORDER == LITTLE_ENDIAN 605195972f6Sopenharmony_ci tmp = n0->tot_len - vjlen + cs->cs_hlen; 606195972f6Sopenharmony_ci IPH_LEN_SET(&cs->cs_ip, lwip_htons((u16_t)tmp)); 607195972f6Sopenharmony_ci#else 608195972f6Sopenharmony_ci IPH_LEN_SET(&cs->cs_ip, lwip_htons(n0->tot_len - vjlen + cs->cs_hlen)); 609195972f6Sopenharmony_ci#endif 610195972f6Sopenharmony_ci 611195972f6Sopenharmony_ci /* recompute the ip header checksum */ 612195972f6Sopenharmony_ci bp = (struct vj_u16_t*) &cs->cs_ip; 613195972f6Sopenharmony_ci IPH_CHKSUM_SET(&cs->cs_ip, 0); 614195972f6Sopenharmony_ci for (tmp = 0; hlen > 0; hlen -= 2) { 615195972f6Sopenharmony_ci tmp += (*bp++).v; 616195972f6Sopenharmony_ci } 617195972f6Sopenharmony_ci tmp = (tmp & 0xffff) + (tmp >> 16); 618195972f6Sopenharmony_ci tmp = (tmp & 0xffff) + (tmp >> 16); 619195972f6Sopenharmony_ci IPH_CHKSUM_SET(&cs->cs_ip, (u16_t)(~tmp)); 620195972f6Sopenharmony_ci 621195972f6Sopenharmony_ci /* Remove the compressed header and prepend the uncompressed header. */ 622195972f6Sopenharmony_ci if (pbuf_remove_header(n0, vjlen)) { 623195972f6Sopenharmony_ci /* Can we cope with this failing? Just assert for now */ 624195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_remove_header failed\n", 0); 625195972f6Sopenharmony_ci goto bad; 626195972f6Sopenharmony_ci } 627195972f6Sopenharmony_ci 628195972f6Sopenharmony_ci if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { 629195972f6Sopenharmony_ci struct pbuf *np; 630195972f6Sopenharmony_ci 631195972f6Sopenharmony_ci#if IP_FORWARD 632195972f6Sopenharmony_ci /* If IP forwarding is enabled we are using a PBUF_LINK packet type so 633195972f6Sopenharmony_ci * the packet is being allocated with enough header space to be 634195972f6Sopenharmony_ci * forwarded (to Ethernet for example). 635195972f6Sopenharmony_ci */ 636195972f6Sopenharmony_ci np = pbuf_alloc(PBUF_LINK, n0->len + cs->cs_hlen, PBUF_POOL); 637195972f6Sopenharmony_ci#else /* IP_FORWARD */ 638195972f6Sopenharmony_ci np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); 639195972f6Sopenharmony_ci#endif /* IP_FORWARD */ 640195972f6Sopenharmony_ci if(!np) { 641195972f6Sopenharmony_ci PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n")); 642195972f6Sopenharmony_ci goto bad; 643195972f6Sopenharmony_ci } 644195972f6Sopenharmony_ci 645195972f6Sopenharmony_ci if (pbuf_remove_header(np, cs->cs_hlen)) { 646195972f6Sopenharmony_ci /* Can we cope with this failing? Just assert for now */ 647195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_remove_header failed\n", 0); 648195972f6Sopenharmony_ci goto bad; 649195972f6Sopenharmony_ci } 650195972f6Sopenharmony_ci 651195972f6Sopenharmony_ci pbuf_take(np, n0->payload, n0->len); 652195972f6Sopenharmony_ci 653195972f6Sopenharmony_ci if(n0->next) { 654195972f6Sopenharmony_ci pbuf_chain(np, n0->next); 655195972f6Sopenharmony_ci pbuf_dechain(n0); 656195972f6Sopenharmony_ci } 657195972f6Sopenharmony_ci pbuf_free(n0); 658195972f6Sopenharmony_ci n0 = np; 659195972f6Sopenharmony_ci } 660195972f6Sopenharmony_ci 661195972f6Sopenharmony_ci if (pbuf_add_header(n0, cs->cs_hlen)) { 662195972f6Sopenharmony_ci struct pbuf *np; 663195972f6Sopenharmony_ci 664195972f6Sopenharmony_ci LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); 665195972f6Sopenharmony_ci np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); 666195972f6Sopenharmony_ci if(!np) { 667195972f6Sopenharmony_ci PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n")); 668195972f6Sopenharmony_ci goto bad; 669195972f6Sopenharmony_ci } 670195972f6Sopenharmony_ci pbuf_cat(np, n0); 671195972f6Sopenharmony_ci n0 = np; 672195972f6Sopenharmony_ci } 673195972f6Sopenharmony_ci LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); 674195972f6Sopenharmony_ci MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); 675195972f6Sopenharmony_ci 676195972f6Sopenharmony_ci *nb = n0; 677195972f6Sopenharmony_ci 678195972f6Sopenharmony_ci return vjlen; 679195972f6Sopenharmony_ci 680195972f6Sopenharmony_cibad: 681195972f6Sopenharmony_ci vj_uncompress_err(comp); 682195972f6Sopenharmony_ci return (-1); 683195972f6Sopenharmony_ci} 684195972f6Sopenharmony_ci 685195972f6Sopenharmony_ci#endif /* PPP_SUPPORT && VJ_SUPPORT */ 686