sndcp: Initial libosmo-gprs-sndcp support

This commit follows a similar approach to Change-Id
I588eb576b2703262f4ab9566ec362920d8390cfd, this time targeting the SNDCP
layer, creating a new library for it.
This new library depends on headers from libosmo-gprs-llc since the
SNDCP spec takes the interface towards lower interfaces from same
llc_prim. It doesn't really call any API from the libosmo-gprs-llc
library to dispatch the primitive, that's left for the application, so
that it can be reused against other implementations.

Most of the SNDCP data structures and APIs are kept private and used
only internally. The Higher/lower layers are expected to interact with
it through the sndcp_prim API.

This commit also implements some of the code paths of the public API by
means on importing SNDCP code from osmo-sgsn.git commit
57b63875c762a784127a13becd1c2549ca6c5454.
The import of code cannot be done in a separate commit since existing code
in osmo-sgsn.git is low quality and has tons of layer violations in all
directions.
Hence, this commit aims at being an initial point of having some working
SNDCP stack by means of a few unit tests, but by no means aims to be a
total working implementation. Some code paths are missing; bugs are
expected at this point.

Change-Id: Ie05b5d721cf0a6147ed45c1feb75ad829865252b
This commit is contained in:
Pau Espin 2022-12-05 14:01:36 +01:00 committed by pespin
parent fc11417ce6
commit 24a100bac8
40 changed files with 8774 additions and 9 deletions

View File

@ -5,3 +5,8 @@
--exclude ^src/rlcmac/ts_44_018.c$
--exclude ^src/rlcmac/ts_44_060.c$
--exclude ^include/osmocom/gprs/rlcmac/gprs_rlcmac.h$
--exclude ^include/osmocom/gprs/sndcp/v42bis_private.h$
--exclude ^include/osmocom/gprs/sndcp/v42bis.h$
--exclude ^src/sndcp/v42bis.c$
--exclude ^include/osmocom/gprs/sndcp/slhc.h$
--exclude ^src/sndcp/slhc.c$

View File

@ -17,6 +17,7 @@ pkgconfig_DATA = \
libosmo-csn1.pc \
libosmo-gprs-llc.pc \
libosmo-gprs-rlcmac.pc \
libosmo-gprs-sndcp.pc \
$(NULL)
BUILT_SOURCES = $(top_srcdir)/.version

View File

@ -77,19 +77,23 @@ AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([libosmo-csn1.pc
libosmo-gprs-llc.pc
libosmo-gprs-rlcmac.pc
libosmo-gprs-sndcp.pc
include/Makefile
include/osmocom/Makefile
include/osmocom/csn1/Makefile
include/osmocom/gprs/Makefile
include/osmocom/gprs/llc/Makefile
include/osmocom/gprs/rlcmac/Makefile
include/osmocom/gprs/sndcp/Makefile
src/Makefile
src/csn1/Makefile
src/llc/Makefile
src/rlcmac/Makefile
src/sndcp/Makefile
tests/Makefile
tests/llc/Makefile
tests/rlcmac/Makefile
tests/sndcp/Makefile
Makefile
contrib/libosmo-gprs.spec])
AC_OUTPUT

View File

@ -78,6 +78,23 @@ Requires: libosmo-csn1-devel = %{version}
This package provides development files for compiling a program using
libosmo-gprs-rlcmac - RLC/MAC protocol definitions for (E)GPRS.
%package -n libosmo-gprs-sndcp0
Summary: Osmocom GPRS SNDCP library
License: AGPL-3.0-or-later
Group: System/Libraries
%description -n libosmo-gprs-sndcp0
This package provides SNDCP (Subnetwork Dependent Convergence Protocol) layer for (E)GPRS.
%package -n libosmo-gprs-sndcp-devel
Summary: Development files for libosmo-gprs-sndcp
License: AGPL-3.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmo-gprs-sndcp0 = %{version}
%description -n libosmo-gprs-sndcp-devel
This package provides development files for compiling a program using
libosmo-gprs-sndcp - SNDCP (Subnetwork Dependent Convergence Protocol) layer for (E)GPRS.
%prep
%setup -q
@ -101,6 +118,8 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%postun -n libosmo-gprs-llc0 -p /sbin/ldconfig
%post -n libosmo-gprs-rlcmac0 -p /sbin/ldconfig
%postun -n libosmo-gprs-rlcmac0 -p /sbin/ldconfig
%post -n libosmo-gprs-sndcp0 -p /sbin/ldconfig
%postun -n libosmo-gprs-sndcp0 -p /sbin/ldconfig
%files -n libosmo-csn1-0
%_libdir/libosmo-csn1.so.0*
@ -134,4 +153,15 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%_libdir/libosmo-gprs-rlcmac.so
%_libdir/pkgconfig/libosmo-gprs-rlcmac.pc
%files -n libosmo-gprs-sndcp0
%_libdir/libosmo-gprs-sndcp.so.0*
%files -n libosmo-gprs-sndcp-devel
%dir %_includedir/%name
%dir %_includedir/%name/osmocom
%dir %_includedir/%name/osmocom/gprs
%_includedir/%name/osmocom/gprs/sndcp
%_libdir/libosmo-gprs-sndcp.so
%_libdir/pkgconfig/libosmo-gprs-sndcp.pc
%changelog

25
debian/control vendored
View File

@ -92,3 +92,28 @@ Section: libdevel
Depends: libosmo-gprs-rlcmac0 (= ${binary:Version}),
${misc:Depends}
Description: Development headers and libraries for libosmo-gprs-rlcmac
Package: libosmo-gprs-sndcp0
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends},
${shlibs:Depends}
Description: Osmocom SNDCP (Subnetwork Dependent Convergence Protocol) layer for GPRS and EGPRS
Package: libosmo-gprs-sndcp-dbg
Architecture: any
Section: debug
Multi-Arch: same
Depends: libosmo-gprs-sndcp0 (= ${binary:Version}),
${misc:Depends}
Description: Debug symbols for libosmo-gprs-sndcp
Package: libosmo-gprs-sndcp-dev
Architecture: any
Multi-Arch: same
Section: libdevel
Depends: libosmo-gprs-sndcp0 (= ${binary:Version}),
${misc:Depends}
Description: Development headers and libraries for libosmo-gprs-sndcp

View File

@ -1,4 +1,5 @@
SUBDIRS = \
llc \
rlcmac \
sndcp \
$(NULL)

View File

@ -252,15 +252,6 @@ struct gprs_llc_llme {
uint16_t nsei;
struct gprs_llc_lle lle[NUM_SAPIS];
/* Compression entities */
struct {
/* In these two list_heads we will store the
* data and protocol compression entities,
* together with their compression states */
struct llist_head *proto;
struct llist_head *data;
} comp;
/* Internal management */
uint32_t age_timestamp;
};

View File

@ -0,0 +1,18 @@
noinst_HEADERS = \
comp.h \
dcomp.h \
pcomp.h \
slhc.h \
sndcp_private.h \
xid.h \
v42bis.h \
v42bis_private.h \
$(NULL)
sndcp_HEADERS = \
sndcp.h \
sndcp_prim.h \
$(NULL)
sndcpdir = $(includedir)/osmocom/gprs/sndcp

View File

@ -0,0 +1,82 @@
/* GPRS SNDCP header compression entity management tools */
/* (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/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gprs/sndcp/xid.h>
/* Header / Data compression entity */
struct gprs_sndcp_comp {
struct llist_head list;
/* Serves as an ID in case we want to delete this entity later */
unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */
/* Specifies to which NSAPIs the compression entity is assigned */
uint8_t nsapi_len; /* Number of applicable NSAPIs (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
/* Assigned pcomp values */
uint8_t comp_len; /* Number of contained PCOMP / DCOMP values */
uint8_t comp[MAX_COMP]; /* see also: 6.5.1.1.5 and 6.6.1.1.5 */
/* Algorithm parameters */
union gprs_sndcp_comp_algo algo;
enum gprs_sndcp_xid_param_types compclass; /* See gprs_sndcp_xid.h/c */
void *state; /* Algorithm status and parameters */
};
#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */
#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */
/* Allocate a compression enitiy list */
struct llist_head *gprs_sndcp_comp_alloc(const void *ctx);
/* Free a compression entitiy list */
void gprs_sndcp_comp_free(struct llist_head *comp_entities);
/* Delete a compression entity */
void gprs_sndcp_comp_delete(struct llist_head *comp_entities, unsigned int entity);
/* Create and Add a new compression entity
* (returns a pointer to the compression entity that has just been created) */
struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
struct llist_head *comp_entities,
const struct gprs_sndcp_comp_field
*comp_field);
/* Find which compression entity handles the specified pcomp/dcomp */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
*comp_entities, uint8_t comp);
/* Find which compression entity handles the specified nsapi */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
*comp_entities, uint8_t nsapi);
/* Find a comp_index for a given pcomp/dcomp value */
uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp);
/* Find a pcomp/dcomp value for a given comp_index */
uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp_index);

View File

@ -0,0 +1,53 @@
/* GPRS SNDCP data compression handler */
/* (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/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gprs/sndcp/comp.h>
/* Note: The decompressed packet may have a maximum size of:
* Return value * MAX_DATADECOMPR_FAC */
#define MAX_DATADECOMPR_FAC 10
/* Note: In unacknowledged mode (SN_UNITDATA), the compression state is reset
* for every NPDU. The compressor needs a reasonably large payload to operate
* effectively (yield positive compression gain). For packets shorter than 100
* byte, no positive compression gain can be expected so we will skip the
* compression for short packets. */
#define MIN_COMPR_PAYLOAD 100
/* Initialize data compression */
int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field);
/* Terminate data compression */
void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity);
/* Expand packet */
int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities);
/* Compress packet */
int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi);

View File

@ -0,0 +1,46 @@
/* GPRS SNDCP header compression handler */
/* (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/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gprs/sndcp/comp.h>
/* Note: The decompressed packet may have a maximum size of:
* Return value + MAX_DECOMPR_INCR */
#define MAX_HDRDECOMPR_INCR 64
/* Initialize header compression */
int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field);
/* Terminate header compression */
void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity);
/* Expand packet header */
int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities);
/* Compress packet header */
int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi);

View File

@ -0,0 +1,187 @@
#ifndef _SLHC_H
#define _SLHC_H
/*
* Definitions for tcp compression routines.
*
* $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
* - Initial distribution.
*
*
* modified for KA9Q Internet Software Package by
* Katie Stevens (dkstevens@ucdavis.edu)
* University of California, Davis
* Computing Services
* - 01-31-90 initial adaptation
*
* - Feb 1991 Bill_Simpson@um.cc.umich.edu
* variable number of conversation slots
* allow zero or one slots
* separate routines
* status display
*/
/*
* Compressed packet format:
*
* The first octet contains the packet type (top 3 bits), TCP
* 'push' bit, and flags that indicate which of the 4 TCP sequence
* numbers have changed (bottom 5 bits). The next octet is a
* conversation number that associates a saved IP/TCP header with
* the compressed packet. The next two octets are the TCP checksum
* from the original datagram. The next 0 to 15 octets are
* sequence number changes, one change per bit set in the header
* (there may be no changes and there are two special cases where
* the receiver implicitly knows what changed -- see below).
*
* There are 5 numbers which can change (they are always inserted
* in the following order): TCP urgent pointer, window,
* acknowledgment, sequence number and IP ID. (The urgent pointer
* is different from the others in that its value is sent, not the
* change in value.) Since typical use of SLIP links is biased
* toward small packets (see comments on MTU/MSS below), changes
* use a variable length coding with one octet for numbers in the
* range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
* range 256 - 65535 or 0. (If the change in sequence number or
* ack is more than 65535, an uncompressed packet is sent.)
*/
/*
* Packet types (must not conflict with IP protocol version)
*
* The top nibble of the first octet is the packet type. There are
* three possible types: IP (not proto TCP or tcp with one of the
* control flags set); uncompressed TCP (a normal IP/TCP packet but
* with the 8-bit protocol field replaced by an 8-bit connection id --
* this type of packet syncs the sender & receiver); and compressed
* TCP (described above).
*
* LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
* is logically part of the 4-bit "changes" field that follows. Top
* three bits are actual packet type. For backward compatibility
* and in the interest of conserving bits, numbers are chosen so the
* IP protocol version number (4) which normally appears in this nibble
* means "IP packet".
*/
#include <linux/ip.h>
#include <linux/tcp.h>
/* SLIP compression masks for len/vers byte */
#define SL_TYPE_IP 0x40
#define SL_TYPE_UNCOMPRESSED_TCP 0x70
#define SL_TYPE_COMPRESSED_TCP 0x80
#define SL_TYPE_ERROR 0x00
/* Bits in first octet of compressed packet */
#define NEW_C 0x40 /* flag bits for what changed in a packet */
#define NEW_I 0x20
#define NEW_S 0x08
#define NEW_A 0x04
#define NEW_W 0x02
#define NEW_U 0x01
/* reserved, special-case values of above */
#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
#define TCP_PUSH_BIT 0x10
/*
* data type and sizes conversion assumptions:
*
* VJ code KA9Q style generic
* u_char byte_t unsigned char 8 bits
* u_short int16 unsigned short 16 bits
* u_int int16 unsigned short 16 bits
* u_long unsigned long unsigned long 32 bits
* int int32 long 32 bits
*/
typedef __u8 byte_t;
typedef __u32 int32;
/*
* "state" data for each active tcp conversation on the wire. This is
* basically a copy of the entire IP/TCP header from the last packet
* we saw from the conversation together with a small identifier
* the transmit & receive ends of the line use to locate saved header.
*/
struct cstate {
byte_t cs_this; /* connection id number (xmit) */
struct cstate *next; /* next in ring (xmit) */
struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
struct tcphdr cs_tcp;
unsigned char cs_ipopt[64];
unsigned char cs_tcpopt[64];
int cs_hsize;
};
#define NULLSLSTATE (struct cstate *)0
/*
* all the state data for one serial line (we need one of these per line).
*/
struct slcompress {
struct cstate *tstate; /* transmit connection states (array)*/
struct cstate *rstate; /* receive connection states (array)*/
byte_t tslot_limit; /* highest transmit slot id (0-l)*/
byte_t rslot_limit; /* highest receive slot id (0-l)*/
byte_t xmit_oldest; /* oldest xmit in ring */
byte_t xmit_current; /* most recent xmit id */
byte_t recv_current; /* most recent rcvd id */
byte_t flags;
#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */
int32 sls_o_nontcp; /* outbound non-TCP packets */
int32 sls_o_tcp; /* outbound TCP packets */
int32 sls_o_uncompressed; /* outbound uncompressed packets */
int32 sls_o_compressed; /* outbound compressed packets */
int32 sls_o_searches; /* searches for connection state */
int32 sls_o_misses; /* times couldn't find conn. state */
int32 sls_i_uncompressed; /* inbound uncompressed packets */
int32 sls_i_compressed; /* inbound compressed packets */
int32 sls_i_error; /* inbound error packets */
int32 sls_i_tossed; /* inbound packets tossed because of error */
int32 sls_i_runt;
int32 sls_i_badcheck;
};
#define NULLSLCOMPR (struct slcompress *)0
/* In slhc.c: */
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,
unsigned char *ocp, unsigned char **cpp, int compress_cid);
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

@ -0,0 +1,16 @@
#pragma once
/* Subnetwork Dependent Convergence Protocol (SNDCP) definitions from 3GPP TS 44.065 */
#include <stdint.h>
#include <stddef.h>
int osmo_gprs_sndcp_init(void);
enum osmo_gprs_sndcp_log_cat {
OSMO_GPRS_SNDCP_LOGC_SNDCP,
OSMO_GPRS_SNDCP_LOGC_SLHC,
_OSMO_GPRS_SNDCP_LOGC_MAX,
};
void osmo_gprs_sndcp_set_log_cat(enum osmo_gprs_sndcp_log_cat logc, int logc_num);

View File

@ -0,0 +1,212 @@
#pragma once
/* 3GPP TS 44.065, section 5 "Service primitives and functions" */
#include <stdint.h>
#include <stddef.h>
#include <osmocom/core/prim.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/gprs/sndcp/sndcp.h>
struct osmo_gprs_llc_prim;
/* Section 5.1 "Service primitives" */
enum osmo_gprs_sndcp_prim_sap {
OSMO_GPRS_SNDCP_SAP_SN,
OSMO_GPRS_SNDCP_SAP_SNSM,
};
extern const struct value_string osmo_gprs_sndcp_prim_sap_names[];
static inline const char *osmo_gprs_sndcp_prim_sap_name(enum osmo_gprs_sndcp_prim_sap val)
{
return get_value_string(osmo_gprs_sndcp_prim_sap_names, val);
}
/* Table 1: SNDCP layer service primitives */
enum osmo_gprs_sndcp_sn_prim_type {
OSMO_GPRS_SNDCP_SN_DATA, /* Req/Ind: N-PDU, NSAPI, N-PDU Number*/
OSMO_GPRS_SNDCP_SN_UNITDATA, /* Req/Ind: N-PDU, NSAPI */
OSMO_GPRS_SNDCP_SN_XID, /* Req/Ind/Resp/Cnf: Req/Neg SNDCP XID Parameters */
};
extern const struct value_string osmo_gprs_sndcp_sn_prim_type_names[];
static inline const char *osmo_gprs_sndcp_sn_prim_type_name(enum osmo_gprs_sndcp_sn_prim_type val)
{
return get_value_string(osmo_gprs_sndcp_sn_prim_type_names, val);
}
/* Table 2: Service primitives used by the SNDCP entity */
enum osmo_gprs_sndcp_snsm_prim_type {
OSMO_GPRS_SNDCP_SNSM_ACTIVATE, /* Ind/Resp: TLLI, NSAPI, QoS profile, SAPI, Radio Priority */
OSMO_GPRS_SNDCP_SNSM_DEACTIVATE, /* Ind/Resp: TLLI, NSAPI(s), LLC Release Indicator, XID Negotiation Indicator */
OSMO_GPRS_SNDCP_SNSM_MODIFY, /* Ind/Resp: TLLI, NSAPI, QoS Profile, SAPI, Radio Priority, Send N-PDU Number, Receive N-PDU Number */
OSMO_GPRS_SNDCP_SNSM_STATUS, /* Req: TLLI, SAPI, Cause */
OSMO_GPRS_SNDCP_SNSM_SEQUENCE, /* Ind/Resp: TLLI, NSAPI, Receive N-PDU Number */
OSMO_GPRS_SNDCP_SNSM_STOP_ASSIGN, /* Ind/Resp: TLLI, NSAPI */
};
extern const struct value_string osmo_gprs_sndcp_snsm_prim_type_names[];
static inline const char *osmo_gprs_sndcp_snsm_prim_type_name(enum osmo_gprs_sndcp_snsm_prim_type val)
{
return get_value_string(osmo_gprs_sndcp_snsm_prim_type_names, val);
}
/* Parameters for OSMO_GPRS_SNDCP_SN_* prims */
struct osmo_gprs_sndcp_sn_prim {
/* Common fields */
uint32_t tlli;
uint8_t sapi; /* llc */
/* Specific fields */
union {
/* OSMO_GPRS_SNDCP_SN_DATA | Req */
struct {
uint8_t nsapi;
uint8_t *npdu;
size_t npdu_len;
uint32_t npdu_number;
} data_req;
/* OSMO_GPRS_SNDCP_SN_DATA | Ind */
struct {
uint8_t nsapi;
uint8_t *npdu;
size_t npdu_len;
} data_ind;
/* OSMO_GPRS_SNDCP_SN_UNITDATA | Req */
struct {
uint8_t nsapi;
uint8_t *npdu;
size_t npdu_len;
} unitdata_req;
/* OSMO_GPRS_SNDCP_SN_UNITDATA | Ind */
struct {
uint8_t nsapi;
uint8_t *npdu;
size_t npdu_len;
} unitdata_ind;
/* OSMO_GPRS_SNDCP_SN_XID | Req */
struct {
uint8_t nsapi;
struct {
bool active;
bool passive;
int s01;
} pcomp_rfc1144;
struct {
bool active;
bool passive;
int p0;
int p1;
int p2;
} dcomp_v42bis;
} xid_req;
/* OSMO_GPRS_SNDCP_SN_XID | Ind */
struct {
uint8_t *req_xid;
uint32_t req_xid_len;
} xid_ind;
/* OSMO_GPRS_SNDCP_SN_XID | Rsp */
struct {
uint8_t nsapi;
} xid_rsp;
/* OSMO_GPRS_SNDCP_SN_XID | Cnf */
struct {
uint8_t *neg_xid;
uint32_t neg_xid_len;
} xid_cnf;
};
};
/* Parameters for OSMO_GPRS_SNDCP_SNSM_* prims */
struct osmo_gprs_sndcp_snsm_prim {
/* Common fields */
uint32_t tlli;
/* Specific fields */
union {
/* OSMO_GPRS_SNDCP_SNSM_ACTIVATE | Ind */
struct {
uint8_t nsapi;
uint8_t sapi;
uint8_t qos_params[3];
uint8_t radio_prio;
} activate_ind;
/* OSMO_GPRS_SNDCP_SNSM_ACTIVATE | Rsp */
struct {
uint8_t nsapi;
} activate_rsp;
/* OSMO_GPRS_SNDCP_SNSM_DEACTIVATE | Req */
struct {
uint8_t nsapi;
/* TODO: LLC Release Indicator,
XID Negotiation Indicator
*/
} deactivate_ind;
/* OSMO_GPRS_SNDCP_SNSM_DEACTIVATE | Rsp */
struct {
uint8_t nsapi;
} deactivate_rsp;
/* OSMO_GPRS_SNDCP_SNSM_MODIFY | Ind */
struct {
uint8_t nsapi;
uint8_t sapi;
uint8_t qos_params[3];
uint8_t radio_prio;
unsigned int tx_npdu_nr;
unsigned int rx_npdu_nr;
} modify_ind;
/* OSMO_GPRS_SNDCP_SNSM_MODIFY | Rsp */
struct {
uint8_t nsapi;
} modify_rsp;
/* OSMO_GPRS_SNDCP_SNSM_STATUS| Req */
struct {
uint8_t sapi;
uint8_t cause;
} status_req;
/* OSMO_GPRS_SNDCP_SNSM_SEQUENCE | Ind */
struct {
uint8_t nsapi;
unsigned int rx_npdu_nr;
} sequence_ind;
/* OSMO_GPRS_SNDCP_SNSM_SEQUENCE | Rsp */
struct {
uint8_t nsapi;
} sequence_rsp;
/* OSMO_GPRS_SNDCP_SNSM_STOP_ASSIGN | Ind */
struct {
uint8_t nsapi;
} stop_assign_ind;
};
};
struct osmo_gprs_sndcp_prim {
struct osmo_prim_hdr oph;
union {
struct osmo_gprs_sndcp_sn_prim sn;
struct osmo_gprs_sndcp_snsm_prim snsm;
};
};
typedef int (*osmo_gprs_sndcp_prim_up_cb)(struct osmo_gprs_sndcp_prim *sndcp_prim, void *up_user_data);
void osmo_gprs_sndcp_prim_set_up_cb(osmo_gprs_sndcp_prim_up_cb up_cb, void *up_user_data);
typedef int (*osmo_gprs_sndcp_prim_down_cb)(struct osmo_gprs_llc_prim *llc_prim, void *down_user_data);
void osmo_gprs_sndcp_prim_set_down_cb(osmo_gprs_sndcp_prim_down_cb down_cb, void *down_user_data);
typedef int (*osmo_gprs_sndcp_prim_snsm_cb)(struct osmo_gprs_sndcp_prim *sndcp_prim, void *snsm_user_data);
void osmo_gprs_sndcp_prim_set_snsm_cb(osmo_gprs_sndcp_prim_snsm_cb snsm_cb, void *snsm_user_data);
int osmo_gprs_sndcp_prim_upper_down(struct osmo_gprs_sndcp_prim *sndcp_prim);
int osmo_gprs_sndcp_prim_lower_up(struct osmo_gprs_llc_prim *llc_prim);
int osmo_gprs_sndcp_prim_dispatch_snsm(struct osmo_gprs_sndcp_prim *sndcp_prim);
const char *osmo_gprs_sndcp_prim_name(const struct osmo_gprs_sndcp_prim *sndcp_prim);
/* Alloc primitive for SN SAP: */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_data_req(uint32_t tlli, uint8_t sapi, uint8_t nsapi, uint8_t *npdu, size_t npdu_len);
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_unitdata_req(uint32_t tlli, uint8_t sapi, uint8_t nsapi, uint8_t *npdu, size_t npdu_len);
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_xid_req(uint32_t tlli, uint8_t sapi, uint8_t nsapi);
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_xid_rsp(uint32_t tlli, uint8_t sapi, uint8_t nsapi);
/* Alloc primitive for SNSM SAP: */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_snsm_activate_ind(uint32_t tlli, uint8_t nsapi, uint8_t sapi);
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_snsm_deactivate_ind(uint32_t tlli, uint8_t nsapi);

