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
This commit is contained in:
Philipp Maier 2016-08-26 16:58:41 +02:00 committed by Harald Welte
parent b3e116c74d
commit 2c7f83762a
14 changed files with 543 additions and 88 deletions

1
openbsc/.gitignore vendored
View File

@ -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

View File

@ -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)

View File

@ -65,6 +65,7 @@ noinst_HEADERS = \
sgsn.h \
signal.h \
silent_call.h \
slhc.h \
smpp.h \
sms_queue.h \
socket.h \

View File

@ -29,6 +29,7 @@ enum {
DBSSGP,
DLLC,
DSNDCP,
DSLHC,
DNAT,
DCTRL,
DSMPP,

View File

@ -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 */

View File

@ -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 \

View File

@ -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 = {

View File

@ -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>
#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
#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>
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)
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");

View File

@ -11,6 +11,7 @@ SUBDIRS = \
mm_auth \
xid \
sndcp_xid \
slhc \
$(NULL)
if BUILD_NAT

View File

@ -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) \

View File

@ -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)

View File

@ -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();
}

View File

@ -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

View File

@ -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