summaryrefslogtreecommitdiffstats
path: root/net/ax25/ax25_vj.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ax25/ax25_vj.c')
-rw-r--r--net/ax25/ax25_vj.c724
1 files changed, 724 insertions, 0 deletions
diff --git a/net/ax25/ax25_vj.c b/net/ax25/ax25_vj.c
new file mode 100644
index 000000000..4fa2dc1ff
--- /dev/null
+++ b/net/ax25/ax25_vj.c
@@ -0,0 +1,724 @@
+/*
+ * ax25_vj.c: VJ compression routines for VJ-compressed IP via NEW-AX.25
+ *
+ * Authors: Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Routines to compress TCP/IP packets according to RFC 1144 and to
+ * suppress redundant retransmissions on a reliable virtual circuit
+ * Largely based on code by Van Jacobson, Phil Karn et.al. Directly
+ * derived from WAMPES' slhc.c written by Dieter Deyke, DK5SG.
+ *
+ * Changelog:
+ * 1998-02-25 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * Adapted to Linux. contains code from drivers/net/slhc.c
+ *
+ * 1998-03-04 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * fixed problem with nonatomically calling kmalloc from interrupt
+ *
+ * 1998-03-08 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * fixed problem in axhc_recv_vjc() that lead to a system panic when
+ * the incoming sk_buff didn't contain enough headroom to rebuild the
+ * TCP/IP header after decompression
+ *
+ * License: This module is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_INET)
+#include <linux/skbuff.h>
+#include <asm/unaligned.h>
+#include <linux/kernel.h>
+
+#include "ax25_vj.h"
+
+#ifdef DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x)
+#endif
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#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)
+
+#define TCP_PUSH_BIT 0x10
+
+static int axhc_toss(struct axvj_slcomp *);
+
+/* Put a short in host order into a char array in network order */
+static inline unsigned char *
+put16(unsigned char *cp, unsigned short x)
+{
+ *cp++ = x >> 8;
+ *cp++ = x;
+
+ return cp;
+}
+
+/* Pull a 16-bit integer in host order from buffer in network byte order */
+static unsigned short pull16(unsigned char **cpp)
+{
+ short rval;
+
+ rval = *(*cpp)++;
+ rval <<= 8;
+ rval |= *(*cpp)++;
+ return rval;
+}
+
+/* Encode a number */
+static unsigned char *
+encode(unsigned char *cp, unsigned short n)
+{
+ if (n >= 256 || n == 0) {
+ *cp++ = 0;
+ cp = put16(cp, n);
+ } else {
+ *cp++ = n;
+ }
+ return cp;
+}
+
+/* Decode a number */
+static long decode(unsigned char **cpp)
+{
+ register int x;
+
+ x = *(*cpp)++;
+ if (x == 0) {
+ return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
+ } else {
+ return x & 0xff; /* -1 if PULLCHAR returned error */
+ }
+}
+
+
+/* Initialize compression data structure
+ * slots must be in range 0 to 255 (zero meaning no compression)
+ */
+struct axvj_slcomp *
+axhc_init(int rslots, int tslots)
+{
+ register short i;
+ register struct axvj_cstate *ts;
+ struct axvj_slcomp *comp;
+
+ comp = (struct axvj_slcomp *)kmalloc(sizeof(struct axvj_slcomp),
+ GFP_ATOMIC);
+ if (! comp)
+ return NULL;
+
+ memset(comp, 0, sizeof(struct axvj_slcomp));
+
+ if ( rslots > 0 && rslots < 256 ) {
+ size_t rsize = rslots * sizeof(struct axvj_cstate);
+ comp->rstate = (struct axvj_cstate *) kmalloc(rsize, GFP_ATOMIC);
+ if (! comp->rstate)
+ {
+ kfree((unsigned char *)comp);
+ return NULL;
+ }
+ memset(comp->rstate, 0, rsize);
+ comp->rslot_limit = rslots - 1;
+ }
+
+ if ( tslots > 0 && tslots < 256 ) {
+ size_t tsize = tslots * sizeof(struct axvj_cstate);
+ comp->tstate = (struct axvj_cstate *) kmalloc(tsize, GFP_ATOMIC);
+ if (! comp->tstate)
+ {
+ kfree((unsigned char *)comp->rstate);
+ kfree((unsigned char *)comp);
+ return NULL;
+ }
+ memset(comp->tstate, 0, tsize);
+ comp->tslot_limit = tslots - 1;
+ }
+
+ comp->xmit_oldest = 0;
+ comp->xmit_current = 255;
+ comp->recv_current = 255;
+ /*
+ * don't accept any packets with implicit index until we get
+ * one with an explicit index. Otherwise the uncompress code
+ * will try to use connection 255, which is almost certainly
+ * out of range
+ */
+ comp->flags |= SLF_TOSS;
+
+ if ( tslots > 0 ) {
+ ts = comp->tstate;
+ for(i = comp->tslot_limit; i > 0; --i){
+ ts[i].cs_this = i;
+ ts[i].next = &(ts[i - 1]);
+ }
+ ts[0].next = &(ts[comp->tslot_limit]);
+ ts[0].cs_this = 0;
+ }
+ return comp;
+}
+
+
+/* Free a compression data structure */
+void
+axhc_free(struct axvj_slcomp *comp)
+{
+ if ( comp == NULL )
+ return;
+
+ if ( comp->rstate != NULL )
+ kfree( comp->rstate );
+
+ if ( comp->tstate != NULL )
+ kfree( comp->tstate );
+
+ kfree( comp );
+}
+
+/* Dear hacker. I assume that you have read and understood RFC 1144
+ * and the original slhc_compress() procedure before tinkering with
+ * this code.
+ *
+ * procedure is as follows:
+ * 1. check if packet is TCP. return AX25_P_IP if not.
+ * 2. check if SYN, FIN, MSS, WSCALE, TSTAMP or RST is set, or if ACK is not
+ * set. deny compression for these packets (do_compression = 0).
+ * 3. try to find the appopriate slot, reuse an old one if no match is found
+ * 4. attempt to compress the packet and check the following rules:
+ * - if the packet contains an old (outdated) seq and no new ack or
+ * window or urgent data, drop it (return 0).
+ * - if nothing changed since the last frame sent (no new seq, ack,
+ * window, urgent data, or changing TCP flags), drop it.
+ * - before dropping a packet, check if any packet made it through the
+ * filter within the last 120sec. If not, assume a packet loss and
+ * transmit the packet.
+ * 5. transmit a compressed, uncompressed or regular packet, depending
+ * on do_compression and cs->deny_compression.
+ */
+
+int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression)
+{
+ struct axvj_cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
+ struct axvj_cstate *lcs = ocs;
+ struct axvj_cstate *cs = lcs->next;
+ unsigned int hlen;
+ struct tcphdr *th, *oth;
+ struct iphdr *iph;
+ unsigned long deltaS, deltaA;
+ unsigned short changes = 0;
+ unsigned char new_seq[16];
+ unsigned char *cp = new_seq;
+
+ /* Peek at IP header */
+ iph = (struct iphdr *) skb->data;
+
+ /* Bail if this packet isn't TCP, or is an IP fragment */
+ if (iph->protocol != IPPROTO_TCP ||
+ (ntohs(iph->frag_off) & 0x1fff) ||
+ (iph->frag_off & 32)) {
+ /* Send as regular IP */
+ if (iph->protocol != IPPROTO_TCP)
+ comp->sls_o_nontcp++;
+ else
+ comp->sls_o_tcp++;
+ return AX25_P_IP;
+ }
+ /* Extract TCP header */
+ th = (struct tcphdr *) (((unsigned char *) iph) + iph->ihl * 4);
+ hlen = iph->ihl * 4 + th->doff * 4;
+
+ PRINTK((KERN_DEBUG "ax25_vj.c: th.seq=%0x\n", ntohl(th->seq)));
+ /*
+ * check if packet may be compressed.
+ */
+ if (th->syn || th->fin || th->rst || !th->ack) {
+ comp->sls_o_tcp++;
+ do_compression = 0;
+ }
+ /*
+ * locate the connection state slot
+ */
+ for (;;) {
+ if (iph->saddr == cs->cs_ip.saddr
+ && iph->daddr == cs->cs_ip.daddr
+ && th->source == cs->cs_tcp.source
+ && th->dest == cs->cs_tcp.dest)
+ goto found;
+
+ /* if current equal oldest, at end of list */
+ if (cs == ocs)
+ break;
+ lcs = cs;
+ cs = cs->next;
+ comp->sls_o_searches++;
+ }
+ /*
+ * Didn't find it -- re-use oldest axvj_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
+ * xmit_oldest to update the lru linkage.
+ */
+ comp->sls_o_misses++;
+ comp->xmit_oldest = lcs->cs_this;
+ cs->deny_compression = 0;
+ cs->lastdropped = 0;
+ PRINTK((KERN_DEBUG "ax25_vj.c: new slot %d\n", cs->cs_this));
+ goto uncompressed;
+
+ found:
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (lcs == ocs) {
+ /* found at most recently used */
+ } else if (cs == ocs) {
+ /* found at least recently used */
+ comp->xmit_oldest = lcs->cs_this;
+ } else {
+ /* more than 2 elements */
+ lcs->next = cs->next;
+ cs->next = ocs->next;
+ ocs->next = cs;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: found slot %d\n", cs->cs_this));
+ /*
+ * Make sure that only what we expect to change changed.
+ * Check the following:
+ * IP protocol version, header length & type of service.
+ * The "Don't fragment" bit.
+ * The time-to-live field.
+ * The TCP header length.
+ * IP options, if any.
+ * TCP options, if any.
+ * If any of these things are different between the previous &
+ * current datagram, we send the current datagram `uncompressed'.
+ */
+ oth = &cs->cs_tcp;
+
+ if (iph->version != cs->cs_ip.version || iph->ihl != cs->cs_ip.ihl
+ || iph->tos != cs->cs_ip.tos
+ || (iph->frag_off & 64) != (cs->cs_ip.frag_off & 64)
+ || iph->ttl != cs->cs_ip.ttl
+ || th->doff != cs->cs_tcp.doff
+ || (iph->ihl > 5 && memcmp(iph + 1, cs->cs_ipopt, ((iph->ihl) - 5) * 4) != 0)
+ || (th->doff > 5 && memcmp(th + 1, cs->cs_tcpopt, ((th->doff) - 5) * 4) != 0)) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: packet uncompressable\n"));
+ goto uncompressed;
+ }
+ /*
+ * Figure out which of the changing fields changed. The
+ * receiver expects changes in the order: urgent, window,
+ * ack, seq (the order minimizes the number of temporaries
+ * needed in this section of code).
+ */
+ if (th->urg) {
+ deltaS = ntohs(th->urg_ptr);
+ cp = encode(cp, 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;
+ }
+ if ((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0) {
+ cp = encode(cp, deltaS);
+ changes |= NEW_W;
+ }
+ if ((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L) {
+ if (deltaA > 0x0000ffff)
+ goto uncompressed;
+ cp = encode(cp, deltaA);
+ changes |= NEW_A;
+ }
+ if ((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L) {
+ if (deltaS > 0x0000ffff) {
+
+ /*
+ * - if the packet contains an old (outdated) seq and no
+ * new ack or window or urgent data, drop it (return 0)
+ */
+ if (before(ntohl(th->seq), ntohl(oth->seq)) && !changes) {
+ if (cs->lastdropped != 0) {
+ if (jiffies - cs->lastdropped > 120 * HZ) {
+ goto uncompressed;
+ }
+ } else {
+ cs->lastdropped = jiffies;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: old packet, dS=%0x th.seq=%0x oth.seq=%0x\n", deltaS, ntohl(th->seq), ntohl(oth->seq)));
+ return 0;
+ }
+ goto uncompressed;
+ }
+ cp = encode(cp, deltaS);
+ changes |= NEW_S;
+ }
+ 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 (iph->tot_len != cs->cs_ip.tot_len
+ && ntohs(cs->cs_ip.tot_len) == hlen) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: data following ack\n"));
+ break;
+ }
+ /*
+ * MW: drop retransmitted packet. seq and ack did not change,
+ * check if flags have changed.
+ */
+ if (th->fin != oth->fin || th->syn != oth->syn || th->rst != oth->rst
+ || th->ack != oth->ack) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: tcp flags changed\n"));
+ goto uncompressed;
+ }
+ if (cs->lastdropped != 0) {
+ if (jiffies - cs->lastdropped > 120 * HZ) {
+ goto uncompressed;
+ }
+ } else {
+ cs->lastdropped = jiffies;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: no changes detected\n"));
+ return 0;
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+ /* actual changes match one of our special case encodings --
+ * send packet uncompressed.
+ */
+ goto uncompressed;
+ case NEW_S | NEW_A:
+ if (deltaS == deltaA &&
+ deltaS == ntohs(cs->cs_ip.tot_len) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+ case NEW_S:
+ if (deltaS == ntohs(cs->cs_ip.tot_len) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ /*
+ * The Packet contains new information, it has not been dropped
+ * until here. But compression has been denied, so we transmit an
+ * uncompressed packet instead.
+ */
+ if (cs->deny_compression) {
+ goto uncompressed;
+ }
+ deltaS = ntohs(iph->id) - ntohs(cs->cs_ip.id);
+ if (deltaS != 1) {
+ cp = encode(cp, deltaS);
+ changes |= NEW_I;
+ }
+ if (th->psh)
+ changes |= TCP_PUSH_BIT;
+ /* Grab the cksum before we overwrite it below. Then update our
+ * state with this packet's header.
+ */
+ deltaA = ntohs(th->check);
+ memcpy(&cs->cs_ip, iph, 20);
+ memcpy(&cs->cs_tcp, th, 20);
+ cs->lastdropped = 0;
+
+ /*
+ * MW: We don't actually perform the compression if we run on an
+ * uncompressible stream.
+ */
+ if (!do_compression) {
+ cs->deny_compression = 1;
+ return AX25_P_IP;
+ }
+ /* 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.
+ */
+ deltaS = cp - new_seq;
+ skb_pull(skb, hlen); /* Strip TCP/IP headers */
+ if (comp->xmit_current != cs->cs_this) {
+ cp = skb_push(skb, deltaS + 4);
+ *cp++ = changes | NEW_C;
+ *cp++ = cs->cs_this;
+ comp->xmit_current = cs->cs_this;
+ } else {
+ cp = skb_push(skb, deltaS + 3);
+ *cp++ = changes;
+ }
+ cp = put16(cp, (short) deltaA); /* Write TCP checksum */
+ memcpy(cp, new_seq, deltaS); /* Write list of deltas */
+ comp->sls_o_compressed++;
+ return AX25_P_VJCOMP;
+
+ /* Update connection state cs & send uncompressed packet (i.e.,
+ * a regular ip/tcp packet but with the 'conversation id' we hope
+ * to use on future compressed packets in the protocol field).
+ */
+ uncompressed:
+ memcpy(&cs->cs_ip, iph, 20);
+ memcpy(&cs->cs_tcp, th, 20);
+ if (iph->ihl > 5)
+ memcpy(cs->cs_ipopt, iph + 1, ((iph->ihl) - 5) * 4);
+ if (th->doff > 5)
+ memcpy(cs->cs_tcpopt, th + 1, ((th->doff) - 5) * 4);
+ comp->xmit_current = cs->cs_this;
+ cs->lastdropped = 0;
+
+ if (!do_compression) {
+ cs->deny_compression = 1;
+ return AX25_P_IP;
+ }
+ iph->protocol = cs->cs_this;
+ cs->deny_compression = 0;
+ comp->sls_o_uncompressed++;
+ return AX25_P_VJUNCOMP;
+}
+
+int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb)
+{
+ register int changes;
+ long x;
+ register struct tcphdr *thp;
+ register struct iphdr *ip;
+ register struct axvj_cstate *cs;
+ int len, hdrlen;
+
+ int isize = skb->len;
+ unsigned char *icp = skb->data;
+ unsigned char *cp = icp;
+
+ /* We've got a compressed packet; read the change byte */
+ comp->sls_i_compressed++;
+ if (isize < 3) {
+ comp->sls_i_error++;
+ return 0;
+ }
+ changes = *cp++;
+ if (changes & NEW_C) {
+ /* Make sure the state index is in range, then grab the state.
+ * If we have a good state index, clear the 'discard' flag.
+ */
+ x = *cp++; /* Read conn index */
+ if (x < 0 || x > comp->rslot_limit)
+ goto bad;
+
+ comp->flags &= ~SLF_TOSS;
+ comp->recv_current = x;
+ } else {
+ /* 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->flags & SLF_TOSS) {
+ comp->sls_i_tossed++;
+ return 0;
+ }
+ }
+ cs = &comp->rstate[comp->recv_current];
+ thp = &cs->cs_tcp;
+ ip = &cs->cs_ip;
+
+ if ((x = pull16(&cp)) == -1) { /* Read the TCP checksum */
+ goto bad;
+ }
+ thp->check = htons(x);
+
+ thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
+/*
+ * we can use the same number for the length of the saved header and
+ * the current one, because the packet wouldn't have been sent
+ * as compressed unless the options were the same as the previous one
+ */
+
+ hdrlen = ip->ihl * 4 + thp->doff * 4;
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I: /* Echoed terminal traffic */
+ {
+ register short i;
+ i = ntohs(ip->tot_len) - hdrlen;
+ thp->ack_seq = htonl(ntohl(thp->ack_seq) + i);
+ thp->seq = htonl(ntohl(thp->seq) + i);
+ }
+ break;
+
+ case SPECIAL_D: /* Unidirectional data */
+ thp->seq = htonl(ntohl(thp->seq) +
+ ntohs(ip->tot_len) - hdrlen);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ thp->urg = 1;
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->urg_ptr = htons(x);
+ } else
+ thp->urg = 0;
+ if (changes & NEW_W) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->window = htons(ntohs(thp->window) + x);
+ }
+ if (changes & NEW_A) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->ack_seq = htonl(ntohl(thp->ack_seq) + x);
+ }
+ if (changes & NEW_S) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->seq = htonl(ntohl(thp->seq) + x);
+ }
+ break;
+ }
+ if (changes & NEW_I) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ ip->id = htons(ntohs(ip->id) + x);
+ } else
+ ip->id = htons(ntohs(ip->id) + 1);
+
+ /*
+ * At this point, cp points to the first byte of data in the
+ * packet. Put the reconstructed TCP and IP headers back on the
+ * packet. Recalculate IP checksum (but not TCP checksum).
+ */
+
+ len = isize - (cp - icp);
+ if (len < 0)
+ goto bad;
+ len += hdrlen;
+ ip->tot_len = htons(len);
+ ip->check = 0;
+
+ /*
+ * MW:
+ * we are working on sk_buffs here, so we can spare the memmove()
+ * and simply skb_push() the hdrlen
+ */
+
+ skb_push(skb, hdrlen - (cp - icp));
+
+ cp = icp = skb->data;
+ memcpy(cp, ip, 20);
+ cp += 20;
+
+ if (ip->ihl > 5) {
+ memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
+ cp += (ip->ihl - 5) * 4;
+ }
+ put_unaligned(ip_fast_csum(icp, ip->ihl),
+ &((struct iphdr *) icp)->check);
+
+ memcpy(cp, thp, 20);
+ cp += 20;
+
+ if (thp->doff > 5) {
+ memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
+ cp += ((thp->doff) - 5) * 4;
+ }
+ return len;
+ bad:
+ comp->sls_i_error++;
+ return axhc_toss(comp);
+}
+
+
+int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb)
+{
+ register struct axvj_cstate *cs;
+ unsigned ihl;
+
+ unsigned char index;
+
+ int isize = skb->len;
+ unsigned char *icp = skb->data;
+
+ if (isize < 20) {
+ /* The packet is shorter than a legal IP header */
+ printk(KERN_DEBUG "axhc_remember: short packet from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_runt++;
+ return axhc_toss(comp);
+ }
+ /* Peek at the IP header's IHL field to find its length */
+ ihl = icp[0] & 0xf;
+ if (ihl < 20 / 4) {
+ /* The IP header length field is too small */
+ printk(KERN_DEBUG "axhc_remember: ihl too small from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_runt++;
+ return axhc_toss(comp);
+ }
+ index = icp[9];
+ icp[9] = IPPROTO_TCP;
+
+ if (ip_fast_csum(icp, ihl)) {
+ /* Bad IP header checksum; discard */
+ printk(KERN_DEBUG "axhc_remember: bad ip header checksum from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_badcheck++;
+ return axhc_toss(comp);
+ }
+ if (index > comp->rslot_limit) {
+ printk(KERN_DEBUG "axhc_remember: illegal slot from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_error++;
+ return axhc_toss(comp);
+ }
+ /* Update local state */
+ cs = &comp->rstate[comp->recv_current = index];
+ comp->flags &= ~SLF_TOSS;
+ memcpy(&cs->cs_ip, icp, 20);
+ memcpy(&cs->cs_tcp, icp + ihl * 4, 20);
+ if (ihl > 5)
+ memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
+ if (cs->cs_tcp.doff > 5)
+ memcpy(cs->cs_tcpopt, icp + ihl * 4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
+ cs->cs_hsize = ihl * 2 + cs->cs_tcp.doff * 2;
+ /* Put headers back on packet
+ * Neither header checksum is recalculated
+ */
+ comp->sls_i_uncompressed++;
+ return isize;
+}
+
+static int
+axhc_toss(struct axvj_slcomp *comp)
+{
+ if ( comp == NULL )
+ return 0;
+
+ comp->flags |= SLF_TOSS;
+ return 0;
+}
+
+#endif