View File

@ -0,0 +1,220 @@
#pragma once
/* 3GPP TS 44.065, private header */
#include <stdint.h>
#include <stddef.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/sndcp/sndcp.h>
#include <osmocom/gprs/sndcp/sndcp_prim.h>
#include <osmocom/gprs/sndcp/comp.h>
#include <osmocom/gprs/sndcp/pcomp.h>
#include <osmocom/gprs/sndcp/dcomp.h>
extern int g_sndcp_log_cat[_OSMO_GPRS_SNDCP_LOGC_MAX];
#define LOGSNDCP(lvl, fmt, args...) LOGP(g_sndcp_log_cat[OSMO_GPRS_SNDCP_LOGC_SNDCP], lvl, fmt, ## args)
#define msgb_sndcp_prim(msg) ((struct osmo_gprs_sndcp_prim *)(msg)->l1h)
/* Chapter 7.2: SN-PDU Formats */
struct sndcp_common_hdr {
#if OSMO_IS_LITTLE_ENDIAN
/* octet 1 */
uint8_t nsapi:4;
uint8_t more:1;
uint8_t type:1;
uint8_t first:1;
uint8_t spare:1;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
uint8_t spare:1, first:1, type:1, more:1, nsapi:4;
#endif
} __attribute__((packed));
/* PCOMP / DCOMP only exist in first fragment */
struct sndcp_comp_hdr {
#if OSMO_IS_LITTLE_ENDIAN
/* octet 2 */
uint8_t pcomp:4;
uint8_t dcomp:4;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
uint8_t dcomp:4, pcomp:4;
#endif
} __attribute__((packed));
struct sndcp_udata_hdr {
#if OSMO_IS_LITTLE_ENDIAN
/* octet 3 */
uint8_t npdu_high:4;
uint8_t seg_nr:4;
/* octet 4 */
uint8_t npdu_low;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
uint8_t seg_nr:4, npdu_high:4;
uint8_t npdu_low;
#endif
} __attribute__((packed));
/* A fragment queue entry, containing one framgent of a N-PDU */
struct defrag_queue_entry {
struct llist_head list;
/* segment number of this fragment */
uint32_t seg_nr;
/* length of the data area of this fragment */
uint32_t data_len;
/* pointer to the data of this fragment */
uint8_t *data;
};
/* TODO: this needs to be set through API or VTY: */
struct gprs_sndcp_ctx_cfg {
bool pcomp_rfc1144_passive_accept;
bool dcomp_v42bis_passive_accept;
};
struct gprs_sndcp_ctx {
osmo_gprs_sndcp_prim_up_cb sndcp_up_cb;
void *sndcp_up_cb_user_data;
osmo_gprs_sndcp_prim_down_cb sndcp_down_cb;
void *sndcp_down_cb_user_data;
osmo_gprs_sndcp_prim_snsm_cb sndcp_snsm_cb;
void *sndcp_snsm_cb_user_data;
struct llist_head snme_list; /* list of struct gprs_sndcp_mgmt_entity->list */
struct gprs_sndcp_ctx_cfg cfg;
};
extern struct gprs_sndcp_ctx *g_ctx;
/* A fragment queue header, maintaining list of fragments for one N-PDU */
struct gprs_sndcp_defrag_state {
/* PDU number for which the defragmentation state applies */
uint16_t npdu;
/* highest segment number we have received so far */
uint8_t highest_seg;
/* bitmask of the segments we already have */
uint32_t seg_have;
/* do we still expect more segments? */
unsigned int no_more;
/* total length of all segments together */
unsigned int tot_len;
/* linked list of defrag_queue_entry: one for each fragment */
struct llist_head frag_list;
struct osmo_timer_list timer;
/* Holds state to know which compression mode is used
* when the packet is re-assembled */
uint8_t pcomp;
uint8_t dcomp;
/* Holds the pointers to the compression entity list
* that is used when the re-assembled packet is decompressed */
struct llist_head *proto;
struct llist_head *data;
};
/* See 6.7.1.2 Reassembly */
enum gprs_sndcp_rx_state {
GPRS_SNDCP_RX_S_FIRST,
GPRS_SNDCP_RX_S_SUBSEQ,
GPRS_SNDCP_RX_S_DISCARD,
};
#define GPRS_SNDCP_NUM_NSAPIS 16
/* SNDCP entity: One per TLLI + NSAPI */
struct gprs_sndcp_mgmt_entity;
struct gprs_sndcp_entity {
struct gprs_sndcp_mgmt_entity *snme; /* backpointer */
/* FIXME: move this RA_ID up to the LLME or even higher */
//struct gprs_ra_id ra_id;
/* reference to the LLC Entity below this SNDCP entity */
uint8_t llc_sapi;
/* The NSAPI we shall use on top of LLC */
uint8_t nsapi;
/* NPDU number for the GTP->SNDCP side */
uint16_t tx_npdu_nr;
/* SNDCP eeceiver state */
enum gprs_sndcp_rx_state rx_state;
/* The defragmentation queue */
struct gprs_sndcp_defrag_state defrag;
/* Copy of the XID fields array we have sent with the last
* originated XID-Request. NULL if not existing (and l3xid_req_len = 0) */
uint8_t *l3xid_req;
unsigned int l3xid_req_len;
/* TODO: taken from lle.params and not yet set ever in code! */
uint16_t n201_u;
uint16_t n201_i;
};
/* SNDCP management entity: One per TLLI */
struct gprs_sndcp_mgmt_entity {
struct llist_head list; /* item in (struct gprs_sndcp_ctx)->snme_list */
uint32_t tlli;
struct gprs_sndcp_entity *sne[GPRS_SNDCP_NUM_NSAPIS];
/* Compression entities */
struct {
/* In these two list_heads we will store the
* data and protocol compression entities,
* together with their compression states */
struct llist_head *proto;
struct llist_head *data;
} comp;
};
static inline struct gprs_sndcp_entity *gprs_sndcp_snme_get_sne(struct gprs_sndcp_mgmt_entity *snme,
uint8_t nsapi) {
OSMO_ASSERT(nsapi < GPRS_SNDCP_NUM_NSAPIS);
return snme->sne[nsapi];
}
/* sndcp_prim.c: */
struct osmo_gprs_sndcp_prim *gprs_sndcp_prim_alloc_sn_unitdata_ind(uint32_t tlli, uint8_t sapi, uint8_t nsapi, uint8_t *npdu, size_t npdu_len);
struct osmo_gprs_sndcp_prim *gprs_sndcp_prim_alloc_snsm_activate_rsp(uint32_t tlli, uint8_t nsapi);
int gprs_sndcp_prim_call_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim);
int gprs_sndcp_prim_call_down_cb(struct osmo_gprs_llc_prim *llc_prim);
/* sndcp.c: */
struct gprs_sndcp_mgmt_entity *gprs_sndcp_snme_alloc(uint32_t tlli);
struct gprs_sndcp_mgmt_entity *gprs_sndcp_snme_find_by_tlli(uint32_t tlli);
struct gprs_sndcp_entity *gprs_sndcp_sne_alloc(struct gprs_sndcp_mgmt_entity *snme, uint8_t llc_sapi, uint8_t nsapi);
void gprs_sndcp_sne_free(struct gprs_sndcp_entity *sne);
struct gprs_sndcp_entity *gprs_sndcp_sne_by_dlci_nsapi(uint32_t tlli, uint8_t llc_sapi, uint8_t nsapi);
int gprs_sndcp_sne_handle_llc_ll_unitdata_ind(struct gprs_sndcp_entity *sne,
struct sndcp_common_hdr *sch, uint16_t len);
int gprs_sndcp_snme_handle_llc_ll_xid_ind(struct gprs_sndcp_mgmt_entity *snme, uint32_t sapi, uint8_t *l3params, unsigned int l3params_len);
int gprs_sndcp_snme_handle_llc_ll_xid_cnf(struct gprs_sndcp_mgmt_entity *snme, uint32_t sapi, uint8_t *l3params, unsigned int l3params_len);
int gprs_sndcp_sne_handle_sn_unitdata_req(struct gprs_sndcp_entity *sne, uint8_t *npdu, unsigned int npdu_len);
int gprs_sndcp_sne_handle_sn_xid_req(struct gprs_sndcp_entity *sne, const struct osmo_gprs_sndcp_prim *sndcp_prim);
#define LOGSNME(snme, level, fmt, args...) \
LOGSNDCP(level, "SNME(%08x) " fmt, \
(snme)->tlli, \
## args)
#define LOGSNE(sne, level, fmt, args...) \
LOGSNDCP(level, "SNE(%08x,%s,%u) " fmt, \
(sne)->snme->tlli, \
osmo_gprs_llc_sapi_name((sne)->llc_sapi), \
(sne)->nsapi, \
## args)

View File

@ -0,0 +1,141 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* v42bis.h
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*/
/*! \page v42bis_page V.42bis modem data compression
\section v42bis_page_sec_1 What does it do?
The v.42bis specification defines a data compression scheme, to work in
conjunction with the error correction scheme defined in V.42.
\section v42bis_page_sec_2 How does it work?
*/
#include <stdint.h>
#if !defined(_SPANDSP_V42BIS_H_)
#define _SPANDSP_V42BIS_H_
#define SPAN_DECLARE(x) x
#define V42BIS_MIN_STRING_SIZE 6
#define V42BIS_MAX_STRING_SIZE 250
#define V42BIS_MIN_DICTIONARY_SIZE 512
#define V42BIS_MAX_BITS 12
#define V42BIS_MAX_CODEWORDS 4096 /* 2^V42BIS_MAX_BITS */
#define V42BIS_MAX_OUTPUT_LENGTH 1024
enum {
V42BIS_P0_NEITHER_DIRECTION = 0,
V42BIS_P0_INITIATOR_RESPONDER,
V42BIS_P0_RESPONDER_INITIATOR,
V42BIS_P0_BOTH_DIRECTIONS
};
enum {
V42BIS_COMPRESSION_MODE_DYNAMIC = 0,
V42BIS_COMPRESSION_MODE_ALWAYS,
V42BIS_COMPRESSION_MODE_NEVER
};
typedef void (*put_msg_func_t)(void *user_data, const uint8_t *msg, int len);
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
single instance of V.42bis compress/decompression.
*/
typedef struct v42bis_state_s v42bis_state_t;
#if defined(__cplusplus)
extern "C"
{
#endif
/*! Compress a block of octets.
\param s The V.42bis context.
\param buf The data to be compressed.
\param len The length of the data buffer.
\return 0 */
SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in a compression buffer.
\param s The V.42bis context.
\return 0 */
SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s);
/*! Decompress a block of octets.
\param s The V.42bis context.
\param buf The data to be decompressed.
\param len The length of the data buffer.
\return 0 */
SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in the decompression buffer.
\param s The V.42bis context.
\return 0 */
SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s);
/*! Set the compression mode.
\param s The V.42bis context.
\param mode One of the V.42bis compression modes -
V42BIS_COMPRESSION_MODE_DYNAMIC,
V42BIS_COMPRESSION_MODE_ALWAYS,
V42BIS_COMPRESSION_MODE_NEVER */
SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode);
/*! Initialise a V.42bis context.
\param s The V.42bis context.
\param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec.
\param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec.
\param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec.
\param encode_handler Encode callback handler.
\param encode_user_data An opaque pointer passed to the encode callback handler.
\param max_encode_len The maximum length that should be passed to the encode handler.
\param decode_handler Decode callback handler.
\param decode_user_data An opaque pointer passed to the decode callback handler.
\param max_decode_len The maximum length that should be passed to the decode handler.
\return The V.42bis context. */
SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
v42bis_state_t *s,
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
put_msg_func_t encode_handler,
void *encode_user_data,
int max_encode_len,
put_msg_func_t decode_handler,
void *decode_user_data,
int max_decode_len);
/*! Release a V.42bis context.
\param s The V.42bis context.
\return 0 if OK */
SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s);
/*! Free a V.42bis context.
\param s The V.42bis context.
\return 0 if OK */
SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s);
#if defined(__cplusplus)
}
#endif
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,119 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* private/v42bis.h
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*/
#if !defined(_SPANDSP_PRIVATE_V42BIS_H_)
#define _SPANDSP_PRIVATE_V42BIS_H_
/*!
V.42bis dictionary node.
Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used
as a "no such value" marker in this structure.
*/
typedef struct {
/*! \brief The value of the octet represented by the current dictionary node */
uint8_t node_octet;
/*! \brief The parent of this node */
uint16_t parent;
/*! \brief The first child of this node */
uint16_t child;
/*! \brief The next node at the same depth */
uint16_t next;
} v42bis_dict_node_t;
/*!
V.42bis compression or decompression. This defines the working state for a single instance
of V.42bis compression or decompression.
*/
typedef struct {
/*! \brief Compression enabled. */
int v42bis_parm_p0;
/*! \brief Compression mode. */
int compression_mode;
/*! \brief Callback function to handle output data. */
put_msg_func_t handler;
/*! \brief An opaque pointer passed in calls to the data handler. */
void *user_data;
/*! \brief The maximum amount to be passed to the data handler. */
int max_output_len;
/*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */
int transparent;
/*! \brief Next empty dictionary entry */
uint16_t v42bis_parm_c1;
/*! \brief Current codeword size */
uint16_t v42bis_parm_c2;
/*! \brief Threshold for codeword size change */
uint16_t v42bis_parm_c3;
/*! \brief The current update point in the dictionary */
uint16_t update_at;
/*! \brief The last entry matched in the dictionary */
uint16_t last_matched;
/*! \brief The last entry added to the dictionary */
uint16_t last_added;
/*! \brief Total number of codewords in the dictionary */
int v42bis_parm_n2;
/*! \brief Maximum permitted string length */
int v42bis_parm_n7;
/*! \brief The dictionary */
v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
/*! \brief The octet string in progress */
uint8_t string[V42BIS_MAX_STRING_SIZE];
/*! \brief The current length of the octet string in progress */
int string_length;
/*! \brief The amount of the octet string in progress which has already
been flushed out of the buffer */
int flushed_length;
/*! \brief Compression performance metric */
uint16_t compression_performance;
/*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */
uint32_t bit_buffer;
/*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */
int bit_count;
/*! \brief The output composition buffer */
uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH];
/*! \brief The length of the contents of the output composition buffer */
int output_octet_count;
/*! \brief The current value of the escape code */
uint8_t escape_code;
/*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */
int escaped;
} v42bis_comp_state_t;
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
single instance of V.42bis compress/decompression.
*/
struct v42bis_state_s {
/*! \brief Compression state. */
v42bis_comp_state_t compress;
/*! \brief Decompression state. */
v42bis_comp_state_t decompress;
/*! \brief Error and flow logging control */
};
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,224 @@
/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */
/* (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/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#define DEFAULT_SNDCP_VERSION 0 /* See 3GPP TS 44.065, clause 8 */
#define MAX_ENTITIES 32 /* 3GPP TS 44.065 reserves 5 bit
* for compression enitity number */
#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */
#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */
#define MAX_ROHC 16 /* Maximum number of ROHC compression profiles */
/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_hdr_comp_algo {
RFC_1144, /* TCP/IP header compression, see also 6.5.2 */
RFC_2507, /* TCP/UDP/IP header compression, see also: 6.5.3 */
ROHC /* Robust Header Compression, see also 6.5.4 */
};
/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_data_comp_algo {
V42BIS, /* V.42bis data compression, see also 6.6.2 */
V44 /* V44 data compression, see also: 6.6.3 */
};
union gprs_sndcp_comp_algo {
enum gprs_sndcp_hdr_comp_algo pcomp;
enum gprs_sndcp_data_comp_algo dcomp;
};
/* According to: 3GPP TS 44.065, 6.5.1.1 Format of the protocol control
* information compression field (Figure 7) and 3GPP TS 44.065,
* 6.6.1.1 Format of the data compression field (Figure 9) */
struct gprs_sndcp_comp_field {
struct llist_head list;
/* Propose bit (P), see also: 6.5.1.1.2 and 6.6.1.1.2 */
unsigned int p;
/* Entity number, see also: 6.5.1.1.3 and 6.6.1.1.3 */
unsigned int entity;
/* Algorithm identifier, see also: 6.5.1.1.4 and 6.6.1.1.4 */
union gprs_sndcp_comp_algo algo;
/* Number of contained PCOMP / DCOMP values */
uint8_t comp_len;
/* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */
uint8_t comp[MAX_COMP];
/* Note: Only one of the following struct pointers may,
be used. Unused pointers must be set to NULL! */
struct gprs_sndcp_pcomp_rfc1144_params *rfc1144_params;
struct gprs_sndcp_pcomp_rfc2507_params *rfc2507_params;
struct gprs_sndcp_pcomp_rohc_params *rohc_params;
struct gprs_sndcp_dcomp_v42bis_params *v42bis_params;
struct gprs_sndcp_dcomp_v44_params *v44_params;
};
/* According to: 3GPP TS 44.065, 8 SNDCP XID parameters */
enum gprs_sndcp_xid_param_types {
SNDCP_XID_VERSION_NUMBER,
SNDCP_XID_DATA_COMPRESSION, /* See also: subclause 6.6.1 */
SNDCP_XID_PROTOCOL_COMPRESSION, /* See also: subclause 6.5.1 */
SNDCP_XID_INVALID_COMPRESSION /* Not part of the spec; this means we found an invalid value */
};
/* According to: 3GPP TS 44.065, 6.5.2.1 Parameters (Table 5) */
struct gprs_sndcp_pcomp_rfc1144_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int s01; /* (default 15) */
};
/* According to: 3GPP TS 44.065, 6.5.2.2 Assignment of PCOMP values */
enum gprs_sndcp_pcomp_rfc1144_pcomp {
RFC1144_PCOMP1, /* Uncompressed TCP */
RFC1144_PCOMP2, /* Compressed TCP */
RFC1144_PCOMP_NUM /* Number of pcomp values */
};
/* According to: 3GPP TS 44.065, 6.5.3.1 Parameters (Table 6) */
struct gprs_sndcp_pcomp_rfc2507_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int f_max_period; /* (default 256) */
int f_max_time; /* (default 5) */
int max_header; /* (default 168) */
int tcp_space; /* (default 15) */
int non_tcp_space; /* (default 15) */
};
/* According to: 3GPP TS 44.065, 6.5.3.2 Assignment of PCOMP values for RFC2507 */
enum gprs_sndcp_pcomp_rfc2507_pcomp {
RFC2507_PCOMP1, /* Full Header */
RFC2507_PCOMP2, /* Compressed TCP */
RFC2507_PCOMP3, /* Compressed TCP non delta */
RFC2507_PCOMP4, /* Compressed non TCP */
RFC2507_PCOMP5, /* Context state */
RFC2507_PCOMP_NUM /* Number of pcomp values */
};
/* According to: 3GPP TS 44.065, 6.5.4.1 Parameter (Table 10) */
struct gprs_sndcp_pcomp_rohc_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int max_cid; /* (default 15) */
int max_header; /* (default 168) */
uint8_t profile_len; /* (default 1) */
uint16_t profile[MAX_ROHC]; /* (default 0, ROHC uncompressed) */
};
/* According to: 3GPP TS 44.065, 6.5.4.2 Assignment of PCOMP values for ROHC */
enum gprs_sndcp_pcomp_rohc_pcomp {
ROHC_PCOMP1, /* ROHC small CIDs */
ROHC_PCOMP2, /* ROHC large CIDs */
ROHC_PCOMP_NUM /* Number of pcomp values */
};
/* ROHC compression profiles, see also:
http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.xhtml */
enum gprs_sndcp_xid_rohc_profiles {
ROHC_UNCOMPRESSED = 0x0000, /* ROHC uncompressed [RFC5795] */
ROHC_RTP = 0x0001, /* ROHC RTP [RFC3095] */
ROHCV2_RTP = 0x0101, /* ROHCv2 RTP [RFC5225] */
ROHC_UDP = 0x0002, /* ROHC UDP [RFC3095] */
ROHCv2_UDP = 0x0102, /* ROHCv2 UDP [RFC5225] */
ROHC_ESP = 0x0003, /* ROHC ESP [RFC3095] */
ROHCV2_ESP = 0x0103, /* ROHCv2 ESP [RFC5225] */
ROHC_IP = 0x0004, /* ROHC IP [RFC3843] */
ROHCV2_IP = 0x0104, /* ROHCv2 IP [RFC5225] */
ROHC_LLA = 0x0005, /* ROHC LLA [RFC4362] */
ROHC_LLA_WITH_R_MODE = 0x0105, /* ROHC LLA with R-mode [RFC3408] */
ROHC_TCP = 0x0006, /* ROHC TCP [RFC6846] */
ROHC_RTP_UDP_LITE = 0x0007, /* ROHC RTP/UDP-Lite [RFC4019] */
ROHCV2_RTP_UDP_LITE = 0x0107, /* ROHCv2 RTP/UDP-Lite [RFC5225] */
ROHC_UDP_LITE = 0x0008, /* ROHC UDP-Lite [RFC4019] */
ROHCV2_UDP_LITE = 0x0108, /* ROHCv2 UDP-Lite [RFC5225] */
};
/* According to: 3GPP TS 44.065, 6.6.2.1 Parameters (Table 7a) */
struct gprs_sndcp_dcomp_v42bis_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int p0; /* (default 3) */
int p1; /* (default 2048) */
int p2; /* (default 20) */
};
/* According to: 3GPP TS 44.065, 6.6.2.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v42bis_dcomp {
V42BIS_DCOMP1, /* V.42bis enabled */
V42BIS_DCOMP_NUM /* Number of dcomp values */
};
/* According to: 3GPP TS 44.065, 6.6.3.1 Parameters (Table 7c) */
struct gprs_sndcp_dcomp_v44_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int c0; /* (default 10000000) */
int p0; /* (default 3) */
int p1t; /* Refer to subclause 6.6.3.1.4 */
int p1r; /* Refer to subclause 6.6.3.1.5 */
int p3t; /* (default 3 x p1t) */
int p3r; /* (default 3 x p1r) */
};
/* According to: 3GPP TS 44.065, 6.6.3.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v44_dcomp {
V44_DCOMP1, /* Packet method compressed */
V44_DCOMP2, /* Multi packet method compressed */
V44_DCOMP_NUM /* Number of dcomp values */
};
/* Transform a list with compression fields into an SNDCP-XID message (dst) */
int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen,
const struct llist_head *comp_fields, int version);
/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */
struct llist_head *gprs_sndcp_parse_xid(int *version,
const void *ctx,
const uint8_t *src,
unsigned int src_len,
const struct llist_head
*comp_fields_req);
/* Find out to which compression class the specified comp-field belongs
* (header compression or data compression?) */
enum gprs_sndcp_xid_param_types gprs_sndcp_get_compression_class(
const struct gprs_sndcp_comp_field *comp_field);
/* Dump a list with SNDCP-XID fields (Debug) */
void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields,
unsigned int logl);

