#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && VJ_SUPPORT
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/pppdebug.h"
#include "netif/ppp/vj.h"
#include <string.h>
#if LINK_STATS
#define INCR(counter) ++comp->stats.counter
#else
#define INCR(counter)
#endif
void
vj_compress_init(struct vjcompress *comp)
{
u8_t i;
struct cstate *tstate = comp->tstate;
#if MAX_SLOTS == 0
memset((char *)comp, 0, sizeof(*comp));
#endif
comp->maxSlotIndex = MAX_SLOTS - 1;
comp->compressSlot = 0;
for (i = MAX_SLOTS - 1; i > 0; --i) {
tstate[i].cs_id = i;
tstate[i].cs_next = &tstate[i - 1];
}
tstate[0].cs_next = &tstate[MAX_SLOTS - 1];
tstate[0].cs_id = 0;
comp->last_cs = &tstate[0];
comp->last_recv = 255;
comp->last_xmit = 255;
comp->flags = VJF_TOSS;
}
#define ENCODE(n) { \
if ((u16_t)(n) >= 256) { \
*cp++ = 0; \
cp[1] = (u8_t)(n); \
cp[0] = (u8_t)((n) >> 8); \
cp += 2; \
} else { \
*cp++ = (u8_t)(n); \
} \
}
#define ENCODEZ(n) { \
if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \
*cp++ = 0; \
cp[1] = (u8_t)(n); \
cp[0] = (u8_t)((n) >> 8); \
cp += 2; \
} else { \
*cp++ = (u8_t)(n); \
} \
}
#define DECODEL(f) { \
if (*cp == 0) {\
u32_t tmp_ = lwip_ntohl(f) + ((cp[1] << 8) | cp[2]); \
(f) = lwip_htonl(tmp_); \
cp += 3; \
} else { \
u32_t tmp_ = lwip_ntohl(f) + (u32_t)*cp++; \
(f) = lwip_htonl(tmp_); \
} \
}
#define DECODES(f) { \
if (*cp == 0) {\
u16_t tmp_ = lwip_ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \
(f) = lwip_htons(tmp_); \
cp += 3; \
} else { \
u16_t tmp_ = lwip_ntohs(f) + (u16_t)*cp++; \
(f) = lwip_htons(tmp_); \
} \
}
#define DECODEU(f) { \
if (*cp == 0) {\
(f) = lwip_htons(((u16_t)cp[1] << 8) | cp[2]); \
cp += 3; \
} else { \
(f) = lwip_htons((u16_t)*cp++); \
} \
}
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct vj_u32_t {
PACK_STRUCT_FIELD(u32_t v);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct vj_u16_t {
PACK_STRUCT_FIELD(u16_t v);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
u8_t
vj_compress_tcp(struct vjcompress *comp, struct pbuf **pb)
{
struct pbuf *np = *pb;
struct ip_hdr *ip = (struct ip_hdr *)np->payload;
struct cstate *cs = comp->last_cs->cs_next;
u16_t ilen = IPH_HL(ip);
u16_t hlen;
struct tcp_hdr *oth;
struct tcp_hdr *th;
u16_t deltaS, deltaA = 0;
u32_t deltaL;
u32_t changes = 0;
u8_t new_seq[16];
u8_t *cp = new_seq;
if (IPH_PROTO(ip) != IP_PROTO_TCP) {
return (TYPE_IP);
}
if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || np->tot_len < 40) {
return (TYPE_IP);
}
th = (struct tcp_hdr *)&((struct vj_u32_t*)ip)[ilen];
if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) {
return (TYPE_IP);
}
hlen = ilen + TCPH_HDRLEN(th);
hlen <<= 2;
if (np->len < hlen) {
PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen));
return (TYPE_IP);
}
np = pbuf_clone(PBUF_RAW, PBUF_RAM, *pb);
if (!np) {
return (TYPE_IP);
}
*pb = np;
ip = (struct ip_hdr *)np->payload;
INCR(vjs_packets);
if (!ip4_addr_eq(&ip->src, &cs->cs_ip.src)
|| !ip4_addr_eq(&ip->dest, &cs->cs_ip.dest)
|| (*(struct vj_u32_t*)th).v != (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
struct cstate *lcs;
struct cstate *lastcs = comp->last_cs;
do {
lcs = cs; cs = cs->cs_next;
INCR(vjs_searches);
if (ip4_addr_eq(&ip->src, &cs->cs_ip.src)
&& ip4_addr_eq(&ip->dest, &cs->cs_ip.dest)
&& (*(struct vj_u32_t*)th).v == (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
goto found;
}
} while (cs != lastcs);
INCR(vjs_misses);
comp->last_cs = lcs;
goto uncompressed;
found:
if (cs == lastcs) {
comp->last_cs = lcs;
} else {
lcs->cs_next = cs->cs_next;
cs->cs_next = lastcs->cs_next;
lastcs->cs_next = cs;
}
}
oth = (struct tcp_hdr *)&((struct vj_u32_t*)&cs->cs_ip)[ilen];
deltaS = ilen;
if ((((struct vj_u16_t*)ip)[0]).v != (((struct vj_u16_t*)&cs->cs_ip)[0]).v
|| (((struct vj_u16_t*)ip)[3]).v != (((struct vj_u16_t*)&cs->cs_ip)[3]).v
|| (((struct vj_u16_t*)ip)[4]).v != (((struct vj_u16_t*)&cs->cs_ip)[4]).v
|| TCPH_HDRLEN(th) != TCPH_HDRLEN(oth)
|| (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2))
|| (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) {
goto uncompressed;
}
if (TCPH_FLAGS(th) & TCP_URG) {
deltaS = lwip_ntohs(th->urgp);
ENCODEZ(deltaS);
changes |= NEW_U;
} else if (th->urgp != oth->urgp) {
goto uncompressed;
}
if ((deltaS = (u16_t)(lwip_ntohs(th->wnd) - lwip_ntohs(oth->wnd))) != 0) {
ENCODE(deltaS);
changes |= NEW_W;
}
if ((deltaL = lwip_ntohl(th->ackno) - lwip_ntohl(oth->ackno)) != 0) {
if (deltaL > 0xffff) {
goto uncompressed;
}
deltaA = (u16_t)deltaL;
ENCODE(deltaA);
changes |= NEW_A;
}
if ((deltaL = lwip_ntohl(th->seqno) - lwip_ntohl(oth->seqno)) != 0) {
if (deltaL > 0xffff) {
goto uncompressed;
}
deltaS = (u16_t)deltaL;
ENCODE(deltaS);
changes |= NEW_S;
}
switch(changes) {
case 0:
if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) &&
lwip_ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
break;
}
case SPECIAL_I:
case SPECIAL_D:
goto uncompressed;
case NEW_S|NEW_A:
if (deltaS == deltaA && deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
changes = SPECIAL_I;
cp = new_seq;
}
break;
case NEW_S:
if (deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
changes = SPECIAL_D;
cp = new_seq;
}
break;
default:
break;
}
deltaS = (u16_t)(lwip_ntohs(IPH_ID(ip)) - lwip_ntohs(IPH_ID(&cs->cs_ip)));
if (deltaS != 1) {
ENCODEZ(deltaS);
changes |= NEW_I;
}
if (TCPH_FLAGS(th) & TCP_PSH) {
changes |= TCP_PUSH_BIT;
}
deltaA = lwip_ntohs(th->chksum);
MEMCPY(&cs->cs_ip, ip, hlen);
deltaS = (u16_t)(cp - new_seq);
if (!comp->compressSlot || comp->last_xmit != cs->cs_id) {
comp->last_xmit = cs->cs_id;
hlen -= deltaS + 4;
if (pbuf_remove_header(np, hlen)){
LWIP_ASSERT("pbuf_remove_header failed", 0);
}
cp = (u8_t*)np->payload;
*cp++ = (u8_t)(changes | NEW_C);
*cp++ = cs->cs_id;
} else {
hlen -= deltaS + 3;
if (pbuf_remove_header(np, hlen)) {
LWIP_ASSERT("pbuf_remove_header failed", 0);
}
cp = (u8_t*)np->payload;
*cp++ = (u8_t)changes;
}
*cp++ = (u8_t)(deltaA >> 8);
*cp++ = (u8_t)deltaA;
MEMCPY(cp, new_seq, deltaS);
INCR(vjs_compressed);
return (TYPE_COMPRESSED_TCP);
uncompressed:
MEMCPY(&cs->cs_ip, ip, hlen);
IPH_PROTO_SET(ip, cs->cs_id);
comp->last_xmit = cs->cs_id;
return (TYPE_UNCOMPRESSED_TCP);
}
void
vj_uncompress_err(struct vjcompress *comp)
{
comp->flags |= VJF_TOSS;
INCR(vjs_errorin);
}
int
vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp)
{
u32_t hlen;
struct cstate *cs;
struct ip_hdr *ip;
ip = (struct ip_hdr *)nb->payload;
hlen = IPH_HL(ip) << 2;
if (IPH_PROTO(ip) >= MAX_SLOTS
|| hlen + sizeof(struct tcp_hdr) > nb->len
|| (hlen += TCPH_HDRLEN_BYTES((struct tcp_hdr *)&((char *)ip)[hlen]))
> nb->len
|| hlen > MAX_HDR) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
IPH_PROTO(ip), hlen, nb->len));
vj_uncompress_err(comp);
return -1;
}
cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)];
comp->flags &=~ VJF_TOSS;
IPH_PROTO_SET(ip, IP_PROTO_TCP);
MEMCPY(&cs->cs_hdr, nb->payload, hlen);
cs->cs_hlen = (u16_t)hlen;
INCR(vjs_uncompressedin);
return 0;
}
int
vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
{
u8_t *cp;
struct tcp_hdr *th;
struct cstate *cs;
struct vj_u16_t *bp;
struct pbuf *n0 = *nb;
u32_t tmp;
u32_t vjlen, hlen, changes;
INCR(vjs_compressedin);
cp = (u8_t*)n0->payload;
changes = *cp++;
if (changes & NEW_C) {
if (*cp >= MAX_SLOTS) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp));
goto bad;
}
comp->flags &=~ VJF_TOSS;
comp->last_recv = *cp++;
} else {
if (comp->flags & VJF_TOSS) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n"));
INCR(vjs_tossed);
return (-1);
}
}
cs = &comp->rstate[comp->last_recv];
hlen = IPH_HL(&cs->cs_ip) << 2;
th = (struct tcp_hdr *)&((u8_t*)&cs->cs_ip)[hlen];
th->chksum = lwip_htons((*cp << 8) | cp[1]);
cp += 2;
if (changes & TCP_PUSH_BIT) {
TCPH_SET_FLAG(th, TCP_PSH);
} else {
TCPH_UNSET_FLAG(th, TCP_PSH);
}
switch (changes & SPECIALS_MASK) {
case SPECIAL_I:
{
u32_t i = lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
tmp = lwip_ntohl(th->ackno) + i;
th->ackno = lwip_htonl(tmp);
tmp = lwip_ntohl(th->seqno) + i;
th->seqno = lwip_htonl(tmp);
}
break;
case SPECIAL_D:
tmp = lwip_ntohl(th->seqno) + lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
th->seqno = lwip_htonl(tmp);
break;
default:
if (changes & NEW_U) {
TCPH_SET_FLAG(th, TCP_URG);
DECODEU(th->urgp);
} else {
TCPH_UNSET_FLAG(th, TCP_URG);
}
if (changes & NEW_W) {
DECODES(th->wnd);
}
if (changes & NEW_A) {
DECODEL(th->ackno);
}
if (changes & NEW_S) {
DECODEL(th->seqno);
}
break;
}
if (changes & NEW_I) {
DECODES(cs->cs_ip._id);
} else {
IPH_ID_SET(&cs->cs_ip, lwip_ntohs(IPH_ID(&cs->cs_ip)) + 1);
IPH_ID_SET(&cs->cs_ip, lwip_htons(IPH_ID(&cs->cs_ip)));
}
vjlen = (u16_t)(cp - (u8_t*)n0->payload);
if (n0->len < vjlen) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n",
n0->len, vjlen));
goto bad;
}
#if BYTE_ORDER == LITTLE_ENDIAN
tmp = n0->tot_len - vjlen + cs->cs_hlen;
IPH_LEN_SET(&cs->cs_ip, lwip_htons((u16_t)tmp));
#else
IPH_LEN_SET(&cs->cs_ip, lwip_htons(n0->tot_len - vjlen + cs->cs_hlen));
#endif
bp = (struct vj_u16_t*) &cs->cs_ip;
IPH_CHKSUM_SET(&cs->cs_ip, 0);
for (tmp = 0; hlen > 0; hlen -= 2) {
tmp += (*bp++).v;
}
tmp = (tmp & 0xffff) + (tmp >> 16);
tmp = (tmp & 0xffff) + (tmp >> 16);
IPH_CHKSUM_SET(&cs->cs_ip, (u16_t)(~tmp));
if (pbuf_remove_header(n0, vjlen)) {
LWIP_ASSERT("pbuf_remove_header failed", 0);
goto bad;
}
if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) {
struct pbuf *np;
#if IP_FORWARD
np = pbuf_alloc(PBUF_LINK, n0->len + cs->cs_hlen, PBUF_POOL);
#else
np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL);
#endif
if(!np) {
PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n"));
goto bad;
}
if (pbuf_remove_header(np, cs->cs_hlen)) {
LWIP_ASSERT("pbuf_remove_header failed", 0);
goto bad;
}
pbuf_take(np, n0->payload, n0->len);
if(n0->next) {
pbuf_chain(np, n0->next);
pbuf_dechain(n0);
}
pbuf_free(n0);
n0 = np;
}
if (pbuf_add_header(n0, cs->cs_hlen)) {
struct pbuf *np;
LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE);
np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL);
if(!np) {
PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n"));
goto bad;
}
pbuf_cat(np, n0);
n0 = np;
}
LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen);
MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen);
*nb = n0;
return vjlen;
bad:
vj_uncompress_err(comp);
return (-1);
}
#endif