From 2c7f83762ab28ef4051d379e295a824e83577d7f Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 26 Aug 2016 16:58:41 +0200 Subject: [PATCH] RFC1144: integration and unit-test The previously pushed slhc implementation has been modified to compile and function outside of the kernel. Also debug log messages were added and datatypes ware matched. The implementation is now ready to be used Change-Id: I7a638e88a43b3eb9d006751a03ef2570e36613f0 --- openbsc/.gitignore | 1 + openbsc/configure.ac | 1 + openbsc/include/openbsc/Makefile.am | 1 + openbsc/include/openbsc/debug.h | 1 + openbsc/include/openbsc/{slhc_vj.h => slhc.h} | 6 +- openbsc/src/gprs/Makefile.am | 1 + openbsc/src/gprs/sgsn_main.c | 5 + openbsc/src/gprs/slhc.c | 243 +++++++++----- openbsc/tests/Makefile.am | 1 + openbsc/tests/sgsn/Makefile.am | 1 + openbsc/tests/slhc/Makefile.am | 15 + openbsc/tests/slhc/slhc_test.c | 298 ++++++++++++++++++ openbsc/tests/slhc/slhc_test.ok | 52 +++ openbsc/tests/testsuite.at | 5 + 14 files changed, 543 insertions(+), 88 deletions(-) rename openbsc/include/openbsc/{slhc_vj.h => slhc.h} (97%) create mode 100644 openbsc/tests/slhc/Makefile.am create mode 100644 openbsc/tests/slhc/slhc_test.c create mode 100644 openbsc/tests/slhc/slhc_test.ok 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 similarity index 97% rename from openbsc/include/openbsc/slhc_vj.h rename to 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 -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERR_PTR(x) x -#ifdef CONFIG_INET -/* Entire module is for IP only */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include 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;i0xFFFF) + { + 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) +void slhc_i_status(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) -{ - 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; + 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); + } } -int -slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) +void slhc_o_status(struct slcompress *comp) { - printk(KERN_DEBUG "Called IP function on non IP-system: slhc_remember"); - return -EINVAL; + 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); + } } -void -slhc_free(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; -} - -#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 + * 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 . + */ + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +/* 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 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