12
libosmo-gprs-sndcp.pc.in Normal file
View File

@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: Osmocom [E]GPRS SNDCP (Subnetwork Dependent Convergence Protocol) Library
Description: C Utility Library
Version: @VERSION@
Requires: libosmocore libosmo-gprs-llc
Libs: -L${libdir} -losmo-gprs-sndcp
Libs.private: -lm -ltalloc
Cflags: -I${includedir}/

View File

@ -2,4 +2,5 @@ SUBDIRS = \
csn1 \
llc \
rlcmac \
sndcp \
$(NULL)

47
src/sndcp/Makefile.am Normal file
View File

@ -0,0 +1,47 @@
# This is _NOT_ the library release version, it's an API version.
# Please read Chapter 6 "Library interface versions" of the libtool
# documentation before making any modification
LIBVERSION=0:0:0
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(NULL)
lib_LTLIBRARIES = \
libosmo-gprs-sndcp.la \
$(NULL)
libosmo_gprs_sndcp_la_SOURCES = \
slhc.c \
sndcp.c \
sndcp_prim.c \
sndcp_comp.c \
sndcp_dcomp.c \
sndcp_pcomp.c \
sndcp_xid.c \
misc.c \
v42bis.c \
$(NULL)
libosmo_gprs_sndcp_la_LDFLAGS = \
-export-symbols-regex '^osmo_' \
-version-info $(LIBVERSION) \
-no-undefined \
$(NULL)
libosmo_gprs_sndcp_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(NULL)

31
src/sndcp/misc.c Normal file
View File

@ -0,0 +1,31 @@
/*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* 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 <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/gprs/sndcp/sndcp.h>
int g_sndcp_log_cat[_OSMO_GPRS_SNDCP_LOGC_MAX] = { [0 ... _OSMO_GPRS_SNDCP_LOGC_MAX - 1] = DLGLOBAL };
void osmo_gprs_sndcp_set_log_cat(enum osmo_gprs_sndcp_log_cat logc, int logc_num)
{
OSMO_ASSERT(logc < _OSMO_GPRS_SNDCP_LOGC_MAX);
g_sndcp_log_cat[logc] = logc_num;
}

815
src/sndcp/slhc.c Normal file
View File

@ -0,0 +1,815 @@
/*
* Routines to compress and uncompress tcp packets (for transmission
* over low speed serial lines).
*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
* - Initial distribution.
*
*
* modified for KA9Q Internet Software Package by
* Katie Stevens (dkstevens@ucdavis.edu)
* University of California, Davis
* Computing Services
* - 01-31-90 initial adaptation (from 1.19)
* PPP.05 02-15-90 [ks]
* PPP.08 05-02-90 [ks] use PPP protocol field to signal compression
* PPP.15 09-90 [ks] improve mbuf handling
* PPP.16 11-02 [karn] substantially rewritten to use NOS facilities
*
* - Feb 1991 Bill_Simpson@um.cc.umich.edu
* variable number of conversation slots
* allow zero or one slots
* separate routines
* status display
* - Jul 1994 Dmitry Gorodchanin
* Fixes for memory leaks.
* - Oct 1994 Dmitry Gorodchanin
* Modularization.
* - Jan 1995 Bjorn Ekwall
* Use ip_fast_csum from ip.h
* - July 1995 Christos A. Polyzols
* Spotted bug in tcp option checking
*
*
* This module is a difficult issue. It's clearly inet code but it's also clearly
* driver code belonging close to PPP and SLIP
*/
#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 <osmocom/gprs/sndcp/slhc.h>
#include <osmocom/gprs/sndcp/sndcp_private.h>
#define ERR_PTR(x) x
#define LOGSLHC(lvl, fmt, args...) LOGSNDCP(lvl, fmt, ## args)
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(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 NULL;
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 = (struct cstate *) talloc_zero_size(ctx, rsize);
if (! comp->rstate)
goto out_free;
comp->rslot_limit = rslots - 1;
}
if (tslots > 0) {
size_t tsize = tslots * sizeof(struct cstate);
comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize);
if (! comp->tstate)
goto out_free2;
comp->tslot_limit = tslots - 1;
}
comp->xmit_oldest = 0;
comp->xmit_current = 255;
comp->recv_current = 255;
/*
* don't accept any packets with implicit index until we get
* one with an explicit index. Otherwise the uncompress code
* will try to use connection 255, which is almost certainly
* out of range
*/
comp->flags |= SLF_TOSS;
if ( tslots > 0 ) {
ts = comp->tstate;
for(i = comp->tslot_limit; i > 0; --i){
ts[i].cs_this = i;
ts[i].next = &(ts[i - 1]);
}
ts[0].next = &(ts[comp->tslot_limit]);
ts[0].cs_this = 0;
}
return comp;
out_free2:
talloc_free(comp->rstate);
out_free:
talloc_free(comp);
out_fail:
return NULL;
}
/* Free a compression data structure */
void
slhc_free(struct slcompress *comp)
{
LOGSLHC(LOGL_DEBUG, "slhc_free(): Freeing compression states...\n");
if ( comp == NULLSLCOMPR )
return;
if ( comp->tstate != NULLSLSTATE )
talloc_free(comp->tstate );
if ( comp->rstate != NULLSLSTATE )
talloc_free( comp->rstate );
talloc_free( comp );
}
/* Put a short in host order into a char array in network order */
static inline unsigned char *
put16(unsigned char *cp, unsigned short x)
{
*cp++ = x >> 8;
*cp++ = x;
return cp;
}
/* Encode a number */
static unsigned char *
encode(unsigned char *cp, unsigned short n)
{
if(n >= 256 || n == 0){
*cp++ = 0;
cp = put16(cp,n);
} else {
*cp++ = n;
}
LOGSLHC(LOGL_DEBUG, "encode(): n=%04x\n",n);
return cp;
}
/* Pull a 16-bit integer in host order from buffer in network byte order */
static unsigned short
pull16(unsigned char **cpp)
{
short rval;
rval = *(*cpp)++;
rval <<= 8;
rval |= *(*cpp)++;
return rval;
}
/* Decode a number */
static long
decode(unsigned char **cpp)
{
register int x;
x = *(*cpp)++;
if(x == 0){
return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
} else {
return x & 0xff; /* -1 if PULLCHAR returned error */
}
}
/*
* icp and isize are the original packet.
* ocp is a place to put a copy if necessary.
* cpp is initially a pointer to icp. If the copy is used,
* change it to ocp.
*/
int
slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
unsigned char *ocp, unsigned char **cpp, int compress_cid)
{
register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
register struct cstate *lcs = ocs;
register struct cstate *cs = lcs->next;
register unsigned long deltaS, deltaA;
register short changes = 0;
int hlen;
unsigned char new_seq[16];
register unsigned char *cp = new_seq;
struct iphdr *ip;
struct tcphdr *th, *oth;
__sum16 csum;
/*
* Don't play with runt packets.
*/
if(isize<sizeof(struct iphdr))
return isize;
ip = (struct iphdr *) icp;
/* Bail if this packet isn't TCP, or is an IP fragment */
if (ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) {
/* Send as regular IP */
if(ip->protocol != IPPROTO_TCP)
comp->sls_o_nontcp++;
else
comp->sls_o_tcp++;
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Not a TCP packat, will not touch...\n");
return isize;
}
/* Extract TCP header */
th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
hlen = ip->ihl*4 + th->doff*4;
/* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
* some other control bit is set). Also uncompressible if
* it's a runt.
*/
if(hlen > isize || th->syn || th->fin || th->rst ||
! (th->ack)){
/* TCP connection stuff; send as regular IP */
comp->sls_o_tcp++;
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n");
return isize;
}
/*
* Packet is compressible -- we're going to send either a
* COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way,
* we need to locate (or create) the connection state.
*
* States are kept in a circularly linked list with
* xmit_oldest pointing to the end of the list. The
* list is kept in lru order by moving a state to the
* head of the list whenever it is referenced. Since
* the list is short and, empirically, the connection
* we want is almost always near the front, we locate
* states via linear search. If we don't find a state
* for the datagram, the oldest state is (re-)used.
*/
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Compressible packet detected!\n");
for ( ; ; ) {
if( ip->saddr == cs->cs_ip.saddr
&& ip->daddr == cs->cs_ip.daddr
&& th->source == cs->cs_tcp.source
&& th->dest == cs->cs_tcp.dest)
goto found;
/* if current equal oldest, at end of list */
if ( cs == ocs )
break;
lcs = cs;
cs = cs->next;
comp->sls_o_searches++;
}
/*
* Didn't find it -- re-use oldest cstate. Send an
* uncompressed packet that tells the other side what
* connection number we're using for this conversation.
*
* Note that since the state list is circular, the oldest
* state points to the newest and we only need to set
* xmit_oldest to update the lru linkage.
*/
LOGSLHC(LOGL_DEBUG, "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:
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Header already seen, trying to compress...\n");
/*
* Found it -- move to the front on the connection list.
*/
if(lcs == ocs) {
/* found at most recently used */
} else if (cs == ocs) {
/* found at least recently used */
comp->xmit_oldest = lcs->cs_this;
} else {
/* more than 2 elements */
lcs->next = cs->next;
cs->next = ocs->next;
ocs->next = cs;
}
/*
* Make sure that only what we expect to change changed.
* Check the following:
* IP protocol version, header length & type of service.
* The "Don't fragment" bit.
* The time-to-live field.
* The TCP header length.
* IP options, if any.
* TCP options, if any.
* If any of these things are different between the previous &
* current datagram, we send the current datagram `uncompressed'.
*/
oth = &cs->cs_tcp;
/* Display a little more debug information about which of the
* header fields changed unexpectedly */
if(ip->version != cs->cs_ip.version)
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n");
if(ip->ihl != cs->cs_ip.ihl)
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n");
if(ip->tos != cs->cs_ip.tos)
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n");
if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)))
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n");
if(ip->ttl != cs->cs_ip.ttl)
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n");
if(th->doff != cs->cs_tcp.doff)
LOGSLHC(LOGL_DEBUG, "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) {
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
LOGSLHC(LOGL_DEBUG, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
LOGSLHC(LOGL_DEBUG, "slhc_compress(): ip+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
LOGSLHC(LOGL_DEBUG, "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) {
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
LOGSLHC(LOGL_DEBUG, "slhc_compress(): th->doff = %i\n", th->doff);
LOGSLHC(LOGL_DEBUG, "slhc_compress(): th+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
LOGSLHC(LOGL_DEBUG, "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))
|| ip->ttl != cs->cs_ip.ttl
|| 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)){
LOGSLHC(LOGL_DEBUG, "slhc_compress(): The header contains unexpected changes, can't compress...\n");
goto uncompressed;
}
/*
* Figure out which of the changing fields changed. The
* receiver expects changes in the order: urgent, window,
* ack, seq (the order minimizes the number of temporaries
* needed in this section of code).
*/
if(th->urg){
deltaS = ntohs(th->urg_ptr);
LOGSLHC(LOGL_DEBUG, "slhc_compress(): flag: Urgent Pointer (U) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_U;
} else if(th->urg_ptr != oth->urg_ptr){
/* argh! URG not set but urp changed -- a sensible
* implementation should never do this but RFC793
* doesn't prohibit the change so we have to deal
* with it. */
LOGSLHC(LOGL_DEBUG, "slhc_compress(): URG not set but urp changed, can't compress...\n");
goto uncompressed;
}
if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
LOGSLHC(LOGL_DEBUG, "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) {
LOGSLHC(LOGL_DEBUG, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n");
goto uncompressed;
}
LOGSLHC(LOGL_DEBUG, "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) {
LOGSLHC(LOGL_DEBUG, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n");
goto uncompressed;
}
LOGSLHC(LOGL_DEBUG, "slhc_compress(): flag: Delta Sequence (S) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_S;
}
switch(changes){
case 0: /* Nothing changed. If this packet contains data and the
* last one didn't, this is probably a data packet following
* an ack (normal on an interactive connection) and we send
* it compressed. Otherwise it's probably a retransmit,
* retransmitted ack or window probe. Send it uncompressed
* in case the other side missed the compressed version.
*/
if(ip->tot_len != cs->cs_ip.tot_len &&
ntohs(cs->cs_ip.tot_len) == hlen)
break;
LOGSLHC(LOGL_DEBUG, "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.
*/
LOGSLHC(LOGL_DEBUG, "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 */
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Special case for echoed terminal traffic detected...\n");
LOGSLHC(LOGL_DEBUG, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
changes = SPECIAL_I;
cp = new_seq;
}
break;
case NEW_S:
if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
/* special case for data xfer */
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Special case for data xfer detected...\n");
LOGSLHC(LOGL_DEBUG, "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;
}
break;
}
deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
if(deltaS != 1){
LOGSLHC(LOGL_DEBUG, "slhc_compress(): flag: Delta IP ID (I) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_I;
}
if(th->psh) {
LOGSLHC(LOGL_DEBUG, "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.
*/
csum = th->check;
memcpy(&cs->cs_ip,ip,20);
memcpy(&cs->cs_tcp,th,20);
/* We want to use the original packet as our compressed packet.
* (cp - new_seq) is the number of bytes we need for compressed
* sequence numbers. In addition we need one byte for the change
* mask, one for the connection id and two for the tcp checksum.
* So, (cp - new_seq) + 4 bytes of header are needed.
*/
deltaS = cp - new_seq;
if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
cp = ocp;
*cpp = ocp;
LOGSLHC(LOGL_DEBUG, "slhc_compress(): flag: Connection number (C) = 1\n");
*cp++ = changes | NEW_C;
*cp++ = cs->cs_this;
comp->xmit_current = cs->cs_this;
} else {
cp = ocp;
*cpp = ocp;
*cp++ = changes;
}
*(__sum16 *)cp = csum;
cp += 2;
/* deltaS is now the size of the change section of the compressed header */
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
LOGSLHC(LOGL_DEBUG, "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++;
ocp[0] |= SL_TYPE_COMPRESSED_TCP;
return isize - hlen + deltaS + (cp - ocp);
/* Update connection state cs & send uncompressed packet (i.e.,
* a regular ip/tcp packet but with the 'conversation id' we hope
* to use on future compressed packets in the protocol field).
*/
uncompressed:
LOGSLHC(LOGL_DEBUG, "slhc_compress(): Packet will be sent uncompressed...\n");
memcpy(&cs->cs_ip,ip,20);
memcpy(&cs->cs_tcp,th,20);
if (ip->ihl > 5)
memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
if (th->doff > 5)
memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
comp->xmit_current = cs->cs_this;
comp->sls_o_uncompressed++;
memcpy(ocp, icp, isize);
*cpp = ocp;
ocp[9] = cs->cs_this;
ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
return isize;
}
int
slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
{
register int changes;
long x;
register struct tcphdr *thp;
register struct iphdr *ip;
register struct cstate *cs;
int len, hdrlen;
unsigned char *cp = icp;
/* We've got a compressed packet; read the change byte */
comp->sls_i_compressed++;
if(isize < 3){
comp->sls_i_error++;
return 0;
}
changes = *cp++;
if(changes & NEW_C){
/* Make sure the state index is in range, then grab the state.
* If we have a good state index, clear the 'discard' flag.
*/
x = *cp++; /* Read conn index */
if(x < 0 || x > comp->rslot_limit)
goto bad;
comp->flags &=~ SLF_TOSS;
comp->recv_current = x;
} else {
/* this packet has an implicit state index. If we've
* had a line error since the last time we got an
* explicit state index, we have to toss the packet. */
if(comp->flags & SLF_TOSS){
comp->sls_i_tossed++;
return 0;
}
}
cs = &comp->rstate[comp->recv_current];
thp = &cs->cs_tcp;
ip = &cs->cs_ip;
thp->check = *(__sum16 *)cp;
cp += 2;
thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
/*
* we can use the same number for the length of the saved header and
* the current one, because the packet wouldn't have been sent
* as compressed unless the options were the same as the previous one
*/
hdrlen = ip->ihl * 4 + thp->doff * 4;
switch(changes & SPECIALS_MASK){
case SPECIAL_I: /* Echoed terminal traffic */
LOGSLHC(LOGL_DEBUG, "slhc_uncompress(): Echoed terminal traffic detected\n");
{
register short i;
i = ntohs(ip->tot_len) - hdrlen;
thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
thp->seq = htonl( ntohl(thp->seq) + i);
}
break;
case SPECIAL_D: /* Unidirectional data */
LOGSLHC(LOGL_DEBUG, "slhc_uncompress(): Unidirectional data detected\n");
thp->seq = htonl( ntohl(thp->seq) +
ntohs(ip->tot_len) - hdrlen);
break;
default:
LOGSLHC(LOGL_DEBUG, "slhc_uncompress(): default packet type detected\n");
if(changes & NEW_U){
thp->urg = 1;
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->urg_ptr = htons(x);
} else
thp->urg = 0;
if(changes & NEW_W){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->window = htons( ntohs(thp->window) + x);
}
if(changes & NEW_A){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
}
if(changes & NEW_S){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->seq = htonl( ntohl(thp->seq) + x);
}
break;
}
if(changes & NEW_I){
if((x = decode(&cp)) == -1) {
goto bad;
}
ip->id = htons (ntohs (ip->id) + x);
} else
ip->id = htons (ntohs (ip->id) + 1);
/*
* At this point, cp points to the first byte of data in the
* packet. Put the reconstructed TCP and IP headers back on the
* packet. Recalculate IP checksum (but not TCP checksum).
*/
len = isize - (cp - icp);
if (len < 0)
goto bad;
len += hdrlen;
ip->tot_len = htons(len);
ip->check = 0;
LOGSLHC(LOGL_DEBUG, "slhc_uncompress(): making space for the reconstructed header...\n");
memmove(icp + hdrlen, cp, len - hdrlen);
cp = icp;
memcpy(cp, ip, 20);
cp += 20;
if (ip->ihl > 5) {
memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
cp += (ip->ihl - 5) * 4;
}
put_unaligned(ip_fast_csum(icp, ip->ihl),
&((struct iphdr *)icp)->check);
memcpy(cp, thp, 20);
cp += 20;
if (thp->doff > 5) {
memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
cp += ((thp->doff) - 5) * 4;
}
return len;
bad:
LOGSLHC(LOGL_DEBUG, "slhc_uncompress(): bad packet detected!\n");
comp->sls_i_error++;
return slhc_toss( comp );
}
int
slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
{
register struct cstate *cs;
unsigned ihl;
unsigned char index;
if(isize < 20) {
/* The packet is shorter than a legal IP header */
comp->sls_i_runt++;
LOGSLHC(LOGL_DEBUG, "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 */
ihl = icp[0] & 0xf;
if(ihl < 20 / 4){
/* The IP header length field is too small */
comp->sls_i_runt++;
LOGSLHC(LOGL_DEBUG, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n");
return slhc_toss( comp );
}
index = icp[9];
icp[9] = IPPROTO_TCP;
if (ip_fast_csum(icp, ihl)) {
/* Bad IP header checksum; discard */
comp->sls_i_badcheck++;
LOGSLHC(LOGL_DEBUG, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n");
return slhc_toss( comp );
}
if(index > comp->rslot_limit) {
comp->sls_i_error++;
LOGSLHC(LOGL_DEBUG, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n");
return slhc_toss(comp);
}
/* Update local state */
cs = &comp->rstate[comp->recv_current = index];
comp->flags &=~ SLF_TOSS;
memcpy(&cs->cs_ip,icp,20);
memcpy(&cs->cs_tcp,icp + ihl*4,20);
if (ihl > 5)
memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
if (cs->cs_tcp.doff > 5)
memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
/* Put headers back on packet
* Neither header checksum is recalculated
*/
comp->sls_i_uncompressed++;
return isize;
}
int
slhc_toss(struct slcompress *comp)
{
LOGSLHC(LOGL_DEBUG, "slhc_toss(): Reset compression state...\n");
if ( comp == NULLSLCOMPR )
return 0;
comp->flags |= SLF_TOSS;
return 0;
}
void slhc_i_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
LOGSLHC(LOGL_DEBUG, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
comp->sls_i_compressed,
comp->sls_i_uncompressed,
comp->sls_i_error,
comp->sls_i_tossed);
}
}
void slhc_o_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
LOGSLHC(LOGL_DEBUG, "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);
}
}

1037
src/sndcp/sndcp.c Normal file

File diff suppressed because it is too large Load Diff

321
src/sndcp/sndcp_comp.c Normal file
View File

@ -0,0 +1,321 @@
/* GPRS SNDCP header compression entity management tools */
/* (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 <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/gprs/sndcp/sndcp_private.h>
/* Create a new compression entity from a XID-Field */
static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx,
const struct
gprs_sndcp_comp_field
*comp_field)
{
struct gprs_sndcp_comp *comp_entity;
comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp);
/* Copy relevant information from the SNDCP-XID field */
comp_entity->entity = comp_field->entity;
comp_entity->comp_len = comp_field->comp_len;
memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp));
if (comp_field->rfc1144_params) {
comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->rfc1144_params->nsapi,
sizeof(comp_entity->nsapi));
comp_entity->algo.pcomp = comp_field->algo.pcomp;
} else if (comp_field->rfc2507_params) {
comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->rfc2507_params->nsapi,
sizeof(comp_entity->nsapi));
comp_entity->algo.pcomp = comp_field->algo.pcomp;
} else if (comp_field->rohc_params) {
comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len;
memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi,
sizeof(comp_entity->nsapi));
comp_entity->algo.pcomp = comp_field->algo.pcomp;
} else if (comp_field->v42bis_params) {
comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->v42bis_params->nsapi,
sizeof(comp_entity->nsapi));
comp_entity->algo.dcomp = comp_field->algo.dcomp;
} else if (comp_field->v44_params) {
comp_entity->nsapi_len = comp_field->v44_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->v44_params->nsapi,
sizeof(comp_entity->nsapi));
comp_entity->algo.dcomp = comp_field->algo.dcomp;
} else {
/* The caller is expected to check carefully if the all
* data fields required for compression entity creation
* are present. Otherwise we blow an assertion here */
OSMO_ASSERT(false);
}
/* Check if an NSAPI is selected, if not, it does not make sense
* to create the compression entity, since the caller should
* have checked the presence of the NSAPI, we blow an assertion
* in case of missing NSAPIs */
OSMO_ASSERT(comp_entity->nsapi_len > 0);
/* Determine of which class our compression entity will be
* (Protocol or Data compresson ?) */
comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field);
/* Create an algorithm specific compression context */
switch (comp_entity->compclass) {
case SNDCP_XID_PROTOCOL_COMPRESSION:
if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
talloc_free(comp_entity);
comp_entity = NULL;
}
break;
case SNDCP_XID_DATA_COMPRESSION:
if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) {
talloc_free(comp_entity);
comp_entity = NULL;
}
break;
default:
/* comp_field is somehow invalid */
OSMO_ASSERT(false);
}
/* Bail on failure */
if (comp_entity == NULL) {
LOGSNDCP(LOGL_ERROR, "Compression entity creation failed!\n");
return NULL;
}
/* Display info message */
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGSNDCP(LOGL_INFO, "New header compression entity (%d) created.\n",
comp_entity->entity);
} else {
LOGSNDCP(LOGL_INFO, "New data compression entity (%d) created.\n",
comp_entity->entity);
}
return comp_entity;
}
/* Allocate a compression enitiy list */
struct llist_head *gprs_sndcp_comp_alloc(const void *ctx)
{
struct llist_head *lh;
lh = talloc_zero(ctx, struct llist_head);
INIT_LLIST_HEAD(lh);
return lh;
}
/* Free a compression entitiy list */
void gprs_sndcp_comp_free(struct llist_head *comp_entities)
{
struct gprs_sndcp_comp *comp_entity;
if (!comp_entities)
return;
llist_for_each_entry(comp_entity, comp_entities, list) {
/* Free compression entity */
switch (comp_entity->compclass) {
case SNDCP_XID_PROTOCOL_COMPRESSION:
LOGSNDCP(LOGL_INFO, "Deleting header compression entity %d ...\n",
comp_entity->entity);
gprs_sndcp_pcomp_term(comp_entity);
break;
case SNDCP_XID_DATA_COMPRESSION:
LOGSNDCP(LOGL_INFO, "Deleting data compression entity %d ...\n",
comp_entity->entity);
gprs_sndcp_dcomp_term(comp_entity);
break;
default:
LOGSNDCP(LOGL_INFO, "Invalid compression class %d!\n",
comp_entity->compclass);
OSMO_ASSERT(false);
}
}
talloc_free(comp_entities);
}
/* Delete a compression entity */
void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
unsigned int entity)
{
struct gprs_sndcp_comp *comp_entity;
struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
if (comp_entity->entity == entity) {
comp_entity_to_delete = comp_entity;
break;
}
}
if (!comp_entity_to_delete)
return;
if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGSNDCP(LOGL_INFO, "Deleting header compression entity %d ...\n",
comp_entity_to_delete->entity);
gprs_sndcp_pcomp_term(comp_entity_to_delete);
} else {
LOGSNDCP(LOGL_INFO, "Deleting data compression entity %d ...\n",
comp_entity_to_delete->entity);
}
/* Delete compression entity */
llist_del(&comp_entity_to_delete->list);
talloc_free(comp_entity_to_delete);
}
/* Create and Add a new compression entity
* (returns a pointer to the compression entity that has just been created) */
struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
struct llist_head *comp_entities,
const struct gprs_sndcp_comp_field
*comp_field)
{
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(comp_entities);
OSMO_ASSERT(comp_field);
/* Just to be sure, if the entity is already in
* the list it will be deleted now */
gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
/* Create and add a new entity to the list */
comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
if (!comp_entity)
return NULL;
llist_add(&comp_entity->list, comp_entities);
return comp_entity;
}
/* Find which compression entity handles the specified pcomp/dcomp */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
*comp_entities, uint8_t comp)
{
struct gprs_sndcp_comp *comp_entity;
int i;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
for (i = 0; i < comp_entity->comp_len; i++) {
if (comp_entity->comp[i] == comp)
return comp_entity;
}
}
LOGSNDCP(LOGL_ERROR, "Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
comp);
return NULL;
}
/* Find which compression entity handles the specified nsapi */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
*comp_entities, uint8_t nsapi)
{
struct gprs_sndcp_comp *comp_entity;
int i;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
for (i = 0; i < comp_entity->nsapi_len; i++) {
if (comp_entity->nsapi[i] == nsapi)
return comp_entity;
}
}
return NULL;
}
/* Find a comp_index for a given pcomp/dcomp value */
uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp)
{
/* Note: This function returns a normalized version of the comp value,
* which matches up with the position of the comp field. Since comp=0
* is reserved for "no compression", the index value starts counting
* at one. The return value is the PCOMPn/DCOMPn value one can find
* in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
int i;
OSMO_ASSERT(comp_entity);
/* A pcomp/dcomp value of zero is reserved for "no comproession",
* So we just bail and return zero in this case */
if (comp == 0)
return 0;
/* Look in the pcomp/dcomp list for the index */
for (i = 0; i < comp_entity->comp_len; i++) {
if (comp_entity->comp[i] == comp)
return i + 1;
}
LOGSNDCP(LOGL_ERROR, "Could not find a matching comp_index for given pcomp/dcomp value %d\n",
comp);
return 0;
}
/* Find a pcomp/dcomp value for a given comp_index */
uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp_index)
{
OSMO_ASSERT(comp_entity);
/* A comp_index of zero translates to zero right away. */
if (comp_index == 0)
return 0;
if (comp_index > comp_entity->comp_len) {
LOGSNDCP(LOGL_ERROR, "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
comp_index);
return 0;
}
/* Look in the pcomp/dcomp list for the comp_index, see
* note in gprs_sndcp_comp_get_idx() */
return comp_entity->comp[comp_index - 1];
}

