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