aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/.gitignore1
-rw-r--r--openbsc/configure.ac1
-rw-r--r--openbsc/include/openbsc/Makefile.am1
-rw-r--r--openbsc/include/openbsc/debug.h1
-rw-r--r--openbsc/include/openbsc/slhc.h (renamed from openbsc/include/openbsc/slhc_vj.h)6
-rw-r--r--openbsc/src/gprs/Makefile.am1
-rw-r--r--openbsc/src/gprs/sgsn_main.c5
-rw-r--r--openbsc/src/gprs/slhc.c245
-rw-r--r--openbsc/tests/Makefile.am1
-rw-r--r--openbsc/tests/sgsn/Makefile.am1
-rw-r--r--openbsc/tests/slhc/Makefile.am15
-rw-r--r--openbsc/tests/slhc/slhc_test.c298
-rw-r--r--openbsc/tests/slhc/slhc_test.ok52
-rw-r--r--openbsc/tests/testsuite.at5
14 files changed, 544 insertions, 89 deletions
diff --git a/openbsc/.gitignore b/openbsc/.gitignore
index 8ce3b7033..e75b9eb98 100644
--- a/openbsc/.gitignore
+++ b/openbsc/.gitignore
@@ -83,6 +83,7 @@ tests/gtphub/gtphub_test
tests/mm_auth/mm_auth_test
tests/xid/xid_test
tests/sndcp_xid/sndcp_xid_test
+tests/slhc/slhc_test
tests/atconfig
tests/atlocal
diff --git a/openbsc/configure.ac b/openbsc/configure.ac
index 9dc2f8d97..cebabdc58 100644
--- a/openbsc/configure.ac
+++ b/openbsc/configure.ac
@@ -232,6 +232,7 @@ AC_OUTPUT(
tests/mm_auth/Makefile
tests/xid/Makefile
tests/sndcp_xid/Makefile
+ tests/slhc/Makefile
doc/Makefile
doc/examples/Makefile
Makefile)
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index a91322fa6..12e1a664e 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -65,6 +65,7 @@ noinst_HEADERS = \
sgsn.h \
signal.h \
silent_call.h \
+ slhc.h \
smpp.h \
sms_queue.h \
socket.h \
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index 43ebb19a0..90ddca5a2 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -29,6 +29,7 @@ enum {
DBSSGP,
DLLC,
DSNDCP,
+ DSLHC,
DNAT,
DCTRL,
DSMPP,
diff --git a/openbsc/include/openbsc/slhc_vj.h b/openbsc/include/openbsc/slhc.h
index 8716d5942..cd5a47cf4 100644
--- a/openbsc/include/openbsc/slhc_vj.h
+++ b/openbsc/include/openbsc/slhc.h
@@ -171,7 +171,8 @@ struct slcompress {
#define NULLSLCOMPR (struct slcompress *)0
/* In slhc.c: */
-struct slcompress *slhc_init(int rslots, int tslots);
+struct slcompress *slhc_init(const void *ctx, int rslots, int tslots);
+
void slhc_free(struct slcompress *comp);
int slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
@@ -180,4 +181,7 @@ int slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize);
int slhc_remember(struct slcompress *comp, unsigned char *icp, int isize);
int slhc_toss(struct slcompress *comp);
+void slhc_i_status(struct slcompress *comp);
+void slhc_o_status(struct slcompress *comp);
+
#endif /* _SLHC_H */
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am
index 3b5839997..06c12d7d9 100644
--- a/openbsc/src/gprs/Makefile.am
+++ b/openbsc/src/gprs/Makefile.am
@@ -87,6 +87,7 @@ osmo_sgsn_SOURCES = \
gprs_gsup_client.c \
sgsn_cdr.c \
sgsn_ares.c \
+ slhc.c \
oap.c \
oap_messages.c \
gprs_llc_xid.c \
diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
index 7d533c060..9f3260d55 100644
--- a/openbsc/src/gprs/sgsn_main.c
+++ b/openbsc/src/gprs/sgsn_main.c
@@ -294,6 +294,11 @@ static struct log_info_cat gprs_categories[] = {
.description = "SCCP User Adaptation (SUA)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
+ [DSLHC] = {
+ .name = "DSLHC",
+ .description = "RFC1144 TCP/IP Header compression (SLHC)",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
};
static const struct log_info gprs_log_info = {
diff --git a/openbsc/src/gprs/slhc.c b/openbsc/src/gprs/slhc.c
index 27ed25252..cbdf8dbd8 100644
--- a/openbsc/src/gprs/slhc.c
+++ b/openbsc/src/gprs/slhc.c
@@ -50,61 +50,77 @@
* driver code belonging close to PPP and SLIP
*/
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <net/slhc_vj.h>
-
-#ifdef CONFIG_INET
-/* Entire module is for IP only */
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/termios.h>
-#include <linux/in.h>
-#include <linux/fcntl.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <net/ip.h>
-#include <net/protocol.h>
-#include <net/icmp.h>
-#include <net/tcp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <linux/timer.h>
-#include <asm/uaccess.h>
-#include <net/checksum.h>
-#include <asm/unaligned.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/talloc.h>
+#include <openbsc/slhc.h>
+#include <openbsc/debug.h>
+
+#define ERR_PTR(x) x
+
static unsigned char *encode(unsigned char *cp, unsigned short n);
static long decode(unsigned char **cpp);
static unsigned char * put16(unsigned char *cp, unsigned short x);
static unsigned short pull16(unsigned char **cpp);
+/* Replacement for kernel space function ip_fast_csum() */
+static uint16_t ip_fast_csum(uint8_t *iph, int ihl)
+{
+ int i;
+ uint16_t temp;
+ uint32_t accumulator = 0xFFFF;
+
+ for(i=0;i<ihl*2;i++)
+ {
+ temp = ((*iph) << 8)&0xFF00;
+ iph++;
+ temp |= (*iph)&0xFF;
+ iph++;
+
+ accumulator+=temp;
+ if(accumulator>0xFFFF)
+ {
+ accumulator++;
+ accumulator&=0xFFFF;
+ }
+ }
+
+ return (uint16_t)(htons(~accumulator)&0xFFFF);
+}
+
+/* Replacement for kernel space function put_unaligned() */
+static void put_unaligned(uint16_t val, void *ptr)
+{
+ memcpy(ptr,&val,sizeof(val));
+}
+
+
/* Allocate compression data structure
* slots must be in range 0 to 255 (zero meaning no compression)
* Returns pointer to structure or ERR_PTR() on error.
*/
struct slcompress *
-slhc_init(int rslots, int tslots)
+slhc_init(const void *ctx, int rslots, int tslots)
{
register short i;
register struct cstate *ts;
struct slcompress *comp;
if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255)
- return ERR_PTR(-EINVAL);
+ return NULL;
- comp = kzalloc(sizeof(struct slcompress), GFP_KERNEL);
+ comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress));
if (! comp)
goto out_fail;
if (rslots > 0) {
size_t rsize = rslots * sizeof(struct cstate);
- comp->rstate = kzalloc(rsize, GFP_KERNEL);
+ comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize);
if (! comp->rstate)
goto out_free;
comp->rslot_limit = rslots - 1;
@@ -112,7 +128,7 @@ slhc_init(int rslots, int tslots)
if (tslots > 0) {
size_t tsize = tslots * sizeof(struct cstate);
- comp->tstate = kzalloc(tsize, GFP_KERNEL);
+ comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize);
if (! comp->tstate)
goto out_free2;
comp->tslot_limit = tslots - 1;
@@ -141,11 +157,11 @@ slhc_init(int rslots, int tslots)
return comp;
out_free2:
- kfree(comp->rstate);
+ talloc_free(comp->rstate);
out_free:
- kfree(comp);
+ talloc_free(comp);
out_fail:
- return ERR_PTR(-ENOMEM);
+ return NULL;
}
@@ -153,16 +169,18 @@ out_fail:
void
slhc_free(struct slcompress *comp)
{
+ DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n");
+
if ( comp == NULLSLCOMPR )
return;
if ( comp->tstate != NULLSLSTATE )
- kfree( comp->tstate );
+ talloc_free(comp->tstate );
if ( comp->rstate != NULLSLSTATE )
- kfree( comp->rstate );
+ talloc_free( comp->rstate );
- kfree( comp );
+ talloc_free( comp );
}
@@ -187,6 +205,8 @@ encode(unsigned char *cp, unsigned short n)
} else {
*cp++ = n;
}
+
+ DEBUGP(DSLHC, "encode(): n=%04x\n",n);
return cp;
}
@@ -256,6 +276,7 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
comp->sls_o_nontcp++;
else
comp->sls_o_tcp++;
+ DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n");
return isize;
}
/* Extract TCP header */
@@ -271,6 +292,7 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
! (th->ack)){
/* TCP connection stuff; send as regular IP */
comp->sls_o_tcp++;
+ DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n");
return isize;
}
/*
@@ -287,6 +309,9 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
* states via linear search. If we don't find a state
* for the datagram, the oldest state is (re-)used.
*/
+
+ DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n");
+
for ( ; ; ) {
if( ip->saddr == cs->cs_ip.saddr
&& ip->daddr == cs->cs_ip.daddr
@@ -310,11 +335,14 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
* state points to the newest and we only need to set
* xmit_oldest to update the lru linkage.
*/
+
+ DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n");
comp->sls_o_misses++;
comp->xmit_oldest = lcs->cs_this;
goto uncompressed;
found:
+ DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n");
/*
* Found it -- move to the front on the connection list.
*/
@@ -344,6 +372,39 @@ found:
*/
oth = &cs->cs_tcp;
+ /* Display a little more debug information about which of the
+ * header fields changed unexpectedly */
+ if(ip->version != cs->cs_ip.version)
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n");
+ if(ip->ihl != cs->cs_ip.ihl)
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n");
+ if(ip->tos != cs->cs_ip.tos)
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n");
+ if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)))
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n");
+ if(ip->ttl != cs->cs_ip.ttl)
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n");
+ if(th->doff != cs->cs_tcp.doff)
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
+ if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
+ DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
+ DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n",
+ osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n",
+ osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4));
+ }
+ if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
+ DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
+ DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
+ DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n",
+ osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
+ DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
+ osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt,
+ ((th->doff)-5)*4));
+ }
+
+
if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
|| ip->tos != cs->cs_ip.tos
|| (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))
@@ -351,6 +412,7 @@ found:
|| th->doff != cs->cs_tcp.doff
|| (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
|| (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){
+ DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n");
goto uncompressed;
}
@@ -362,6 +424,7 @@ found:
*/
if(th->urg){
deltaS = ntohs(th->urg_ptr);
+ DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_U;
} else if(th->urg_ptr != oth->urg_ptr){
@@ -369,21 +432,29 @@ found:
* implementation should never do this but RFC793
* doesn't prohibit the change so we have to deal
* with it. */
+ DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n");
goto uncompressed;
}
if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
+ DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_W;
}
if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
- if(deltaA > 0x0000ffff)
+ if(deltaA > 0x0000ffff) {
+ DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n");
goto uncompressed;
+ }
+ DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n");
cp = encode(cp,deltaA);
changes |= NEW_A;
}
if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
- if(deltaS > 0x0000ffff)
+ if(deltaS > 0x0000ffff) {
+ DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n");
goto uncompressed;
+ }
+ DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_S;
}
@@ -399,17 +470,21 @@ found:
if(ip->tot_len != cs->cs_ip.tot_len &&
ntohs(cs->cs_ip.tot_len) == hlen)
break;
+ DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n");
goto uncompressed;
case SPECIAL_I:
case SPECIAL_D:
/* actual changes match one of our special case encodings --
* send packet uncompressed.
*/
+ DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n");
goto uncompressed;
case NEW_S|NEW_A:
if(deltaS == deltaA &&
deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
/* special case for echoed terminal traffic */
+ DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n");
+ DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
changes = SPECIAL_I;
cp = new_seq;
}
@@ -417,6 +492,8 @@ found:
case NEW_S:
if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
/* special case for data xfer */
+ DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n");
+ DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
changes = SPECIAL_D;
cp = new_seq;
}
@@ -424,11 +501,14 @@ found:
}
deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
if(deltaS != 1){
+ DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_I;
}
- if(th->psh)
+ if(th->psh) {
+ DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n");
changes |= TCP_PUSH_BIT;
+ }
/* Grab the cksum before we overwrite it below. Then update our
* state with this packet's header.
*/
@@ -445,6 +525,7 @@ found:
if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
cp = ocp;
*cpp = ocp;
+ DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n");
*cp++ = changes | NEW_C;
*cp++ = cs->cs_this;
comp->xmit_current = cs->cs_this;
@@ -456,6 +537,10 @@ found:
*(__sum16 *)cp = csum;
cp += 2;
/* deltaS is now the size of the change section of the compressed header */
+
+ DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
+ DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
+
memcpy(cp,new_seq,deltaS); /* Write list of deltas */
memcpy(cp+deltaS,icp+hlen,isize-hlen);
comp->sls_o_compressed++;
@@ -467,6 +552,7 @@ found:
* to use on future compressed packets in the protocol field).
*/
uncompressed:
+ DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n");
memcpy(&cs->cs_ip,ip,20);
memcpy(&cs->cs_tcp,th,20);
if (ip->ihl > 5)
@@ -538,6 +624,8 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
switch(changes & SPECIALS_MASK){
case SPECIAL_I: /* Echoed terminal traffic */
+ DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n");
+
{
register short i;
i = ntohs(ip->tot_len) - hdrlen;
@@ -547,11 +635,13 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
break;
case SPECIAL_D: /* Unidirectional data */
+ DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n");
thp->seq = htonl( ntohl(thp->seq) +
ntohs(ip->tot_len) - hdrlen);
break;
default:
+ DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n");
if(changes & NEW_U){
thp->urg = 1;
if((x = decode(&cp)) == -1) {
@@ -601,6 +691,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
ip->tot_len = htons(len);
ip->check = 0;
+ DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n");
memmove(icp + hdrlen, cp, len - hdrlen);
cp = icp;
@@ -625,6 +716,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
return len;
bad:
+ DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n");
comp->sls_i_error++;
return slhc_toss( comp );
}
@@ -641,6 +733,7 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
if(isize < 20) {
/* The packet is shorter than a legal IP header */
comp->sls_i_runt++;
+ DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n");
return slhc_toss( comp );
}
/* Peek at the IP header's IHL field to find its length */
@@ -648,6 +741,7 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
if(ihl < 20 / 4){
/* The IP header length field is too small */
comp->sls_i_runt++;
+ DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n");
return slhc_toss( comp );
}
index = icp[9];
@@ -656,10 +750,12 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
if (ip_fast_csum(icp, ihl)) {
/* Bad IP header checksum; discard */
comp->sls_i_badcheck++;
+ DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n");
return slhc_toss( comp );
}
if(index > comp->rslot_limit) {
comp->sls_i_error++;
+ DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n");
return slhc_toss(comp);
}
@@ -683,6 +779,7 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
int
slhc_toss(struct slcompress *comp)
{
+ DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n");
if ( comp == NULLSLCOMPR )
return 0;
@@ -690,55 +787,27 @@ slhc_toss(struct slcompress *comp)
return 0;
}
-#else /* CONFIG_INET */
-
-int
-slhc_toss(struct slcompress *comp)
-{
- printk(KERN_DEBUG "Called IP function on non IP-system: slhc_toss");
- return -EINVAL;
-}
-int
-slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
+void slhc_i_status(struct slcompress *comp)
{
- printk(KERN_DEBUG "Called IP function on non IP-system: slhc_uncompress");
- return -EINVAL;
-}
-int
-slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
- unsigned char *ocp, unsigned char **cpp, int compress_cid)
-{
- printk(KERN_DEBUG "Called IP function on non IP-system: slhc_compress");
- return -EINVAL;
-}
-
-int
-slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
-{
- printk(KERN_DEBUG "Called IP function on non IP-system: slhc_remember");
- return -EINVAL;
+ if (comp != NULLSLCOMPR) {
+ DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
+ comp->sls_i_compressed,
+ comp->sls_i_uncompressed,
+ comp->sls_i_error,
+ comp->sls_i_tossed);
+ }
}
-void
-slhc_free(struct slcompress *comp)
+void slhc_o_status(struct slcompress *comp)
{
- printk(KERN_DEBUG "Called IP function on non IP-system: slhc_free");
-}
-struct slcompress *
-slhc_init(int rslots, int tslots)
-{
- printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init");
- return NULL;
+ if (comp != NULLSLCOMPR) {
+ DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n",
+ comp->sls_o_compressed,
+ comp->sls_o_uncompressed,
+ comp->sls_o_tcp,
+ comp->sls_o_nontcp,
+ comp->sls_o_searches,
+ comp->sls_o_misses);
+ }
}
-#endif /* CONFIG_INET */
-
-/* VJ header compression */
-EXPORT_SYMBOL(slhc_init);
-EXPORT_SYMBOL(slhc_free);
-EXPORT_SYMBOL(slhc_remember);
-EXPORT_SYMBOL(slhc_compress);
-EXPORT_SYMBOL(slhc_uncompress);
-EXPORT_SYMBOL(slhc_toss);
-
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 7b145f789..2e342e0aa 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -11,6 +11,7 @@ SUBDIRS = \
mm_auth \
xid \
sndcp_xid \
+ slhc \
$(NULL)
if BUILD_NAT
diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index cf8810160..bab3f0ef1 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -58,6 +58,7 @@ sgsn_test_LDADD = \
$(top_builddir)/src/gprs/oap_messages.o \
$(top_builddir)/src/gprs/gprs_llc_xid.o \
$(top_builddir)/src/gprs/gprs_sndcp_xid.o \
+ $(top_builddir)/src/gprs/slhc.o \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOCORE_LIBS) \
diff --git a/openbsc/tests/slhc/Makefile.am b/openbsc/tests/slhc/Makefile.am
new file mode 100644
index 000000000..32a3cc447
--- /dev/null
+++ b/openbsc/tests/slhc/Makefile.am
@@ -0,0 +1,15 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS)
+
+EXTRA_DIST = slhc_test.ok
+
+noinst_PROGRAMS = slhc_test
+
+slhc_test_SOURCES = slhc_test.c
+
+slhc_test_LDADD = \
+ $(top_builddir)/src/gprs/slhc.o \
+ $(top_builddir)/src/libcommon/libcommon.a \
+ $(LIBOSMOCORE_LIBS)
+
+
diff --git a/openbsc/tests/slhc/slhc_test.c b/openbsc/tests/slhc/slhc_test.c
new file mode 100644
index 000000000..59a54259d
--- /dev/null
+++ b/openbsc/tests/slhc/slhc_test.c
@@ -0,0 +1,298 @@
+/* Test SLHC/RFC1144 TCP/IP Header compression/decompression */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <openbsc/slhc.h>
+#include <openbsc/debug.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/core/application.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+/* Number of compression slots (S0-1) */
+#define SLOTS 8
+
+/* Maximum packet bytes to display */
+#define DISP_MAX_BYTES 100
+
+/* Sample packets to test with */
+#define PACKETS_LEN 6
+char *packets[] = {
+ "4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27",
+ "4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0",
+ "4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01",
+ "4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01",
+ "4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a",
+ "4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20"
+};
+
+/* Compress a packet using Van Jacobson RFC1144 header compression */
+static int compress(uint8_t *data_o, uint8_t *data_i, int len,
+ struct slcompress *comp)
+{
+ uint8_t *comp_ptr; /* Not used */
+ int compr_len;
+
+ /* Create a working copy of the incoming data */
+ memcpy(data_o, data_i, len);
+
+ /* Run compressor */
+ compr_len = slhc_compress(comp, data_i, len, data_o, &comp_ptr, 0);
+ return compr_len;
+}
+
+/* Expand a packet using Van Jacobson RFC1144 header compression */
+static int expand(uint8_t *data_o, uint8_t *data_i, int len,
+ struct slcompress *comp)
+{
+ int data_decompressed_len;
+
+ /* Create a working copy of the incoming data */
+ memcpy(data_o, data_i, len);
+
+ /* Handle an uncompressed packet (learn header information */
+ if ((data_i[0] & SL_TYPE_UNCOMPRESSED_TCP) == SL_TYPE_UNCOMPRESSED_TCP) {
+ data_o[0] &= 0x4F;
+ data_decompressed_len = slhc_remember(comp, data_o, len);
+ return data_decompressed_len;
+ }
+
+ /* Uncompress compressed packets */
+ else if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
+ data_decompressed_len = slhc_uncompress(comp, data_o, len);
+ return data_decompressed_len;
+ }
+
+ /* Regular or unknown packets will not be touched */
+ return len;
+}
+
+/* Calculate IP Header checksum */
+static uint16_t calc_ip_csum(uint8_t *data, int len)
+{
+ int i;
+ uint32_t accumulator = 0;
+ uint16_t *pointer = (uint16_t *) data;
+
+ for (i = len; i > 1; i -= 2) {
+ accumulator += *pointer;
+ pointer++;
+ }
+
+ if (len % 2)
+ accumulator += *pointer;
+
+ accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff);
+ accumulator += (accumulator >> 16) & 0xffff;
+ return (~accumulator);
+}
+
+/* Calculate TCP/IP checksum */
+static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len)
+{
+ uint8_t *buf;
+ uint16_t csum;
+
+ buf = talloc_zero_size(ctx, len);
+ memset(buf, 0, len);
+ memcpy(buf, packet + 12, 8);
+ buf[9] = packet[9];
+ buf[11] = (len - 20) & 0xFF;
+ buf[10] = (len - 20) >> 8 & 0xFF;
+ memcpy(buf + 12, packet + 20, len - 20);
+ csum = calc_ip_csum(buf, len - 20 + 12);
+ talloc_free(buf);
+ return csum;
+}
+
+/* Check TCP/IP packet */
+static void check_packet(const void *ctx, uint8_t *packet, int len)
+{
+ /* Check IP header */
+ OSMO_ASSERT(len > 20);
+ OSMO_ASSERT(calc_ip_csum(packet, 20) == 0);
+
+ /* Check TCP packet */
+ if (packet[9] != 0x06)
+ return;
+ OSMO_ASSERT(len > 40);
+ OSMO_ASSERT(calc_tcpip_csum(ctx, packet, len) == 0);
+}
+
+/* Strip TCP options from TCP/IP packet */
+static int strip_tcp_options(const void *ctx, uint8_t *packet, int len)
+{
+ uint8_t doff;
+ uint16_t csum;
+
+ /* Check if the packet can be handled here */
+ if (len < 37)
+ return len;
+ if (packet[9] != 0x06)
+ return len;
+
+ /* Strip TCP/IP options from packet */
+ doff = ((packet[32] >> 4) & 0x0F) * 4;
+ memmove(packet + 40, packet + doff + 20, len - 40 - (doff - 20));
+ len = len - (doff - 20);
+
+ /* Repair data offset (TCP header length) */
+ packet[32] &= 0x0F;
+ packet[32] |= 0x50;
+
+ /* Repair checksum */
+ packet[36] = 0;
+ packet[37] = 0;
+ csum = calc_tcpip_csum(ctx, packet, len);
+ packet[36] = csum & 0xFF;
+ packet[37] = csum >> 8 & 0xFF;
+
+ /* Repair total length */
+ packet[3] = len & 0xFF;
+ packet[2] = len >> 8 & 0xFF;
+
+ /* Repair IP header checksum */
+ packet[10] = 0;
+ packet[11] = 0;
+ csum = calc_ip_csum(packet, 20);
+ packet[10] = csum & 0xFF;
+ packet[11] = csum >> 8 & 0xFF;
+ printf("csum=%04x\n", csum);
+
+ return len;
+}
+
+/* Compress / Decompress packets */
+static void test_slhc(const void *ctx)
+{
+ char packet_ascii[2048];
+ int i;
+
+ struct slcompress *comp;
+ uint8_t packet[1024];
+ int packet_len;
+ uint8_t packet_compr[1024];
+ int packet_compr_len;
+ uint8_t packet_decompr[1024];
+ int packet_decompr_len;
+
+ printf("Allocating compression state...\n");
+ comp = slhc_init(ctx, SLOTS, SLOTS);
+ OSMO_ASSERT(comp);
+
+ for(i=0;i<PACKETS_LEN;i++) {
+ /* Read input file */
+ memset(packet_ascii, 0, sizeof(packet_ascii));
+ memset(packet, 0, sizeof(packet));
+ memset(packet_compr, 0, sizeof(packet_compr));
+ memset(packet_decompr, 0, sizeof(packet_decompr));
+ strcpy(packet_ascii,packets[i]);
+
+ packet_len =
+ osmo_hexparse(packet_ascii, packet, sizeof(packet));
+ check_packet(ctx, packet, packet_len);
+ packet_len = strip_tcp_options(ctx, packet, packet_len);
+ check_packet(ctx, packet, packet_len);
+
+ /* Run compression/decompression algorithm */
+ printf("Compressing...\n");
+ packet_compr_len =
+ compress(packet_compr, packet, packet_len, comp);
+ printf("Decompressing...\n");
+ packet_decompr_len =
+ expand(packet_decompr, packet_compr, packet_compr_len,
+ comp);
+ OSMO_ASSERT(packet_decompr_len == packet_len);
+ check_packet(ctx,packet_decompr,packet_decompr_len);
+
+ /* Display results */
+ printf("Results:\n");
+ if (packet_compr_len > DISP_MAX_BYTES)
+ packet_compr_len = DISP_MAX_BYTES;
+ if (packet_len > DISP_MAX_BYTES)
+ packet_len = DISP_MAX_BYTES;
+ if (packet_decompr_len > DISP_MAX_BYTES)
+ packet_decompr_len = DISP_MAX_BYTES;
+ printf("Original Packet: (%i bytes) %s\n", packet_len,
+ osmo_hexdump_nospc(packet, packet_len));
+ printf("DecompressedPacket: (%i bytes) %s\n",
+ packet_decompr_len, osmo_hexdump_nospc(packet_decompr,
+ packet_decompr_len));
+ printf("CompressedPacket: (%i bytes) %s\n", packet_compr_len,
+ osmo_hexdump_nospc(packet_compr, packet_compr_len));
+ slhc_o_status(comp);
+ slhc_o_status(comp);
+
+ printf("\n");
+ }
+
+ printf("Freeing compression state...\n");
+ slhc_free(comp);
+ printf("\n");
+}
+
+static struct log_info_cat gprs_categories[] = {
+ [DSNDCP] = {
+ .name = "DSNDCP",
+ .description =
+ "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
+ .enabled = 1,.loglevel = LOGL_DEBUG,
+ },
+ [DSLHC] = {
+ .name = "DSLHC",
+ .description =
+ "Van Jacobson RFC1144 TCP/IP header compression (SLHC)",
+ .enabled = 1,.loglevel = LOGL_DEBUG,
+ }
+};
+
+static struct log_info info = {
+ .cat = gprs_categories,
+ .num_cat = ARRAY_SIZE(gprs_categories),
+};
+
+int main(int argc, char **argv)
+{
+ void *ctx;
+
+ osmo_init_logging(&info);
+
+ ctx = talloc_named_const(NULL, 0, "slhc_ctx");
+
+ test_slhc(ctx);
+
+ printf("Done\n");
+
+ talloc_report_full(ctx, stderr);
+ OSMO_ASSERT(talloc_total_blocks(ctx) == 1);
+ return 0;
+}
+
+/* stubs */
+struct osmo_prim_hdr;
+int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ abort();
+}
diff --git a/openbsc/tests/slhc/slhc_test.ok b/openbsc/tests/slhc/slhc_test.ok
new file mode 100644
index 000000000..636241dba
--- /dev/null
+++ b/openbsc/tests/slhc/slhc_test.ok
@@ -0,0 +1,52 @@
+Allocating compression state...
+csum=b3a9
+Compressing...
+Decompressing...
+Results:
+Original Packet: (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
+DecompressedPacket: (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
+CompressedPacket: (52 bytes) 7510003446dd40004000a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
+
+csum=97a9
+Compressing...
+Decompressing...
+Results:
+Original Packet: (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
+DecompressedPacket: (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
+CompressedPacket: (43 bytes) df00cda4fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
+
+csum=baa9
+Compressing...
+Decompressing...
+Results:
+Original Packet: (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01
+DecompressedPacket: (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01
+CompressedPacket: (9 bytes) dc00a70a5227fffd01
+
+csum=b9a9
+Compressing...
+Decompressing...
+Results:
+Original Packet: (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01
+DecompressedPacket: (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01
+CompressedPacket: (7 bytes) db00a706fffb01
+
+csum=7ba9
+Compressing...
+Decompressing...
+Results:
+Original Packet: (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
+DecompressedPacket: (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
+CompressedPacket: (68 bytes) db00c2d00d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a
+
+csum=aca9
+Compressing...
+Decompressing...
+Results:
+Original Packet: (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20
+DecompressedPacket: (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20
+CompressedPacket: (18 bytes) df0021fb706f6c6c7578206c6f67696e3a20
+
+Freeing compression state...
+
+Done
diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at
index 85a81d6d5..5f37b8e22 100644
--- a/openbsc/tests/testsuite.at
+++ b/openbsc/tests/testsuite.at
@@ -136,3 +136,8 @@ cat $abs_srcdir/sndcp_xid/sndcp_xid_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/sndcp_xid/sndcp_xid_test], [], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([slhc])
+AT_KEYWORDS([slhc])
+cat $abs_srcdir/slhc/slhc_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/slhc/slhc_test], [], [expout], [ignore])
+AT_CLEANUP