342
src/sndcp/sndcp_dcomp.c Normal file
View File

@ -0,0 +1,342 @@
/* GPRS SNDCP data compression handler */
/* (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 <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gprs/sndcp/v42bis.h>
#include <osmocom/gprs/sndcp/v42bis_private.h>
#include <osmocom/gprs/sndcp/sndcp_private.h>
/* A struct to capture the output data of compressor and decompressor */
struct v42bis_output_buffer {
uint8_t *buf;
uint8_t *buf_pointer;
int len;
};
/* Handler to capture the output data from the compressor */
void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len)
{
struct v42bis_output_buffer *output_buffer =
(struct v42bis_output_buffer *)user_data;
memcpy(output_buffer->buf_pointer, pkt, len);
output_buffer->buf_pointer += len;
output_buffer->len += len;
}
/* Handler to capture the output data from the decompressor */
void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len)
{
struct v42bis_output_buffer *output_buffer =
(struct v42bis_output_buffer *)user_data;
memcpy(output_buffer->buf_pointer, buf, len);
output_buffer->buf_pointer += len;
output_buffer->len += len;
}
/* Initialize data compression */
int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a new data compression
* entity is created by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
OSMO_ASSERT(comp_field);
if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
&& comp_entity->algo.dcomp == V42BIS) {
OSMO_ASSERT(comp_field->v42bis_params);
comp_entity->state =
v42bis_init(ctx, NULL, comp_field->v42bis_params->p0,
comp_field->v42bis_params->p1,
comp_field->v42bis_params->p2,
&tx_v42bis_frame_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH,
&rx_v42bis_data_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH);
LOGSNDCP(LOGL_INFO, "V.42bis data compression initialized.\n");
return 0;
}
/* Just in case someone tries to initialize an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Terminate data compression */
void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a data compression
* entity is deleted by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
&& comp_entity->algo.dcomp == V42BIS) {
if (comp_entity->state) {
v42bis_free((v42bis_state_t *) comp_entity->state);
comp_entity->state = NULL;
}
LOGSNDCP(LOGL_INFO, "V.42bis data compression terminated.\n");
return;
}
/* Just in case someone tries to terminate an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Perform a full reset of the V.42bis compression state */
static void v42bis_reset(v42bis_state_t *comp)
{
/* This function performs a complete reset of the V.42bis compression
* state by reinitalizing the state withe the previously negotiated
* parameters. */
int p0, p1, p2;
p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0;
p1 = comp->decompress.v42bis_parm_n2;
p2 = comp->decompress.v42bis_parm_n7;
LOGSNDCP(LOGL_DEBUG, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n",
comp, p0, p1, p2);
v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH);
}
/* Compress a packet using V.42bis data compression */
static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data,
unsigned int len, v42bis_state_t *comp)
{
/* Note: This implementation may only be used to compress SN_UNITDATA
* packets, since it resets the compression state for each NPDU. */
uint8_t *data_o;
int rc;
int skip = 0;
struct v42bis_output_buffer compressed_data;
/* Don't bother with short packets */
if (len < MIN_COMPR_PAYLOAD)
skip = 1;
/* Skip if compression is not enabled for TX direction */
if (!comp->compress.v42bis_parm_p0)
skip = 1;
/* Skip compression */
if (skip) {
*pcomp_index = 0;
return len;
}
/* Reset V.42bis compression state */
v42bis_reset(comp);
/* Run compressor */
data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC);
compressed_data.buf = data_o;
compressed_data.buf_pointer = data_o;
compressed_data.len = 0;
comp->compress.user_data = (&compressed_data);
rc = v42bis_compress(comp, data, len);
if (rc < 0) {
LOGSNDCP(LOGL_ERROR, "Data compression failed, skipping...\n");
skip = 1;
}
rc = v42bis_compress_flush(comp);
if (rc < 0) {
LOGSNDCP(LOGL_ERROR, "Data compression failed, skipping...\n");
skip = 1;
}
/* The compressor might yield negative compression gain, in
* this case, we just decide to send the packat as normal,
* uncompressed payload => skip compresssion */
if (compressed_data.len >= len) {
LOGSNDCP(LOGL_ERROR, "Data compression ineffective, skipping...\n");
skip = 1;
}
/* Skip compression */
if (skip) {
*pcomp_index = 0;
talloc_free(data_o);
return len;
}
*pcomp_index = 1;
memcpy(data, data_o, compressed_data.len);
talloc_free(data_o);
return compressed_data.len;
}
/* Expand a packet using V.42bis data compression */
static int v42bis_expand_unitdata(uint8_t *data, unsigned int len,
uint8_t pcomp_index, v42bis_state_t *comp)
{
/* Note: This implementation may only be used to compress SN_UNITDATA
* packets, since it resets the compression state for each NPDU. */
int rc;
struct v42bis_output_buffer uncompressed_data;
uint8_t *data_i;
/* Skip when the packet is marked as uncompressed */
if (pcomp_index == 0) {
return len;
}
/* Reset V.42bis compression state */
v42bis_reset(comp);
/* Decompress packet */
data_i = talloc_zero_size(comp, len);
memcpy(data_i, data, len);
uncompressed_data.buf = data;
uncompressed_data.buf_pointer = data;
uncompressed_data.len = 0;
comp->decompress.user_data = (&uncompressed_data);
rc = v42bis_decompress(comp, data_i, len);
talloc_free(data_i);
if (rc < 0)
return -EINVAL;
rc = v42bis_decompress_flush(comp);
if (rc < 0)
return -EINVAL;
return uncompressed_data.len;
}
/* Expand packet */
int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(comp_entities);
LOGSNDCP(LOGL_DEBUG, "Data compression entity list: comp_entities=%p\n", comp_entities);
LOGSNDCP(LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp);
/* Skip on pcomp=0 */
if (pcomp == 0) {
return len;
}
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
return len;
}
/* Note: Only data compression entities may appear in
* data compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
/* Note: Currently V42BIS is the only compression method we
* support, so the only allowed algorithm is V42BIS */
OSMO_ASSERT(comp_entity->algo.dcomp == V42BIS);
/* Find pcomp_index */
pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
/* Run decompression algo */
rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state);
LOGSNDCP(LOGL_DEBUG, "Data expansion done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}
/* Compress packet */
int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(pcomp);
OSMO_ASSERT(comp_entities);
LOGSNDCP(LOGL_DEBUG, "Data compression entity list: comp_entities=%p\n", comp_entities);
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
*pcomp = 0;
return len;
}
/* Note: Only data compression entities may appear in
* data compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
/* Note: Currently V42BIS is the only compression method we
* support, so the only allowed algorithm is V42BIS */
OSMO_ASSERT(comp_entity->algo.dcomp == V42BIS);
/* Run compression algo */
rc = v42bis_compress_unitdata(&pcomp_index, data, len,
comp_entity->state);
/* Find pcomp value */
*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
LOGSNDCP(LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp);
LOGSNDCP(LOGL_DEBUG, "Data compression done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}

270
src/sndcp/sndcp_pcomp.c Normal file
View File

@ -0,0 +1,270 @@
/* GPRS SNDCP header compression handler */
/* (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 <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gprs/sndcp/slhc.h>
#include <osmocom/gprs/sndcp/sndcp_private.h>
/* Initialize header compression */
int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a new header compression
* entity is created by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
OSMO_ASSERT(comp_field);
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
&& comp_entity->algo.pcomp == RFC_1144) {
OSMO_ASSERT(comp_field->rfc1144_params);
comp_entity->state =
slhc_init(ctx, comp_field->rfc1144_params->s01 + 1,
comp_field->rfc1144_params->s01 + 1);
LOGSNDCP(LOGL_INFO, "RFC1144 header compression initialized.\n");
return 0;
}
/* Just in case someone tries to initialize an unknown or unsupported
* header compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Terminate header compression */
void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a header compression
* entity is deleted by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
&& comp_entity->algo.pcomp == RFC_1144) {
if (comp_entity->state) {
slhc_free((struct slcompress *)comp_entity->state);
comp_entity->state = NULL;
}
LOGSNDCP(LOGL_INFO, "RFC1144 header compression terminated.\n");
return;
}
/* Just in case someone tries to terminate an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Compress a packet using Van Jacobson RFC1144 header compression */
static int rfc1144_compress(uint8_t *pcomp_index, uint8_t *data,
unsigned int len, struct slcompress *comp)
{
uint8_t *comp_ptr;
int compr_len;
uint8_t *data_o;
/* Create a working copy of the incoming data */
data_o = talloc_zero_size(comp, len);
memcpy(data_o, data, len);
/* Run compressor */
compr_len = slhc_compress(comp, data, len, data_o, &comp_ptr, 0);
/* Generate pcomp_index */
if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
*pcomp_index = 2;
data_o[0] &= ~SL_TYPE_COMPRESSED_TCP;
memcpy(data, data_o, compr_len);
} else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) ==
SL_TYPE_UNCOMPRESSED_TCP) {
*pcomp_index = 1;
data_o[0] &= 0x4F;
memcpy(data, data_o, compr_len);
} else
*pcomp_index = 0;
talloc_free(data_o);
return compr_len;
}
/* Expand a packet using Van Jacobson RFC1144 header compression */
static int rfc1144_expand(uint8_t *data, unsigned int len, uint8_t pcomp_index,
struct slcompress *comp)
{
int data_decompressed_len;
int type;
/* Note: this function should never be called with pcomp_index=0,
* since this condition is already filtered
* out by gprs_sndcp_pcomp_expand() */
/* Determine the data type by the PCOMP index */
switch (pcomp_index) {
case 0:
type = SL_TYPE_IP;
break;
case 1:
type = SL_TYPE_UNCOMPRESSED_TCP;
break;
case 2:
type = SL_TYPE_COMPRESSED_TCP;
break;
default:
LOGSNDCP(LOGL_ERROR, "rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
pcomp_index);
type = SL_TYPE_IP;
break;
}
/* Restore the original version nibble on
* marked uncompressed packets */
if (type == SL_TYPE_UNCOMPRESSED_TCP) {
/* Just in case the phone tags uncompressed tcp-data
* (normally this is handled by pcomp so there is
* no need for tagging the data) */
data[0] &= 0x4F;
data_decompressed_len = slhc_remember(comp, data, len);
return data_decompressed_len;
}
/* Uncompress compressed packets */
else if (type == SL_TYPE_COMPRESSED_TCP) {
data_decompressed_len = slhc_uncompress(comp, data, len);
return data_decompressed_len;
}
/* Regular or unknown packets will not be touched */
return len;
}
/* Expand packet header */
int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(comp_entities);
LOGSNDCP(LOGL_DEBUG, "Header compression entity list: comp_entities=%p\n",
comp_entities);
LOGSNDCP(LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp);
/* Skip on pcomp=0 */
if (pcomp == 0) {
return len;
}
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
return len;
}
/* Note: Only protocol compression entities may appear in
* protocol compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
/* Note: Currently RFC1144 is the only compression method we
* support, so the only allowed algorithm is RFC1144 */
OSMO_ASSERT(comp_entity->algo.pcomp == RFC_1144);
/* Find pcomp_index */
pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
/* Run decompression algo */
rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state);
slhc_i_status(comp_entity->state);
slhc_o_status(comp_entity->state);
LOGSNDCP(LOGL_DEBUG, "Header expansion done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}
/* Compress packet header */
int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(pcomp);
OSMO_ASSERT(comp_entities);
LOGSNDCP(LOGL_DEBUG, "Header compression entity list: comp_entities=%p\n",
comp_entities);
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
*pcomp = 0;
return len;
}
/* Note: Only protocol compression entities may appear in
* protocol compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
/* Note: Currently RFC1144 is the only compression method we
* support, so the only allowed algorithm is RFC1144 */
OSMO_ASSERT(comp_entity->algo.pcomp == RFC_1144);
/* Run compression algo */
rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state);
slhc_i_status(comp_entity->state);
slhc_o_status(comp_entity->state);
/* Find pcomp value */
*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
LOGSNDCP(LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
LOGSNDCP(LOGL_DEBUG, "Header compression done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}

644
src/sndcp/sndcp_prim.c Normal file
View File

@ -0,0 +1,644 @@
/* SNDCP service primitive implementation as per 3GPP TS 44.065 */
/*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* 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 <stdint.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/sndcp/sndcp.h>
#include <osmocom/gprs/sndcp/sndcp_prim.h>
#include <osmocom/gprs/sndcp/sndcp_private.h>
#define SNDCP_MSGB_HEADROOM 0
const struct value_string osmo_gprs_sndcp_prim_sap_names[] = {
{ OSMO_GPRS_SNDCP_SAP_SN, "SN" },
{ OSMO_GPRS_SNDCP_SAP_SNSM, "SNSM" },
{ 0, NULL }
};
const struct value_string osmo_gprs_sndcp_sn_prim_type_names[] = {
{ OSMO_GPRS_SNDCP_SN_DATA, "DATA" },
{ OSMO_GPRS_SNDCP_SN_UNITDATA, "UNITDATA" },
{ OSMO_GPRS_SNDCP_SN_XID, "XID" },
{ 0, NULL }
};
const struct value_string osmo_gprs_sndcp_snsm_prim_type_names[] = {
{ OSMO_GPRS_SNDCP_SNSM_ACTIVATE, "ACTIVATE" },
{ OSMO_GPRS_SNDCP_SNSM_DEACTIVATE, "DEACTIVATE" },
{ OSMO_GPRS_SNDCP_SNSM_MODIFY, "MODIFY" },
{ OSMO_GPRS_SNDCP_SNSM_STATUS, "STATUS" },
{ OSMO_GPRS_SNDCP_SNSM_SEQUENCE, "SEQUENCE" },
{ OSMO_GPRS_SNDCP_SNSM_STOP_ASSIGN, "STOP-ASSIGN" },
{ 0, NULL }
};
const char *osmo_gprs_sndcp_prim_name(const struct osmo_gprs_sndcp_prim *sndcp_prim)
{
static char name_buf[256];
const char *sap = osmo_gprs_sndcp_prim_sap_name(sndcp_prim->oph.sap);
const char *op = get_value_string(osmo_prim_op_names, sndcp_prim->oph.operation);
const char *type;
switch (sndcp_prim->oph.sap) {
case OSMO_GPRS_SNDCP_SAP_SN:
type = osmo_gprs_sndcp_sn_prim_type_name(sndcp_prim->oph.primitive);
break;
case OSMO_GPRS_SNDCP_SAP_SNSM:
type = osmo_gprs_sndcp_snsm_prim_type_name(sndcp_prim->oph.primitive);
break;
default:
type = "unsupported-sndcp-sap";
}
snprintf(name_buf, sizeof(name_buf), "%s-%s.%s", sap, type, op);
return name_buf;
}
static int sndcp_up_cb_dummy(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data)
{
LOGSNDCP(LOGL_INFO, "sndcp_up_cb_dummy(%s)\n", osmo_gprs_sndcp_prim_name(sndcp_prim));
return 0;
}
static int sndcp_down_cb_dummy(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
{
LOGSNDCP(LOGL_INFO, "sndcp_down_cb_dummy(%s)\n", osmo_gprs_llc_prim_name(llc_prim));
return 0;
}
static int sndcp_snsm_cb_dummy(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data)
{
LOGSNDCP(LOGL_INFO, "sndcp_snsm_cb_dummy(%s)\n", osmo_gprs_sndcp_prim_name(sndcp_prim));
return 0;
}
/* Set callback used by SNDCP layer to push primitives to higher layers in protocol stack */
void osmo_gprs_sndcp_prim_set_up_cb(osmo_gprs_sndcp_prim_up_cb up_cb, void *up_user_data)
{
g_ctx->sndcp_up_cb = up_cb;
g_ctx->sndcp_up_cb_user_data = up_user_data;
}
/* Set callback used by SNDCP layer to push primitives to lower layers in protocol stack */
void osmo_gprs_sndcp_prim_set_down_cb(osmo_gprs_sndcp_prim_down_cb down_cb, void *down_user_data)
{
g_ctx->sndcp_down_cb = down_cb;
g_ctx->sndcp_down_cb_user_data = down_user_data;
}
/* Set callback used by SNDCP layer to push primitives to SM sublayer */
void osmo_gprs_sndcp_prim_set_snsm_cb(osmo_gprs_sndcp_prim_snsm_cb snsm_cb, void *snsm_user_data)
{
g_ctx->sndcp_snsm_cb = snsm_cb;
g_ctx->sndcp_snsm_cb_user_data = snsm_user_data;
}
/********************************
* Primitive allocation:
********************************/
/* allocate a msgb containing a struct osmo_gprs_sndcp_prim + optional l3 data */
static struct msgb *gprs_sndcp_prim_msgb_alloc(unsigned int npdu_len)
{
const int headroom = SNDCP_MSGB_HEADROOM;
const int size = headroom + sizeof(struct osmo_gprs_sndcp_prim) + npdu_len;
struct msgb *msg = msgb_alloc_headroom(size, headroom, "sndcp_prim");
if (!msg)
return NULL;
msg->l1h = msgb_put(msg, sizeof(struct osmo_gprs_sndcp_prim));
return msg;
}
struct osmo_gprs_sndcp_prim *gprs_sndcp_prim_alloc(unsigned int sap, unsigned int type,
enum osmo_prim_operation operation,
unsigned int extra_size)
{
struct msgb *msg = gprs_sndcp_prim_msgb_alloc(extra_size);
struct osmo_gprs_sndcp_prim *sndcp_prim = msgb_sndcp_prim(msg);
osmo_prim_init(&sndcp_prim->oph, sap, type, operation, msg);
return sndcp_prim;
}
/*** SN ***/
static inline struct osmo_gprs_sndcp_prim *sndcp_prim_sn_alloc(enum osmo_gprs_sndcp_sn_prim_type type,
enum osmo_prim_operation operation,
unsigned int extra_size)
{
return gprs_sndcp_prim_alloc(OSMO_GPRS_SNDCP_SAP_SN, type, operation, extra_size);
}
/* 5.1.1.1 SN-DATA.request */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_data_req(uint32_t tlli, uint8_t sapi, uint8_t nsapi, uint8_t *npdu, size_t npdu_len)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_sn_alloc(OSMO_GPRS_SNDCP_SN_DATA, PRIM_OP_REQUEST, npdu_len);
sndcp_prim->sn.tlli = tlli;
sndcp_prim->sn.sapi = sapi;
sndcp_prim->sn.data_req.nsapi = nsapi;
sndcp_prim->sn.data_req.npdu = npdu;
sndcp_prim->sn.data_req.npdu_len = npdu_len;
return sndcp_prim;
}
/* 5.1.1.3 SN-UNITDATA.request */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_unitdata_req(uint32_t tlli, uint8_t sapi, uint8_t nsapi, uint8_t *npdu, size_t npdu_len)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_sn_alloc(OSMO_GPRS_SNDCP_SN_UNITDATA, PRIM_OP_REQUEST, npdu_len);
sndcp_prim->sn.tlli = tlli;
sndcp_prim->sn.sapi = sapi;
sndcp_prim->sn.unitdata_req.nsapi = nsapi;
sndcp_prim->sn.unitdata_req.npdu = npdu;
sndcp_prim->sn.unitdata_req.npdu_len = npdu_len;
return sndcp_prim;
}
/* 5.1.1.3 SN-UNITDATA.ind */
struct osmo_gprs_sndcp_prim *gprs_sndcp_prim_alloc_sn_unitdata_ind(uint32_t tlli, uint8_t sapi, uint8_t nsapi, uint8_t *npdu, size_t npdu_len)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_sn_alloc(OSMO_GPRS_SNDCP_SN_UNITDATA, PRIM_OP_INDICATION, npdu_len);
sndcp_prim->sn.tlli = tlli;
sndcp_prim->sn.sapi = sapi;
sndcp_prim->sn.unitdata_req.nsapi = nsapi;
sndcp_prim->sn.unitdata_req.npdu = npdu;
sndcp_prim->sn.unitdata_req.npdu_len = npdu_len;
return sndcp_prim;
}
/* 5.1.1.5 SN-XID.request */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_xid_req(uint32_t tlli, uint8_t sapi, uint8_t nsapi)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_sn_alloc(OSMO_GPRS_SNDCP_SN_XID, PRIM_OP_REQUEST, 0);
sndcp_prim->sn.tlli = tlli;
sndcp_prim->sn.sapi = sapi;
sndcp_prim->sn.xid_req.nsapi = nsapi;
return sndcp_prim;
}
/* 5.1.1.7 SN-XID.response */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_sn_xid_rsp(uint32_t tlli, uint8_t sapi, uint8_t nsapi)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_sn_alloc(OSMO_GPRS_SNDCP_SN_XID, PRIM_OP_REQUEST, 0);
sndcp_prim->sn.tlli = tlli;
sndcp_prim->sn.sapi = sapi;
sndcp_prim->sn.xid_rsp.nsapi = nsapi;
return sndcp_prim;
}
/*** SN SM ***/
static inline struct osmo_gprs_sndcp_prim *sndcp_prim_snsm_alloc(enum osmo_gprs_sndcp_snsm_prim_type type,
enum osmo_prim_operation operation,
unsigned int extra_size)
{
return gprs_sndcp_prim_alloc(OSMO_GPRS_SNDCP_SAP_SNSM, type, operation, extra_size);
}
/* 5.1.2.19 SNSM-ACTIVATE.indication */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_snsm_activate_ind(uint32_t tlli, uint8_t nsapi, uint8_t sapi)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_snsm_alloc(OSMO_GPRS_SNDCP_SNSM_ACTIVATE, PRIM_OP_INDICATION, 0);
sndcp_prim->snsm.tlli = tlli;
sndcp_prim->snsm.activate_ind.nsapi = nsapi;
sndcp_prim->snsm.activate_ind.sapi = sapi;
return sndcp_prim;
}
/* 5.1.2.20 SNSM-ACTIVATE.response */
struct osmo_gprs_sndcp_prim *gprs_sndcp_prim_alloc_snsm_activate_rsp(uint32_t tlli, uint8_t nsapi)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_snsm_alloc(OSMO_GPRS_SNDCP_SNSM_ACTIVATE, PRIM_OP_RESPONSE, 0);
sndcp_prim->snsm.tlli = tlli;
sndcp_prim->snsm.activate_rsp.nsapi = nsapi;
return sndcp_prim;
}
/* 5.1.2.21 SNSM-DEACTIVATE.indication */
struct osmo_gprs_sndcp_prim *osmo_gprs_sndcp_prim_alloc_snsm_deactivate_ind(uint32_t tlli, uint8_t nsapi)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
sndcp_prim = sndcp_prim_snsm_alloc(OSMO_GPRS_SNDCP_SNSM_DEACTIVATE, PRIM_OP_INDICATION, 0);
sndcp_prim->snsm.tlli = tlli;
sndcp_prim->snsm.deactivate_ind.nsapi = nsapi;
return sndcp_prim;
}
static int gprs_sndcp_prim_handle_unsupported(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
LOGSNDCP(LOGL_ERROR, "Unsupported sndcp_prim! %s\n", osmo_gprs_sndcp_prim_name(sndcp_prim));
msgb_free(sndcp_prim->oph.msg);
return -ENOTSUP;
}
static int gprs_sndcp_prim_handle_llc_ll_unsupported(struct osmo_gprs_llc_prim *llc_prim)
{
LOGSNDCP(LOGL_ERROR, "Unsupported sndcp_prim! %s\n", osmo_gprs_llc_prim_name(llc_prim));
msgb_free(llc_prim->oph.msg);
return -ENOTSUP;
}
/********************************
* Handling from/to upper layers:
********************************/
int gprs_sndcp_prim_call_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc;
if (g_ctx->sndcp_up_cb)
rc = g_ctx->sndcp_up_cb(sndcp_prim, g_ctx->sndcp_up_cb_user_data);
else
rc = sndcp_up_cb_dummy(sndcp_prim, g_ctx->sndcp_up_cb_user_data);
msgb_free(sndcp_prim->oph.msg);
return rc;
}
/* 5.1.1.1 SN-DATA.request:*/
static int gprs_sndcp_prim_handle_sndcp_sn_data_req(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc;
struct gprs_sndcp_entity *sne;
OSMO_ASSERT(sndcp_prim->sn.data_req.npdu);
OSMO_ASSERT(sndcp_prim->sn.data_req.npdu_len > 0);
sne = gprs_sndcp_sne_by_dlci_nsapi(sndcp_prim->sn.tlli, sndcp_prim->sn.sapi,
sndcp_prim->sn.data_req.nsapi);
if (!sne) {
LOGSNDCP(LOGL_ERROR, "Message for non-existing SNDCP Entity "
"(TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
sndcp_prim->sn.tlli, sndcp_prim->sn.sapi,
sndcp_prim->sn.data_req.nsapi);
rc = -EIO;
goto ret_free;
}
rc = gprs_sndcp_prim_handle_unsupported(sndcp_prim);
ret_free:
msgb_free(sndcp_prim->oph.msg);
return rc;
}
/* 5.1.1.3 SN-UNITDATA.request:*/
static int gprs_sndcp_prim_handle_sndcp_sn_unitdata_req(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc;
struct gprs_sndcp_entity *sne;
OSMO_ASSERT(sndcp_prim->sn.unitdata_req.npdu);
OSMO_ASSERT(sndcp_prim->sn.unitdata_req.npdu_len > 0);
sne = gprs_sndcp_sne_by_dlci_nsapi(sndcp_prim->sn.tlli, sndcp_prim->sn.sapi,
sndcp_prim->sn.unitdata_req.nsapi);
if (!sne) {
LOGSNDCP(LOGL_ERROR, "Message for non-existing SNDCP Entity "
"(TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
sndcp_prim->sn.tlli, sndcp_prim->sn.sapi,
sndcp_prim->sn.unitdata_req.nsapi);
rc = -EIO;
goto ret_free;
}
rc = gprs_sndcp_sne_handle_sn_unitdata_req(sne, sndcp_prim->sn.unitdata_req.npdu,
sndcp_prim->sn.unitdata_req.npdu_len);
ret_free:
msgb_free(sndcp_prim->oph.msg);
return rc;
}
/* 5.1.1.5 SN-XID.request:*/
static int gprs_sndcp_prim_handle_sndcp_sn_xid_req(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc;
struct gprs_sndcp_entity *sne;
sne = gprs_sndcp_sne_by_dlci_nsapi(sndcp_prim->sn.tlli, sndcp_prim->sn.sapi,
sndcp_prim->sn.xid_req.nsapi);
if (!sne) {
LOGSNDCP(LOGL_ERROR, "Message for non-existing SNDCP Entity "
"(TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
sndcp_prim->sn.tlli, sndcp_prim->sn.sapi,
sndcp_prim->sn.unitdata_req.nsapi);
rc = -EIO;
goto ret_free;
}
rc = gprs_sndcp_sne_handle_sn_xid_req(sne, sndcp_prim);
ret_free:
msgb_free(sndcp_prim->oph.msg);
return rc;
}
/* SNDCP higher layers push SNDCP primitive down to SNDCP layer: */
int osmo_gprs_sndcp_prim_upper_down(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc;
OSMO_ASSERT(g_ctx);
LOGSNDCP(LOGL_INFO, "Rx from upper layers: %s\n", osmo_gprs_sndcp_prim_name(sndcp_prim));
if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SN)
return gprs_sndcp_prim_handle_unsupported(sndcp_prim);
switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_DATA, PRIM_OP_REQUEST):
rc = gprs_sndcp_prim_handle_sndcp_sn_data_req(sndcp_prim);
break;
case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_UNITDATA, PRIM_OP_REQUEST):
rc = gprs_sndcp_prim_handle_sndcp_sn_unitdata_req(sndcp_prim);
break;
case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_XID, PRIM_OP_REQUEST):
rc = gprs_sndcp_prim_handle_sndcp_sn_xid_req(sndcp_prim);
break;
default:
rc = gprs_sndcp_prim_handle_unsupported(sndcp_prim);
}
return rc;
}
/********************************
* Handling from/to lower layers:
********************************/
int gprs_sndcp_prim_call_down_cb(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
if (g_ctx->sndcp_down_cb)
rc = g_ctx->sndcp_down_cb(llc_prim, g_ctx->sndcp_down_cb_user_data);
else
rc = sndcp_down_cb_dummy(llc_prim, g_ctx->sndcp_down_cb_user_data);
msgb_free(llc_prim->oph.msg);
return rc;
}
static int gprs_sndcp_prim_handle_llc_ll_unitdata_ind(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
struct gprs_sndcp_entity *sne;
struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)llc_prim->ll.l3_pdu;
OSMO_ASSERT(sch);
OSMO_ASSERT(llc_prim->ll.l3_pdu_len > 0);
sne = gprs_sndcp_sne_by_dlci_nsapi(llc_prim->ll.tlli, llc_prim->ll.sapi, sch->nsapi);
if (!sne) {
LOGSNDCP(LOGL_ERROR, "Message for non-existing SNDCP Entity "
"(TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
llc_prim->ll.tlli, llc_prim->ll.sapi, sch->nsapi);
return -EIO;
}
rc = gprs_sndcp_sne_handle_llc_ll_unitdata_ind(sne, sch, llc_prim->ll.l3_pdu_len);
return rc;
}
static int gprs_sndcp_prim_handle_llc_ll_xid_ind(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
struct gprs_sndcp_mgmt_entity *snme;
snme = gprs_sndcp_snme_find_by_tlli(llc_prim->ll.tlli);
if (!snme) {
LOGSNDCP(LOGL_ERROR, "SNDCP-LL-XID.ind: Message for non-existing SNDCP Entity "
"(TLLI=%08x, SAPI=%u)\n",
llc_prim->ll.tlli, llc_prim->ll.sapi);
return -EIO;
}
rc = gprs_sndcp_snme_handle_llc_ll_xid_ind(snme, llc_prim->ll.sapi,
llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len);
return rc;
}
static int gprs_sndcp_prim_handle_llc_ll_xid_cnf(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
struct gprs_sndcp_mgmt_entity *snme;
snme = gprs_sndcp_snme_find_by_tlli(llc_prim->ll.tlli);
if (!snme) {
LOGSNDCP(LOGL_ERROR, "SNDCP-LL-XID.cnf: Message for non-existing SNDCP Entity "
"(TLLI=%08x, SAPI=%u)\n",
llc_prim->ll.tlli, llc_prim->ll.sapi);
return -EIO;
}
rc = gprs_sndcp_snme_handle_llc_ll_xid_cnf(snme, llc_prim->ll.sapi,
llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len);
return rc;
}
int gprs_sndcp_prim_lower_up_llc_ll(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
switch (OSMO_PRIM_HDR(&llc_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_INDICATION):
rc = gprs_sndcp_prim_handle_llc_ll_unitdata_ind(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LL_XID, PRIM_OP_INDICATION):
rc = gprs_sndcp_prim_handle_llc_ll_xid_ind(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LL_XID, PRIM_OP_CONFIRM):
rc = gprs_sndcp_prim_handle_llc_ll_xid_cnf(llc_prim);
break;
default:
rc = gprs_sndcp_prim_handle_llc_ll_unsupported(llc_prim);
}
return rc;
}
/* SNDCP lower layers (LLC) push SNDCP primitive up to SNDCP layer: */
int osmo_gprs_sndcp_prim_lower_up(struct osmo_gprs_llc_prim *llc_prim)
{
OSMO_ASSERT(g_ctx);
OSMO_ASSERT(llc_prim);
struct msgb *msg = llc_prim->oph.msg;
int rc;
LOGSNDCP(LOGL_INFO, "Rx from lower layers: %s\n", osmo_gprs_llc_prim_name(llc_prim));
switch (llc_prim->oph.sap) {
case OSMO_GPRS_LLC_SAP_LL:
rc = gprs_sndcp_prim_lower_up_llc_ll(llc_prim);
break;
default:
rc = gprs_sndcp_prim_handle_llc_ll_unsupported(llc_prim);
}
/* Special return value '1' means: do not free */
if (rc != 1)
msgb_free(msg);
return rc;
}
/********************************
* Handling from/to SM sublayer:
********************************/
int gprs_sndcp_prim_call_snsm_cb(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc;
if (g_ctx->sndcp_snsm_cb)
rc = g_ctx->sndcp_snsm_cb(sndcp_prim, g_ctx->sndcp_snsm_cb_user_data);
else
rc = sndcp_snsm_cb_dummy(sndcp_prim, g_ctx->sndcp_snsm_cb_user_data);
msgb_free(sndcp_prim->oph.msg);
return rc;
}
/* 5.1.2.19 SNSM-ACTIVATE.indication: */
static int gprs_sndcp_prim_handle_sndcp_snsm_activate_ind(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc = 0;
uint32_t tlli = sndcp_prim->snsm.tlli;
uint8_t sapi = sndcp_prim->snsm.activate_ind.sapi;
uint8_t nsapi = sndcp_prim->snsm.activate_ind.nsapi;
struct gprs_sndcp_mgmt_entity *snme;
LOGSNDCP(LOGL_INFO, "SNSM-ACTIVATE.ind (TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
tlli, sapi, nsapi);
snme = gprs_sndcp_snme_find_by_tlli(tlli);
if (!snme) {
snme = gprs_sndcp_snme_alloc(tlli);
} else if (gprs_sndcp_snme_get_sne(snme, nsapi)) {
LOGSNDCP(LOGL_ERROR, "Trying to ACTIVATE already-existing entity "
"(TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
tlli, sapi, nsapi);
return -EEXIST;
}
if (!snme) {
LOGSNDCP(LOGL_ERROR, "Out of memory during ACTIVATE\n");
return -ENOMEM;
}
if (!gprs_sndcp_sne_alloc(snme, sapi, nsapi)) {
LOGSNDCP(LOGL_ERROR, "Out of memory during ACTIVATE\n");
return -ENOMEM;
}
return rc;
}
/* 5.1.2.21 SNSM-DEACTIVATE.indication: */
static int gprs_sndcp_prim_handle_sndcp_snsm_deactivate_ind(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc = 0;
struct gprs_sndcp_mgmt_entity *snme;
struct gprs_sndcp_entity *sne;
uint32_t tlli = sndcp_prim->snsm.tlli;
uint8_t nsapi = sndcp_prim->snsm.deactivate_ind.nsapi;
snme = gprs_sndcp_snme_find_by_tlli(tlli);
if (!snme) {
LOGSNDCP(LOGL_ERROR, "SNSM-DEACTIVATE.ind: Message for non-existing SNDCP Management Entity "
"(TLLI=%08x, NSAPI=%u)\n", tlli, nsapi);
return -EIO;
}
sne = gprs_sndcp_snme_get_sne(snme, nsapi);
if (!sne) {
LOGSNDCP(LOGL_ERROR, "SNSM-DEACTIVATE.ind: Message for non-existing SNDCP Entity "
"(TLLI=%08x, NSAPI=%u)\n", tlli, nsapi);
return -EIO;
}
gprs_sndcp_sne_free(sne);
return rc;
}
/* 5.1.2.23 SNSM-MODIFY.indication: */
static int gprs_sndcp_prim_handle_sndcp_snsm_modify_ind(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc = 0;
rc = gprs_sndcp_prim_handle_unsupported(sndcp_prim);
return rc;
}
/* 5.1.2.26 SNSM-SEQUENCE.indication: */
static int gprs_sndcp_prim_handle_sndcp_snsm_sequence_ind(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc = 0;
rc = gprs_sndcp_prim_handle_unsupported(sndcp_prim);
return rc;
}
/* 5.1.2.28 SNSM-STOP-ASSIGN.indication: */
static int gprs_sndcp_prim_handle_sndcp_snsm_stop_assign_ind(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc = 0;
rc = gprs_sndcp_prim_handle_unsupported(sndcp_prim);
return rc;
}
/* SNDCP higher layers push SNDCP primitive down to SNDCP layer: */
int osmo_gprs_sndcp_prim_dispatch_snsm(struct osmo_gprs_sndcp_prim *sndcp_prim)
{
int rc;
OSMO_ASSERT(g_ctx);
OSMO_ASSERT(sndcp_prim);
struct msgb *msg = sndcp_prim->oph.msg;
LOGSNDCP(LOGL_INFO, "Rx from SNDCP SM sublayer: %s\n", osmo_gprs_sndcp_prim_name(sndcp_prim));
if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SNSM)
return gprs_sndcp_prim_handle_unsupported(sndcp_prim);
switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_ACTIVATE, PRIM_OP_INDICATION):
rc = gprs_sndcp_prim_handle_sndcp_snsm_activate_ind(sndcp_prim);
break;
case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_DEACTIVATE, PRIM_OP_INDICATION):
rc = gprs_sndcp_prim_handle_sndcp_snsm_deactivate_ind(sndcp_prim);
break;
case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_MODIFY, PRIM_OP_INDICATION):
rc = gprs_sndcp_prim_handle_sndcp_snsm_modify_ind(sndcp_prim);
break;
case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_SEQUENCE, PRIM_OP_INDICATION):
rc = gprs_sndcp_prim_handle_sndcp_snsm_sequence_ind(sndcp_prim);
break;
case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_STOP_ASSIGN, PRIM_OP_INDICATION):
rc = gprs_sndcp_prim_handle_sndcp_snsm_stop_assign_ind(sndcp_prim);
break;
default:
rc = gprs_sndcp_prim_handle_unsupported(sndcp_prim);
}
/* Special return value '1' means: do not free */
if (rc != 1)
msgb_free(msg);
return rc;
}

1889
src/sndcp/sndcp_xid.c Normal file

File diff suppressed because it is too large Load Diff

764
src/sndcp/v42bis.c Normal file
View File

@ -0,0 +1,764 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* v42bis.c
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*/
/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED.
Currently it performs the core compression and decompression functions OK.
However, a number of the bells and whistles in V.42bis are incomplete. */
/*! \file */
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <assert.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/sndcp/v42bis.h>
#include <osmocom/gprs/sndcp/v42bis_private.h>
#include <osmocom/gprs/sndcp/sndcp_private.h>
#define span_log(x, y, msg, ...) LOGSNDCP(LOGL_DEBUG, msg, ##__VA_ARGS__)
#define span_log_init(x, y, z)
#define span_log_set_protocol(x, y)
#define FALSE 0
#define TRUE 1
/* Fixed parameters from the spec. */
/* Character size (bits) */
#define V42BIS_N3 8
/* Number of characters in the alphabet */
#define V42BIS_N4 256
/* Index number of first dictionary entry used to store a string */
#define V42BIS_N5 (V42BIS_N4 + V42BIS_N6)
/* Number of control codewords */
#define V42BIS_N6 3
/* V.42bis/9.2 */
#define V42BIS_ESC_STEP 51
/* Compreeibility monitoring parameters for assessing automated switches between
transparent and compressed mode */
#define COMPRESSIBILITY_MONITOR (256*V42BIS_N3)
#define COMPRESSIBILITY_MONITOR_HYSTERESIS 11
/* Control code words in compressed mode */
enum {
V42BIS_ETM = 0, /* Enter transparent mode */
V42BIS_FLUSH = 1, /* Flush data */
V42BIS_STEPUP = 2 /* Step up codeword size */
};
/* Command codes in transparent mode */
enum
{
V42BIS_ECM = 0, /* Enter compression mode */
V42BIS_EID = 1, /* Escape character in data */
V42BIS_RESET = 2 /* Force reinitialisation */
};
static __inline__ void push_octet(v42bis_comp_state_t *s, int octet)
{
s->output_buf[s->output_octet_count++] = (uint8_t) octet;
if (s->output_octet_count >= s->max_output_len)
{
s->handler(s->user_data, s->output_buf, s->output_octet_count);
s->output_octet_count = 0;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len)
{
int i;
int chunk;
i = 0;
while ((s->output_octet_count + len - i) >= s->max_output_len)
{
chunk = s->max_output_len - s->output_octet_count;
memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
s->handler(s->user_data, s->output_buf, s->max_output_len);
s->output_octet_count = 0;
i += chunk;
}
chunk = len - i;
if (chunk > 0)
{
memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
s->output_octet_count += chunk;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code)
{
s->bit_buffer |= code << s->bit_count;
s->bit_count += s->v42bis_parm_c2;
while (s->bit_count >= 8)
{
push_octet(s, s->bit_buffer & 0xFF);
s->bit_buffer >>= 8;
s->bit_count -= 8;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_octet_alignment(v42bis_comp_state_t *s)
{
if ((s->bit_count & 7))
{
s->bit_count += (8 - (s->bit_count & 7));
while (s->bit_count >= 8)
{
push_octet(s, s->bit_buffer & 0xFF);
s->bit_buffer >>= 8;
s->bit_count -= 8;
}
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void flush_octets(v42bis_comp_state_t *s)
{
if (s->output_octet_count > 0)
{
s->handler(s->user_data, s->output_buf, s->output_octet_count);
s->output_octet_count = 0;
}
}
/*- End of function --------------------------------------------------------*/
static void dictionary_init(v42bis_comp_state_t *s)
{
int i;
memset(s->dict, 0, sizeof(s->dict));
for (i = 0; i < V42BIS_N4; i++)
s->dict[i + V42BIS_N6].node_octet = i;
s->v42bis_parm_c1 = V42BIS_N5;
s->v42bis_parm_c2 = V42BIS_N3 + 1;
s->v42bis_parm_c3 = V42BIS_N4 << 1;
s->last_matched = 0;
s->update_at = 0;
s->last_added = 0;
s->bit_buffer = 0;
s->bit_count = 0;
s->flushed_length = 0;
s->string_length = 0;
s->escape_code = 0;
s->transparent = TRUE;
s->escaped = FALSE;
s->compression_performance = COMPRESSIBILITY_MONITOR;
}
/*- End of function --------------------------------------------------------*/
static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
{
uint16_t e;
if (at == 0)
return octet + V42BIS_N6;
e = s->dict[at].child;
while (e)
{
if (s->dict[e].node_octet == octet)
return e;
e = s->dict[e].next;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
{
uint16_t newx;
uint16_t next;
uint16_t e;
newx = s->v42bis_parm_c1;
s->dict[newx].node_octet = octet;
s->dict[newx].parent = at;
s->dict[newx].child = 0;
s->dict[newx].next = s->dict[at].child;
s->dict[at].child = newx;
next = newx;
/* 6.5 Recovering a dictionary entry to use next */
do
{
/* 6.5(a) and (b) */
if (++next == s->v42bis_parm_n2)
next = V42BIS_N5;
}
while (s->dict[next].child);
/* 6.5(c) We need to reuse a leaf node */
if (s->dict[next].parent)
{
/* 6.5(d) Detach the leaf node from its parent, and re-use it */
e = s->dict[next].parent;
if (s->dict[e].child == next)
{
s->dict[e].child = s->dict[next].next;
}
else
{
e = s->dict[e].child;
while (s->dict[e].next != next)
e = s->dict[e].next;
s->dict[e].next = s->dict[next].next;
}
}
s->v42bis_parm_c1 = next;
return newx;
}
/*- End of function --------------------------------------------------------*/
static void send_string(v42bis_comp_state_t *s)
{
push_octets(s, s->string, s->string_length);
s->string_length = 0;
s->flushed_length = 0;
}
/*- End of function --------------------------------------------------------*/
static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code)
{
int i;
uint16_t p;
/* Work out the length */
for (i = 0, p = code; p; i++)
p = s->dict[p].parent;
s->string_length += i;
/* Now expand the known length of string */
i = s->string_length - 1;
for (p = code; p; )
{
s->string[i--] = s->dict[p].node_octet;
p = s->dict[p].parent;
}
}
/*- End of function --------------------------------------------------------*/
static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code)
{
int i;
/* Update compressibility metric */
/* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */
s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR);
if (s->transparent)
{
for (i = 0; i < s->string_length; i++)
{
push_octet(s, s->string[i]);
if (s->string[i] == s->escape_code)
{
push_octet(s, V42BIS_EID);
s->escape_code += V42BIS_ESC_STEP;
}
}
}
else
{
/* Allow for any escape octets in the string */
for (i = 0; i < s->string_length; i++)
{
if (s->string[i] == s->escape_code)
s->escape_code += V42BIS_ESC_STEP;
}
/* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
while (code >= s->v42bis_parm_c3)
{
/* We need to increase the codeword size */
/* 7.4(a) */
push_compressed_code(s, V42BIS_STEPUP);
/* 7.4(b) */
s->v42bis_parm_c2++;
/* 7.4(c) */
s->v42bis_parm_c3 <<= 1;
/* 7.4(d) this might need to be repeated, so we loop */
}
/* 7.5 Transfer - output the last state of the string */
push_compressed_code(s, code);
}
s->string_length = 0;
s->flushed_length = 0;
}
/*- End of function --------------------------------------------------------*/
static void go_compressed(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
if (!s->transparent)
return;
span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n");
/* Switch out of transparent now, between codes. We need to send the octet which did not
match, just before switching. */
if (s->last_matched)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
}
push_octet(s, s->escape_code);
push_octet(s, V42BIS_ECM);
s->bit_buffer = 0;
s->transparent = FALSE;
}
/*- End of function --------------------------------------------------------*/
static void go_transparent(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
if (s->transparent)
return;
span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n");
/* Switch into transparent now, between codes, and the unmatched octet should
go out in transparent mode, just below */
if (s->last_matched)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
}
s->last_added = 0;
push_compressed_code(s, V42BIS_ETM);
push_octet_alignment(s);
s->transparent = TRUE;
}
/*- End of function --------------------------------------------------------*/
static void monitor_for_mode_change(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
switch (s->compression_mode)
{
case V42BIS_COMPRESSION_MODE_DYNAMIC:
/* 7.8 Data compressibility test */
if (s->transparent)
{
if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS)
{
/* 7.8.1 Transition to compressed mode */
go_compressed(ss);
}
}
else
{
if (s->compression_performance > COMPRESSIBILITY_MONITOR)
{
/* 7.8.2 Transition to transparent mode */
go_transparent(ss);
}
}
/* 7.8.3 Reset function - TODO */
break;
case V42BIS_COMPRESSION_MODE_ALWAYS:
if (s->transparent)
go_compressed(ss);
break;
case V42BIS_COMPRESSION_MODE_NEVER:
if (!s->transparent)
go_transparent(ss);
break;
}
}
/*- End of function --------------------------------------------------------*/
static int v42bis_comp_init(v42bis_comp_state_t *s,
int p1,
int p2,
put_msg_func_t handler,
void *user_data,
int max_output_len)
{
memset(s, 0, sizeof(*s));
s->v42bis_parm_n2 = p1;
s->v42bis_parm_n7 = p2;
s->handler = handler;
s->user_data = user_data;
s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH;
s->output_octet_count = 0;
dictionary_init(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
static int comp_exit(v42bis_comp_state_t *s)
{
s->v42bis_parm_n2 = 0;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
v42bis_comp_state_t *s;
int i;
uint16_t code;
s = &ss->compress;
if (!s->v42bis_parm_p0)
{
/* Compression is off - just push the incoming data out */
push_octets(s, buf, len);
return 0;
}
for (i = 0; i < len; )
{
/* 6.4 Add the string to the dictionary */
if (s->update_at)
{
if (match_octet(s, s->update_at, buf[i]) == 0)
s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]);
s->update_at = 0;
}
/* Match string */
while (i < len)
{
code = match_octet(s, s->last_matched, buf[i]);
if (code == 0)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
if (code == s->last_added)
{
s->last_added = 0;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
s->last_matched = code;
/* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
created by the last invocation of the string matching procedure, then the
next character shall be read and appended to the string and this step
repeated. */
s->string[s->string_length++] = buf[i++];
/* 6.4(a) The string must not exceed N7 in length */
if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
{
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
}
monitor_for_mode_change(ss);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
int len;
s = &ss->compress;
if (s->update_at)
return 0;
if (s->last_matched)
{
len = s->string_length;
send_encoded_data(s, s->last_matched);
s->flushed_length += len;
}
if (!s->transparent)
{
s->update_at = s->last_matched;
s->last_matched = 0;
s->flushed_length = 0;
push_compressed_code(s, V42BIS_FLUSH);
push_octet_alignment(s);
}
flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
v42bis_comp_state_t *s;
int i;
int j;
int yyy;
uint16_t code;
uint16_t p;
uint8_t ch;
uint8_t in;
s = &ss->decompress;
if (!s->v42bis_parm_p0)
{
/* Compression is off - just push the incoming data out */
push_octets(s, buf, len);
return 0;
}
for (i = 0; i < len; )
{
if (s->transparent)
{
in = buf[i];
if (s->escaped)
{
/* Command */
s->escaped = FALSE;
switch (in)
{
case V42BIS_ECM:
/* Enter compressed mode */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n");
send_string(s);
s->transparent = FALSE;
s->update_at = s->last_matched;
s->last_matched = 0;
i++;
continue;
case V42BIS_EID:
/* Escape symbol */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n");
in = s->escape_code;
s->escape_code += V42BIS_ESC_STEP;
break;
case V42BIS_RESET:
/* Reset dictionary */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n");
/* TODO: */
send_string(s);
dictionary_init(s);
i++;
continue;
default:
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in);
return -1;
}
}
else if (in == s->escape_code)
{
s->escaped = TRUE;
i++;
continue;
}
yyy = TRUE;
for (j = 0; j < 2 && yyy; j++)
{
if (s->update_at)
{
if (match_octet(s, s->update_at, in) == 0)
s->last_added = add_octet_to_dictionary(s, s->update_at, in);
s->update_at = 0;
}
code = match_octet(s, s->last_matched, in);
if (code == 0)
{
s->update_at = s->last_matched;
send_string(s);
s->last_matched = 0;
}
else if (code == s->last_added)
{
s->last_added = 0;
send_string(s);
s->last_matched = 0;
}
else
{
s->last_matched = code;
s->string[s->string_length++] = in;
if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
{
send_string(s);
s->last_matched = 0;
}
i++;
yyy = FALSE;
}
}
}
else
{
/* Get code from input */
while (s->bit_count < s->v42bis_parm_c2 && i < len)
{
s->bit_buffer |= buf[i++] << s->bit_count;
s->bit_count += 8;
}
if (s->bit_count < s->v42bis_parm_c2)
continue;
code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1);
s->bit_buffer >>= s->v42bis_parm_c2;
s->bit_count -= s->v42bis_parm_c2;
if (code < V42BIS_N6)
{
/* We have a control code. */
switch (code)
{
case V42BIS_ETM:
/* Enter transparent mode */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n");
s->bit_count = 0;
s->transparent = TRUE;
s->last_matched = 0;
s->last_added = 0;
break;
case V42BIS_FLUSH:
/* Flush signal */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n");
s->bit_count = 0;
break;
case V42BIS_STEPUP:
/* Increase code word size */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n");
s->v42bis_parm_c2++;
s->v42bis_parm_c3 <<= 1;
if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3))
return -1;
break;
}
continue;
}
/* Regular codeword */
if (code == s->v42bis_parm_c1)
return -1;
expand_codeword_to_string(s, code);
if (s->update_at)
{
ch = s->string[0];
if ((p = match_octet(s, s->update_at, ch)) == 0)
{
s->last_added = add_octet_to_dictionary(s, s->update_at, ch);
if (code == s->v42bis_parm_c1)
return -1;
}
else if (p == s->last_added)
{
s->last_added = 0;
}
}
s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code;
/* Allow for any escapes which may be in this string */
for (j = 0; j < s->string_length; j++)
{
if (s->string[j] == s->escape_code)
s->escape_code += V42BIS_ESC_STEP;
}
send_string(s);
}
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
int len;
s = &ss->decompress;
len = s->string_length;
send_string(s);
s->flushed_length += len;
flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode)
{
s->compress.compression_mode = mode;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
v42bis_state_t *s,
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
put_msg_func_t encode_handler,
void *encode_user_data,
int max_encode_len,
put_msg_func_t decode_handler,
void *decode_user_data,
int max_decode_len)
{
int ret;
if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE || negotiated_p1 > 65535)
return NULL;
if (negotiated_p2 < V42BIS_MIN_STRING_SIZE || negotiated_p2 > V42BIS_MAX_STRING_SIZE)
return NULL;
if (s == NULL)
{
if ((s = (v42bis_state_t *) talloc_zero_size(ctx,sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "V.42bis");
if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len)))
return NULL;
if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len)))
{
comp_exit(&s->compress);
return NULL;
}
s->compress.v42bis_parm_p0 = negotiated_p0 & 2;
s->decompress.v42bis_parm_p0 = negotiated_p0 & 1;
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s)
{
comp_exit(&s->compress);
comp_exit(&s->decompress);
talloc_free(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@ -1,6 +1,7 @@
SUBDIRS = \
llc \
rlcmac \
sndcp \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.

53
tests/sndcp/Makefile.am Normal file
View File

@ -0,0 +1,53 @@
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
-I$(top_srcdir)/include/ \
$(NULL)
AM_LDFLAGS = \
-no-install \
$(NULL)
check_PROGRAMS = \
sndcp_prim_test \
sndcp_xid_test \
slhc_test \
$(NULL)
EXTRA_DIST = \
sndcp_prim_test.err \
sndcp_prim_test.ok \
sndcp_xid_test.ok \
slhc_test.ok \
$(NULL)
sndcp_prim_test_SOURCES = sndcp_prim_test.c
# libosmo-gprs-llc.a is used below to access non-exported private symbols used in the test:
sndcp_prim_test_LDADD = \
$(top_builddir)/src/sndcp/libosmo-gprs-sndcp.la \
$(top_builddir)/src/llc/.libs/libosmo-gprs-llc.a \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
-lrt -lm \
$(NULL)
sndcp_xid_test_SOURCES = sndcp_xid_test.c
# libosmo-gprs-sndcp.a is used below to access non-exported private symbols used in the test:
sndcp_xid_test_LDADD = \
$(top_builddir)/src/sndcp/.libs/libosmo-gprs-sndcp.a \
$(top_builddir)/src/llc/libosmo-gprs-llc.la \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
-lrt -lm \
$(NULL)
slhc_test_SOURCES = slhc_test.c
# libosmo-gprs-sndcp.a is used below to access non-exported private symbols used in the test:
slhc_test_LDADD = \
$(top_builddir)/src/sndcp/.libs/libosmo-gprs-sndcp.a \
$(top_builddir)/src/llc/libosmo-gprs-llc.la \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
-lrt -lm \
$(NULL)

286
tests/sndcp/slhc_test.c Normal file
View File

@ -0,0 +1,286 @@
/* 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 <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/gprs/sndcp/slhc.h>
#include <osmocom/gprs/sndcp/sndcp.h>
#define DSNDCP 0
#define DSLHC 1
/* 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 15
char *packets[] = {
/* With TCP Option 10 (Timestamps) in place (forces UNCOMPRESSED_TCP) */
"4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27",
"4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0",
"4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01",
"4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01",
"4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a",
"4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20",
/* Regular TCP packets (COMPRESSED_TCP) */
"4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27",
"4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0",
"4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01",
"4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01",
"4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a",
"4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20",
/* UDP packets (TYPE_IP */
"450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001",
"450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e6b73c014c00c000200010000173d000603646e73c041c00c000200010000173d000a0767616e65736861c041c058000100010000173d0004d55f2e45c058001c00010000173d0010200107800045f0460000000000690001c06a0001000100006a710004d55f1b78c039000100010000173d000453ecb2cb",
"45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001",
"0050b6162c10000db93a3ff908004500004726a6000038114083080808080a0901650035ef1b00338a8cc3918180000100010000000006676f6f676c650264650000010001c00c000100010000012b0004d83ad503",
};
/* 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);
printf("packet[9]=%02x\n", packet[9]);
/* Check TCP packet */
if (packet[9] != 0x06)
return;
OSMO_ASSERT(len > 40);
OSMO_ASSERT(calc_tcpip_csum(ctx, packet, len) == 0);
}
/* 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++) {
printf("Testing with packet No. %d\n", 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));
OSMO_ASSERT(strlen(packets[i]) < sizeof(packet_ascii));
OSMO_STRLCPY_ARRAY(packet_ascii, packets[i]);
packet_len =
osmo_hexparse(packet_ascii, packet, sizeof(packet));
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: (%d bytes) %s\n", packet_len,
osmo_hexdump_nospc(packet, packet_len));
printf("DecompressedPacket: (%d bytes) %s\n",
packet_decompr_len, osmo_hexdump_nospc(packet_decompr,
packet_decompr_len));
printf("CompressedPacket: (%d 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;
void *log_ctx;
int rc;
ctx = talloc_named_const(NULL, 0, "slhc_ctx");
log_ctx = talloc_named_const(ctx, 0, "log");
osmo_init_logging2(log_ctx, &info);
rc = osmo_gprs_sndcp_init();
OSMO_ASSERT(rc == 0);
osmo_gprs_sndcp_set_log_cat(OSMO_GPRS_SNDCP_LOGC_SNDCP, DSNDCP);
osmo_gprs_sndcp_set_log_cat(OSMO_GPRS_SNDCP_LOGC_SLHC, DSLHC);
test_slhc(ctx);
printf("Done\n");
talloc_report_full(ctx, stderr);
talloc_free(log_ctx);
OSMO_ASSERT(talloc_total_blocks(ctx) == 1);
talloc_free(ctx);
return 0;
}
/* stubs */
struct osmo_prim_hdr;
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
abort();
}

154
tests/sndcp/slhc_test.ok Normal file
View File

@ -0,0 +1,154 @@
Allocating compression state...
Testing with packet No. 0
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (64 bytes) 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27
DecompressedPacket: (64 bytes) 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27
CompressedPacket: (64 bytes) 7510004046dd40004000a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27
Testing with packet No. 1
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (91 bytes) 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
DecompressedPacket: (91 bytes) 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
CompressedPacket: (91 bytes) 7510005b46de40004000a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
Testing with packet No. 2
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (55 bytes) 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01
DecompressedPacket: (55 bytes) 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01
CompressedPacket: (55 bytes) 7510003746df40004000a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01
Testing with packet No. 3
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (55 bytes) 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01
DecompressedPacket: (55 bytes) 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01
CompressedPacket: (55 bytes) 7510003746e040004000a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01
Testing with packet No. 4
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (100 bytes) 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d
DecompressedPacket: (100 bytes) 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d
CompressedPacket: (100 bytes) 7510007446e140004000a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d
Testing with packet No. 5
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (66 bytes) 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20
DecompressedPacket: (66 bytes) 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20
CompressedPacket: (66 bytes) 7510004246e240004000a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20
Testing with packet No. 6
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
DecompressedPacket: (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
CompressedPacket: (52 bytes) 7510003446dd40004000a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
Testing with packet No. 7
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
DecompressedPacket: (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
CompressedPacket: (43 bytes) df00cda4fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
Testing with packet No. 8
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01
DecompressedPacket: (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01
CompressedPacket: (9 bytes) dc00a70a5227fffd01
Testing with packet No. 9
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01
DecompressedPacket: (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01
CompressedPacket: (7 bytes) db00a706fffb01
Testing with packet No. 10
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
DecompressedPacket: (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
CompressedPacket: (68 bytes) db00c2d00d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a
Testing with packet No. 11
packet[9]=06
Compressing...
Decompressing...
packet[9]=06
Results:
Original Packet: (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20
DecompressedPacket: (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20
CompressedPacket: (18 bytes) df0021fb706f6c6c7578206c6f67696e3a20
Testing with packet No. 12
packet[9]=11
Compressing...
Decompressing...
packet[9]=11
Results:
Original Packet: (57 bytes) 450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001
DecompressedPacket: (57 bytes) 450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001
CompressedPacket: (57 bytes) 450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001
Testing with packet No. 13
packet[9]=11
Compressing...
Decompressing...
packet[9]=11
Results:
Original Packet: (100 bytes) 450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e
DecompressedPacket: (100 bytes) 450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e
CompressedPacket: (100 bytes) 450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e
Testing with packet No. 14
packet[9]=11
Compressing...
Decompressing...
packet[9]=11
Results:
Original Packet: (55 bytes) 45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001
DecompressedPacket: (55 bytes) 45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001
CompressedPacket: (55 bytes) 45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001
Freeing compression state...
Done

View File

@ -0,0 +1,208 @@
/* sndcp_prim tests
*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau espin Pedrol <pespin@sysmocom.de>
*
* All Rights Reserved
*
* 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 <stdint.h>
#include <stdio.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/sndcp/sndcp_prim.h>
#include <osmocom/gprs/sndcp/sndcp.h>
/* Included to have access to some internal functions in general only used by the LLC layer: */
#include <osmocom/gprs/llc/llc_private.h>
static void *tall_ctx = NULL;
int test_sndcp_prim_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data)
{
const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim);
if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SN) {
printf("%s(): Unexpected Rx %s\n", __func__, npdu_name);
OSMO_ASSERT(0);
}
switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_UNITDATA, PRIM_OP_INDICATION):
printf("%s(): Rx %s TLLI=0x%08x SAPI=%s NSAPI=%u NPDU=[%s]\n",
__func__, npdu_name,
sndcp_prim->sn.tlli, osmo_gprs_llc_sapi_name(sndcp_prim->sn.sapi),
sndcp_prim->sn.data_req.nsapi,
osmo_hexdump(sndcp_prim->sn.data_ind.npdu, sndcp_prim->sn.data_ind.npdu_len));
break;
default:
printf("%s(): Rx %s\n", __func__, npdu_name);
break;
};
return 0;
}
int test_sndcp_prim_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
{
const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim);
if (llc_prim->oph.sap != OSMO_GPRS_LLC_SAP_LL) {
printf("%s(): Unexpected Rx %s\n", __func__, pdu_name);
OSMO_ASSERT(0);
}
switch (OSMO_PRIM_HDR(&llc_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_REQUEST):
printf("%s(): Rx %s TLLI=0x%08x SAPI=%s L3=[%s]\n",
__func__, pdu_name,
llc_prim->ll.tlli, osmo_gprs_llc_sapi_name(llc_prim->ll.sapi),
osmo_hexdump(llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len));
break;
default:
printf("%s(): Rx %s\n", __func__, pdu_name);
break;
};
return 0;
}
int test_sndcp_prim_snsm_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data)
{
const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim);
if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SN) {
printf("%s(): Unexpected Rx %s\n", __func__, npdu_name);
OSMO_ASSERT(0);
}
printf("%s(): Rx %s\n", __func__, npdu_name);
return 0;
}
/*
Subnetwork Dependent Convergence Protocol
Address field NSAPI: Unknown (101)
No compression
Unacknowledged mode, N-PDU 0 (segment 0)
*/
static const char sndcp_data_hex[] = "650000007522fa7cffd956ba86c72af3d21dcebc883a8eb6247dde266a58de29be96d240947f9f4df59c61d419e99a58752121a9fd1fd79690a73ffe5b59f0e7c1522caaa139b6c5fd682efcf4c0109b7a649dae3affa30fb0567d4b5233741367446ace6245eacb";
/* Example of a real world SNDCP-XID message */
static const uint8_t sndcp_xid[] = {
0x00, 0x01, 0x00, 0x02, 0x31, 0x82, 0x02, 0x27, 0x89, 0xff, 0xe0,
0x00, 0x0f, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02,
0x01, 0x02, 0x00, 0x03, 0x01, 0x03, 0x00, 0x04, 0x01, 0x04, 0x00, 0x05,
0x01, 0x05, 0x00, 0x06, 0x00, 0x07, 0x01, 0x07, 0x00, 0x08, 0x01, 0x08,
0x80, 0x00, 0x04, 0x12, 0x00, 0x40, 0x07 };
static void test_sndcp_prim(void)
{
struct osmo_gprs_sndcp_prim *sndcp_prim;
struct osmo_gprs_llc_prim *llc_prim;
const uint32_t tlli = 0xe1c5d364;
const enum osmo_gprs_llc_sapi ll_sapi = OSMO_GPRS_LLC_SAPI_SNDCP3;
const uint8_t nsapi = 0x05;
uint8_t sndcp_data[1024];
int rc;
printf("==== %s() [start] ====\n", __func__);
rc = osmo_gprs_sndcp_init();
OSMO_ASSERT(rc == 0);
osmo_gprs_sndcp_prim_set_up_cb(test_sndcp_prim_up_cb, NULL);
osmo_gprs_sndcp_prim_set_down_cb(test_sndcp_prim_down_cb, NULL);
osmo_gprs_sndcp_prim_set_snsm_cb(test_sndcp_prim_snsm_cb, NULL);
/* SNSM-ACTIVATE.Ind: */
sndcp_prim = osmo_gprs_sndcp_prim_alloc_snsm_activate_ind(tlli, nsapi, ll_sapi);
OSMO_ASSERT(sndcp_prim);
rc = osmo_gprs_sndcp_prim_dispatch_snsm(sndcp_prim);
OSMO_ASSERT(rc == 0);
/* SN-XID.Req: */
sndcp_prim = osmo_gprs_sndcp_prim_alloc_sn_xid_req(tlli, ll_sapi, nsapi);
OSMO_ASSERT(sndcp_prim);
sndcp_prim->sn.xid_req.pcomp_rfc1144.active = true;
sndcp_prim->sn.xid_req.pcomp_rfc1144.s01 = 7;
rc = osmo_gprs_sndcp_prim_upper_down(sndcp_prim);
OSMO_ASSERT(rc == 0);
/* SN-XID.Cnf: */
llc_prim = gprs_llc_prim_alloc_ll_xid_cnf(tlli, ll_sapi, (uint8_t *)sndcp_xid, sizeof(sndcp_xid));
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_sndcp_prim_lower_up(llc_prim);
OSMO_ASSERT(rc == 0);
/* SN-XID.Ind: */
llc_prim = gprs_llc_prim_alloc_ll_xid_ind(tlli, ll_sapi, (uint8_t *)sndcp_xid, sizeof(sndcp_xid));
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_sndcp_prim_lower_up(llc_prim);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(osmo_hexparse(sndcp_data_hex, sndcp_data, sizeof(sndcp_data)) > 0);
llc_prim = gprs_llc_prim_alloc_ll_unitdata_ind(tlli, ll_sapi, (uint8_t *)sndcp_data, sizeof(sndcp_data));
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_sndcp_prim_lower_up(llc_prim);
OSMO_ASSERT(rc == 0);
char ndpu_data[] = "some-npdu-data-like-an-ip-pkt";
sndcp_prim = osmo_gprs_sndcp_prim_alloc_sn_unitdata_req(tlli, ll_sapi, nsapi, (uint8_t *)ndpu_data, sizeof(ndpu_data));
OSMO_ASSERT(sndcp_prim);
rc = osmo_gprs_sndcp_prim_upper_down(sndcp_prim);
OSMO_ASSERT(rc == 0);
/* TODO: SN-XID REQ / IND / RESP / CONF
* TODO: Other primitivbes coming from LLC layer
*/
/* SNSM-DEACTIVATE.Ind: */
sndcp_prim = osmo_gprs_sndcp_prim_alloc_snsm_deactivate_ind(tlli, nsapi);
OSMO_ASSERT(sndcp_prim);
rc = osmo_gprs_sndcp_prim_dispatch_snsm(sndcp_prim);
OSMO_ASSERT(rc == 0);
printf("==== %s() [end] ====\n", __func__);
}
static const struct log_info_cat test_log_categories[] = { };
static const struct log_info test_log_info = {
.cat = test_log_categories,
.num_cat = ARRAY_SIZE(test_log_categories),
};
int main(int argc, char *argv[])
{
tall_ctx = talloc_named_const(NULL, 1, __FILE__);
osmo_init_logging2(tall_ctx, &test_log_info);
log_parse_category_mask(osmo_stderr_target, "DLGLOBAL,1:");
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_level(osmo_stderr_target, 1);
log_set_use_color(osmo_stderr_target, 0);
test_sndcp_prim();
talloc_free(tall_ctx);
}

View File

@ -0,0 +1,185 @@
DLGLOBAL INFO Rx from SNDCP SM sublayer: SNSM-ACTIVATE.indication
DLGLOBAL INFO SNSM-ACTIVATE.ind (TLLI=e1c5d364, SAPI=3, NSAPI=5)
DLGLOBAL INFO Rx from upper layers: SN-XID.request
DLGLOBAL DEBUG SNE(e1c5d364,SNDCP3,5) SN-XID.req comp_fields:
DLGLOBAL DEBUG SNDCP-XID:
DLGLOBAL DEBUG struct gprs_sndcp_comp_field {
DLGLOBAL DEBUG entity=0;
DLGLOBAL DEBUG algo=0;
DLGLOBAL DEBUG comp_len=2;
DLGLOBAL DEBUG comp[0]=1;
DLGLOBAL DEBUG comp[1]=2;
DLGLOBAL DEBUG gprs_sndcp_pcomp_rfc1144_params {
DLGLOBAL DEBUG nsapi_len=1;
DLGLOBAL DEBUG nsapi[0]=5;
DLGLOBAL DEBUG s01=7;
DLGLOBAL DEBUG }
DLGLOBAL DEBUG }
DLGLOBAL INFO Rx from lower layers: LL-XID.confirm
DLGLOBAL DEBUG SNE(e1c5d364,SNDCP3,5) LL-XID.cnf: Found SNE SAPI=3
DLGLOBAL DEBUG LL-XID.cnf response comp_fields:
DLGLOBAL DEBUG SNDCP-XID:
DLGLOBAL DEBUG struct gprs_sndcp_comp_field {
DLGLOBAL DEBUG entity=0;
DLGLOBAL DEBUG algo=0;
DLGLOBAL DEBUG comp_len=2;
DLGLOBAL DEBUG comp[0]=1;
DLGLOBAL DEBUG comp[1]=2;
DLGLOBAL DEBUG gprs_sndcp_pcomp_rfc1144_params {
DLGLOBAL DEBUG nsapi_len=1;
DLGLOBAL DEBUG nsapi[0]=6;
DLGLOBAL DEBUG s01=7;
DLGLOBAL DEBUG }
DLGLOBAL DEBUG }
DLGLOBAL DEBUG SNDCP-XID:
DLGLOBAL DEBUG struct gprs_sndcp_comp_field {
DLGLOBAL DEBUG entity=2;
DLGLOBAL DEBUG algo=2;
DLGLOBAL DEBUG comp_len=2;
DLGLOBAL DEBUG comp[0]=8;
DLGLOBAL DEBUG comp[1]=9;
DLGLOBAL DEBUG gprs_sndcp_pcomp_rohc_params {
DLGLOBAL DEBUG nsapi_len=11;
DLGLOBAL DEBUG nsapi[0]=5;
DLGLOBAL DEBUG nsapi[1]=6;
DLGLOBAL DEBUG nsapi[2]=7;
DLGLOBAL DEBUG nsapi[3]=8;
DLGLOBAL DEBUG nsapi[4]=9;
DLGLOBAL DEBUG nsapi[5]=10;
DLGLOBAL DEBUG nsapi[6]=11;
DLGLOBAL DEBUG nsapi[7]=12;
DLGLOBAL DEBUG nsapi[8]=13;
DLGLOBAL DEBUG nsapi[9]=14;
DLGLOBAL DEBUG nsapi[10]=15;
DLGLOBAL DEBUG max_cid=15;
DLGLOBAL DEBUG max_header=168;
DLGLOBAL DEBUG profile_len=16;
DLGLOBAL DEBUG profile[0]=0000;
DLGLOBAL DEBUG profile[1]=0001;
DLGLOBAL DEBUG profile[2]=0101;
DLGLOBAL DEBUG profile[3]=0002;
DLGLOBAL DEBUG profile[4]=0102;
DLGLOBAL DEBUG profile[5]=0003;
DLGLOBAL DEBUG profile[6]=0103;
DLGLOBAL DEBUG profile[7]=0004;
DLGLOBAL DEBUG profile[8]=0104;
DLGLOBAL DEBUG profile[9]=0005;
DLGLOBAL DEBUG profile[10]=0105;
DLGLOBAL DEBUG profile[11]=0006;
DLGLOBAL DEBUG profile[12]=0007;
DLGLOBAL DEBUG profile[13]=0107;
DLGLOBAL DEBUG profile[14]=0008;
DLGLOBAL DEBUG profile[15]=0108;
DLGLOBAL DEBUG }
DLGLOBAL DEBUG }
DLGLOBAL DEBUG SNME(e1c5d364) Rejecting RFC1144 header compression...
DLGLOBAL DEBUG SNME(e1c5d364) Rejecting ROHC header compression...
DLGLOBAL INFO Rx from lower layers: LL-XID.indication
DLGLOBAL DEBUG SNME(e1c5d364) LL-XID.cnf requested comp_fields:
DLGLOBAL DEBUG SNDCP-XID:
DLGLOBAL DEBUG struct gprs_sndcp_comp_field {
DLGLOBAL DEBUG entity=0;
DLGLOBAL DEBUG algo=0;
DLGLOBAL DEBUG comp_len=2;
DLGLOBAL DEBUG comp[0]=1;
DLGLOBAL DEBUG comp[1]=2;
DLGLOBAL DEBUG gprs_sndcp_pcomp_rfc1144_params {
DLGLOBAL DEBUG nsapi_len=1;
DLGLOBAL DEBUG nsapi[0]=6;
DLGLOBAL DEBUG s01=7;
DLGLOBAL DEBUG }
DLGLOBAL DEBUG }
DLGLOBAL DEBUG SNDCP-XID:
DLGLOBAL DEBUG struct gprs_sndcp_comp_field {
DLGLOBAL DEBUG entity=2;
DLGLOBAL DEBUG algo=2;
DLGLOBAL DEBUG comp_len=2;
DLGLOBAL DEBUG comp[0]=8;
DLGLOBAL DEBUG comp[1]=9;
DLGLOBAL DEBUG gprs_sndcp_pcomp_rohc_params {
DLGLOBAL DEBUG nsapi_len=11;
DLGLOBAL DEBUG nsapi[0]=5;
DLGLOBAL DEBUG nsapi[1]=6;
DLGLOBAL DEBUG nsapi[2]=7;
DLGLOBAL DEBUG nsapi[3]=8;
DLGLOBAL DEBUG nsapi[4]=9;
DLGLOBAL DEBUG nsapi[5]=10;
DLGLOBAL DEBUG nsapi[6]=11;
DLGLOBAL DEBUG nsapi[7]=12;
DLGLOBAL DEBUG nsapi[8]=13;
DLGLOBAL DEBUG nsapi[9]=14;
DLGLOBAL DEBUG nsapi[10]=15;
DLGLOBAL DEBUG max_cid=15;
DLGLOBAL DEBUG max_header=168;
DLGLOBAL DEBUG profile_len=16;
DLGLOBAL DEBUG profile[0]=0000;
DLGLOBAL DEBUG profile[1]=0001;
DLGLOBAL DEBUG profile[2]=0101;
DLGLOBAL DEBUG profile[3]=0002;
DLGLOBAL DEBUG profile[4]=0102;
DLGLOBAL DEBUG profile[5]=0003;
DLGLOBAL DEBUG profile[6]=0103;
DLGLOBAL DEBUG profile[7]=0004;
DLGLOBAL DEBUG profile[8]=0104;
DLGLOBAL DEBUG profile[9]=0005;
DLGLOBAL DEBUG profile[10]=0105;
DLGLOBAL DEBUG profile[11]=0006;
DLGLOBAL DEBUG profile[12]=0007;
DLGLOBAL DEBUG profile[13]=0107;
DLGLOBAL DEBUG profile[14]=0008;
DLGLOBAL DEBUG profile[15]=0108;
DLGLOBAL DEBUG }
DLGLOBAL DEBUG }
DLGLOBAL DEBUG SNME(e1c5d364) Rejecting RFC1144 header compression...
DLGLOBAL DEBUG SNME(e1c5d364) Rejecting ROHC header compression...
DLGLOBAL DEBUG SNME(e1c5d364) SN-XID.rsp comp_fields:
DLGLOBAL DEBUG SNDCP-XID:
DLGLOBAL DEBUG struct gprs_sndcp_comp_field {
DLGLOBAL DEBUG entity=0;
DLGLOBAL DEBUG algo=0;
DLGLOBAL DEBUG comp_len=2;
DLGLOBAL DEBUG comp[0]=1;
DLGLOBAL DEBUG comp[1]=2;
DLGLOBAL DEBUG gprs_sndcp_pcomp_rfc1144_params {
DLGLOBAL DEBUG nsapi_len=0;
DLGLOBAL DEBUG nsapi[] = NULL;
DLGLOBAL DEBUG s01=7;
DLGLOBAL DEBUG }
DLGLOBAL DEBUG }
DLGLOBAL DEBUG SNDCP-XID:
DLGLOBAL DEBUG struct gprs_sndcp_comp_field {
DLGLOBAL DEBUG entity=2;
DLGLOBAL DEBUG algo=2;
DLGLOBAL DEBUG comp_len=2;
DLGLOBAL DEBUG comp[0]=8;
DLGLOBAL DEBUG comp[1]=9;
DLGLOBAL DEBUG gprs_sndcp_pcomp_rohc_params {
DLGLOBAL DEBUG nsapi_len=0;
DLGLOBAL DEBUG nsapi[] = NULL;
DLGLOBAL DEBUG max_cid=15;
DLGLOBAL DEBUG max_header=168;
DLGLOBAL DEBUG profile_len=16;
DLGLOBAL DEBUG profile[0]=0000;
DLGLOBAL DEBUG profile[1]=0001;
DLGLOBAL DEBUG profile[2]=0101;
DLGLOBAL DEBUG profile[3]=0002;
DLGLOBAL DEBUG profile[4]=0102;
DLGLOBAL DEBUG profile[5]=0003;
DLGLOBAL DEBUG profile[6]=0103;
DLGLOBAL DEBUG profile[7]=0004;
DLGLOBAL DEBUG profile[8]=0104;
DLGLOBAL DEBUG profile[9]=0005;
DLGLOBAL DEBUG profile[10]=0105;
DLGLOBAL DEBUG profile[11]=0006;
DLGLOBAL DEBUG profile[12]=0007;
DLGLOBAL DEBUG profile[13]=0107;
DLGLOBAL DEBUG profile[14]=0008;
DLGLOBAL DEBUG profile[15]=0108;
DLGLOBAL DEBUG }
DLGLOBAL DEBUG }
DLGLOBAL INFO Rx from lower layers: LL-UNITDATA.indication
DLGLOBAL INFO Rx from upper layers: SN-UNITDATA.request
DLGLOBAL INFO Rx from SNDCP SM sublayer: SNSM-DEACTIVATE.indication
DLGLOBAL DEBUG SNE(e1c5d364,SNDCP3,5) free()
DLGLOBAL DEBUG SNME(e1c5d364) No SNDCP Entities left activate, freeing SNME
DLGLOBAL DEBUG SNME(e1c5d364) free()

View File

@ -0,0 +1,6 @@
==== test_sndcp_prim() [start] ====
test_sndcp_prim_down_cb(): Rx LL-XID.request
test_sndcp_prim_down_cb(): Rx LL-XID.response
test_sndcp_prim_up_cb(): Rx SN-UNITDATA.indication TLLI=0xe1c5d364 SAPI=SNDCP3 NSAPI=5 NPDU=[75 22 fa 7c ff d9 56 ba 86 c7 2a f3 d2 1d ce bc 88 3a 8e b6 24 7d de 26 6a 58 de 29 be 96 d2 40 94 7f 9f 4d f5 9c 61 d4 19 e9 9a 58 75 21 21 a9 fd 1f d7 96 90 a7 3f fe 5b 59 f0 e7 c1 52 2c aa a1 39 b6 c5 fd 68 2e fc f4 c0 10 9b 7a 64 9d ae 3a ff a3 0f b0 56 7d 4b 52 33 74 13 67 44 6a ce 62 45 ea cb 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]
test_sndcp_prim_down_cb(): Rx LL-UNITDATA.request TLLI=0xe1c5d364 SAPI=SNDCP3 L3=[65 00 00 00 73 6f 6d 65 2d 6e 70 64 75 2d 64 61 74 61 2d 6c 69 6b 65 2d 61 6e 2d 69 70 2d 70 6b 74 00 ]
==== test_sndcp_prim() [end] ====

View File

@ -0,0 +1,294 @@
/* Test SNDCP-XID Encoding/Decoding */
/* (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 <stdio.h>
#include <string.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/gprs/sndcp/xid.h>
#include <osmocom/gprs/sndcp/sndcp.h>
#define DSNDCP 0
/* Test SNDCP-XID decoding with a real world sample */
static void test_xid_decode_realworld(const void *ctx)
{
struct llist_head *comp_fields;
int rc;
printf("Testing SNDCP XID-Decoder/Encoder (real world data)\n");
/* Example of a real world SNDCP-XID message */
uint8_t xid[] = {
0x00, 0x01, 0x00, 0x02, 0x31, 0x82, 0x02, 0x27, 0x89, 0xff, 0xe0,
0x00, 0x0f, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02,
0x01, 0x02, 0x00, 0x03, 0x01, 0x03, 0x00, 0x04, 0x01, 0x04, 0x00, 0x05,
0x01, 0x05, 0x00, 0x06, 0x00, 0x07, 0x01, 0x07, 0x00, 0x08, 0x01, 0x08,
0x80, 0x00, 0x04, 0x12, 0x00, 0x40, 0x07 };
uint8_t xid_r[512];
/* Parse and show contained comp fields */
comp_fields = gprs_sndcp_parse_xid(NULL, ctx, xid, sizeof(xid), NULL);
OSMO_ASSERT(comp_fields);
printf("Decoded:\n");
gprs_sndcp_dump_comp_fields(comp_fields, DSNDCP);
/* Encode comp-fields again */
rc = gprs_sndcp_compile_xid(xid_r, sizeof(xid_r), comp_fields,
DEFAULT_SNDCP_VERSION);
printf("Result length=%d\n", rc);
printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid)));
printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc));
OSMO_ASSERT(rc == 54);
OSMO_ASSERT(memcmp(xid, xid_r, sizeof(xid)) == 0);
/* Free comp fields */
talloc_free(comp_fields);
printf("\n");
}
/* Encode and decode test with artificial test data */
static void test_xid_encode_decode(const void *ctx)
{
printf("Testing SNDCP XID-Encoder/Decoder\n");
LLIST_HEAD(comp_fields);
struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params;
struct gprs_sndcp_comp_field rfc1144_comp_field;
struct gprs_sndcp_pcomp_rfc2507_params rfc2507_params;
struct gprs_sndcp_comp_field rfc2507_comp_field;
struct gprs_sndcp_pcomp_rohc_params rohc_params;
struct gprs_sndcp_comp_field rohc_comp_field;
struct gprs_sndcp_dcomp_v42bis_params v42bis_params;
struct gprs_sndcp_comp_field v42bis_comp_field;
struct gprs_sndcp_dcomp_v44_params v44_params;
struct gprs_sndcp_comp_field v44_comp_field;
struct llist_head *comp_fields_dec;
uint8_t xid[512];
unsigned int xid_len = sizeof(xid);
int rc;
memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
memset(&rfc2507_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
memset(&rohc_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
memset(&v44_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
/* Setup which NSAPIs shall make use of rfc1144 */
rfc1144_params.nsapi[0] = 5;
rfc1144_params.nsapi_len = 1;
/* Setup rfc1144 operating parameters */
rfc1144_params.s01 = 7;
/* Setup rfc1144 compression field */
rfc1144_comp_field.p = 1;
rfc1144_comp_field.entity = 0;
rfc1144_comp_field.algo.pcomp = RFC_1144;
rfc1144_comp_field.comp[RFC1144_PCOMP1] = 1;
rfc1144_comp_field.comp[RFC1144_PCOMP2] = 2;
rfc1144_comp_field.comp_len = RFC1144_PCOMP_NUM;
rfc1144_comp_field.rfc1144_params = &rfc1144_params;
/* Setup which NSAPIs shall make use of rfc1144 */
rfc2507_params.nsapi[0] = 6;
rfc2507_params.nsapi_len = 1;
/* Setup rfc2507 operating parameters */
rfc2507_params.f_max_period = 256;
rfc2507_params.f_max_time = 5;
rfc2507_params.max_header = 168;
rfc2507_params.tcp_space = 15;
rfc2507_params.non_tcp_space = 15;
/* Setup rfc2507 compression field */
rfc2507_comp_field.p = 1;
rfc2507_comp_field.entity = 1;
rfc2507_comp_field.algo.pcomp = RFC_2507;
rfc2507_comp_field.comp[RFC2507_PCOMP1] = 3;
rfc2507_comp_field.comp[RFC2507_PCOMP2] = 4;
rfc2507_comp_field.comp[RFC2507_PCOMP3] = 5;
rfc2507_comp_field.comp[RFC2507_PCOMP4] = 6;
rfc2507_comp_field.comp[RFC2507_PCOMP5] = 7;
rfc2507_comp_field.comp_len = RFC2507_PCOMP_NUM;
rfc2507_comp_field.rfc2507_params = &rfc2507_params;
/* Setup which NSAPIs shall make use of ROHC */
rohc_params.nsapi[0] = 5;
rohc_params.nsapi[1] = 6;
rohc_params.nsapi[2] = 7;
rohc_params.nsapi[3] = 8;
rohc_params.nsapi[4] = 9;
rohc_params.nsapi[5] = 10;
rohc_params.nsapi[6] = 11;
rohc_params.nsapi[7] = 12;
rohc_params.nsapi[8] = 13;
rohc_params.nsapi[9] = 14;
rohc_params.nsapi[10] = 15;
rohc_params.nsapi_len = 11;
/* Setup ROHC operating parameters */
rohc_params.max_cid = 15; /* default */
rohc_params.max_header = 168; /* default */
rohc_params.profile[0] = ROHC_UNCOMPRESSED;
rohc_params.profile[1] = ROHC_RTP;
rohc_params.profile[2] = ROHCV2_RTP;
rohc_params.profile[3] = ROHC_UDP;
rohc_params.profile[4] = ROHCv2_UDP;
rohc_params.profile[5] = ROHC_ESP;
rohc_params.profile[6] = ROHCV2_ESP;
rohc_params.profile[7] = ROHC_IP;
rohc_params.profile[8] = ROHCV2_IP;
rohc_params.profile[9] = ROHC_LLA;
rohc_params.profile[10] = ROHC_LLA_WITH_R_MODE;
rohc_params.profile[11] = ROHC_TCP;
rohc_params.profile[12] = ROHC_RTP_UDP_LITE;
rohc_params.profile[13] = ROHCV2_RTP_UDP_LITE;
rohc_params.profile[14] = ROHC_UDP_LITE;
rohc_params.profile[15] = ROHCV2_UDP_LITE;
rohc_params.profile_len = 16;
/* Setup ROHC compression field */
rohc_comp_field.p = 1;
rohc_comp_field.entity = 2;
rohc_comp_field.algo.pcomp = ROHC;
rohc_comp_field.comp[ROHC_PCOMP1] = 8;
rohc_comp_field.comp[ROHC_PCOMP2] = 9;
rohc_comp_field.comp_len = ROHC_PCOMP_NUM;
rohc_comp_field.rohc_params = &rohc_params;
/* Setup which NSAPIs shall make use of v42bis */
v42bis_params.nsapi[0] = 5;
v42bis_params.nsapi_len = 1;
/* Setup v42bis operating parameters */
v42bis_params.p0 = 3;
v42bis_params.p1 = 2048;
v42bis_params.p2 = 20;
/* Setup v42bis compression field */
v42bis_comp_field.p = 1;
v42bis_comp_field.entity = 3;
v42bis_comp_field.algo.dcomp = V42BIS;
v42bis_comp_field.comp[V42BIS_DCOMP1] = 10;
v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM;
v42bis_comp_field.v42bis_params = &v42bis_params;
/* Setup which NSAPIs shall make use of v44 */
v44_params.nsapi[0] = 5;
v44_params.nsapi_len = 1;
/* Setup v44 operating parameters */
v44_params.c0 = 0x80;
v44_params.p0 = 3;
v44_params.p1t = 300;
v44_params.p1r = 300;
v44_params.p3t = 600;
v44_params.p3r = 600;
/* Setup v44 compression field */
v44_comp_field.p = 1;
v44_comp_field.entity = 3;
v44_comp_field.algo.dcomp = V44;
v44_comp_field.comp[V44_DCOMP1] = 10;
v44_comp_field.comp[V44_DCOMP2] = 11;
v44_comp_field.comp_len = V44_DCOMP_NUM;
v44_comp_field.v44_params = &v44_params;
/* Add compression field(s) to list */
llist_add(&v44_comp_field.list, &comp_fields);
llist_add(&v42bis_comp_field.list, &comp_fields);
llist_add(&rfc1144_comp_field.list, &comp_fields);
llist_add(&rfc2507_comp_field.list, &comp_fields);
llist_add(&rohc_comp_field.list, &comp_fields);
printf("Test input data:\n");
gprs_sndcp_dump_comp_fields(&comp_fields, DSNDCP);
/* Encode SNDCP-XID fields */
rc = gprs_sndcp_compile_xid(xid, xid_len, &comp_fields,
DEFAULT_SNDCP_VERSION);
OSMO_ASSERT(rc > 0);
printf("Encoded: %s (%d bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
/* Parse and show contained comp fields */
comp_fields_dec = gprs_sndcp_parse_xid(NULL, ctx, xid, rc, NULL);
OSMO_ASSERT(comp_fields_dec);
printf("Decoded:\n");
gprs_sndcp_dump_comp_fields(comp_fields_dec, DSNDCP);
/* Free comp fields */
talloc_free(comp_fields_dec);
}
static struct log_info_cat gprs_categories[] = {
[DSNDCP] = {
.name = "DSNDCP",
.description =
"GPRS Sub-Network Dependent Control Protocol (SNDCP)",
.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 *xid_ctx;
void *log_ctx;
int rc;
xid_ctx = talloc_named_const(NULL, 0, "xid_ctx");
log_ctx = talloc_named_const(xid_ctx, 0, "log");
osmo_init_logging2(log_ctx, &info);
rc = osmo_gprs_sndcp_init();
OSMO_ASSERT(rc == 0);
osmo_gprs_sndcp_set_log_cat(OSMO_GPRS_SNDCP_LOGC_SNDCP, DSNDCP);
test_xid_decode_realworld(xid_ctx);
test_xid_encode_decode(xid_ctx);
printf("Done\n");
talloc_report_full(xid_ctx, stderr);
talloc_free(log_ctx);
OSMO_ASSERT(talloc_total_blocks(xid_ctx) == 1);
talloc_free(xid_ctx);
return 0;
}
/* stubs */
struct osmo_prim_hdr;
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
abort();
}

View File

@ -0,0 +1,11 @@
Testing SNDCP XID-Decoder/Encoder (real world data)
Decoded:
Result length=54
Encoded: 000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007
Rencoded: 000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007
Testing SNDCP XID-Encoder/Decoder
Test input data:
Encoded: 000100011a83010dab00208003012c012c02580258830007a000200308001402408000041200200781010c3456700040010005a80f000f82022789ffe0000f00a80000000101010002010200030103000401040005010500060007010700080108 (97 bytes)
Decoded:
Done

View File

@ -35,3 +35,22 @@ cat $abs_srcdir/rlcmac/ts_44_060_test.ok > expout
cat $abs_srcdir/rlcmac/ts_44_060_test.err > experr
AT_CHECK([$abs_top_builddir/tests/rlcmac/ts_44_060_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([sndcp/sndcp_prim])
AT_KEYWORDS([sndcp sndcp_prim])
cat $abs_srcdir/sndcp/sndcp_prim_test.ok > expout
cat $abs_srcdir/sndcp/sndcp_prim_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sndcp/sndcp_prim_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([sndcp/sndcp_xid])
AT_KEYWORDS([sndcp sndcp_xid])
cat $abs_srcdir/sndcp/sndcp_xid_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/sndcp/sndcp_xid_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([sndcp/slhc])
AT_KEYWORDS([sndcp slhc])
cat $abs_srcdir/sndcp/slhc_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/sndcp/slhc_test], [], [expout], [ignore])
AT_CLEANUP