SNDCP: add RFC1144 header compression functionality
- Add module to handle compression entities - Add module to control header compression - Introduce VTY commands for heade compression configuration - Add changes in sndcp and llc to integrate header compression Change-Id: Ia00260dc09978844c2865957b4d43000b78b5e43
This commit is contained in:
parent
4948209446
commit
91df16c6d9
|
@ -25,6 +25,8 @@ noinst_HEADERS = \
|
|||
gprs_llc_xid.h \
|
||||
gprs_sgsn.h \
|
||||
gprs_sndcp.h \
|
||||
gprs_sndcp_comp.h \
|
||||
gprs_sndcp_pcomp.h \
|
||||
gprs_sndcp_xid.h \
|
||||
gprs_utils.h \
|
||||
gsm_04_08.h \
|
||||
|
|
|
@ -174,6 +174,15 @@ struct gprs_llc_llme {
|
|||
* able to create the compression entity. */
|
||||
struct llist_head *xid;
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
|
|
@ -21,6 +21,16 @@ struct defrag_state {
|
|||
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 */
|
||||
|
@ -50,4 +60,20 @@ struct gprs_sndcp_entity {
|
|||
|
||||
extern struct llist_head gprs_sndcp_entities;
|
||||
|
||||
/* Set of SNDCP-XID negotiation (See also: TS 144 065,
|
||||
* Section 6.8 XID parameter negotiation) */
|
||||
int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi);
|
||||
|
||||
/* Process SNDCP-XID indication (See also: TS 144 065,
|
||||
* Section 6.8 XID parameter negotiation) */
|
||||
int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
|
||||
struct gprs_llc_xid_field *xid_field_response,
|
||||
struct gprs_llc_lle *lle);
|
||||
|
||||
/* Process SNDCP-XID indication
|
||||
* (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
|
||||
int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
|
||||
struct gprs_llc_xid_field *xid_field_request,
|
||||
struct gprs_llc_lle *lle);
|
||||
|
||||
#endif /* INT_SNDCP_H */
|
||||
|
|
|
@ -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 <openbsc/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 */
|
||||
int algo; /* Algorithm type (see gprs_sndcp_xid.h) */
|
||||
int 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);
|
|
@ -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 <openbsc/gprs_sndcp_comp.h>
|
||||
|
||||
/* Note: The decompressed packet may have a maximum size of:
|
||||
* Return value + MAX_DECOMPR_INCR */
|
||||
#define MAX_HDRDECOMPR_INCR 64
|
||||
|
||||
/* Initalize 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);
|
|
@ -93,6 +93,13 @@ struct sgsn_config {
|
|||
int dynamic_lookup;
|
||||
|
||||
struct oap_config oap;
|
||||
|
||||
/* RFC1144 TCP/IP header compression */
|
||||
struct {
|
||||
int active;
|
||||
int passive;
|
||||
int s01;
|
||||
} pcomp_rfc1144;
|
||||
};
|
||||
|
||||
struct sgsn_instance {
|
||||
|
|
|
@ -71,6 +71,8 @@ osmo_sgsn_SOURCES = \
|
|||
gprs_gmm.c \
|
||||
gprs_sgsn.c \
|
||||
gprs_sndcp.c \
|
||||
gprs_sndcp_comp.c \
|
||||
gprs_sndcp_pcomp.c \
|
||||
gprs_sndcp_vty.c \
|
||||
gprs_sndcp_xid.c \
|
||||
sgsn_main.c \
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <openbsc/crc24.h>
|
||||
#include <openbsc/sgsn.h>
|
||||
#include <openbsc/gprs_llc_xid.h>
|
||||
#include <openbsc/gprs_sndcp_comp.h>
|
||||
#include <openbsc/gprs_sndcp.h>
|
||||
|
||||
static struct gprs_llc_llme *llme_alloc(uint32_t tlli);
|
||||
|
@ -140,6 +141,16 @@ static int gprs_llc_process_xid_conf(uint8_t *bytes, int bytes_len,
|
|||
|
||||
struct llist_head *xid_fields;
|
||||
struct gprs_llc_xid_field *xid_field;
|
||||
struct gprs_llc_xid_field *xid_field_request;
|
||||
struct gprs_llc_xid_field *xid_field_request_l3 = NULL;
|
||||
|
||||
/* Pick layer3 XID from the XID request we have sent last */
|
||||
if (lle->llme->xid) {
|
||||
llist_for_each_entry(xid_field_request, lle->llme->xid, list) {
|
||||
if (xid_field_request->type == GPRS_LLC_XID_T_L3_PAR)
|
||||
xid_field_request_l3 = xid_field_request;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse and analyze XID-Response */
|
||||
xid_fields = gprs_llc_parse_xid(NULL, bytes, bytes_len);
|
||||
|
@ -150,12 +161,10 @@ static int gprs_llc_process_xid_conf(uint8_t *bytes, int bytes_len,
|
|||
llist_for_each_entry(xid_field, xid_fields, list) {
|
||||
|
||||
/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
|
||||
if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) {
|
||||
LOGP(DLLC, LOGL_NOTICE,
|
||||
"Ignoring SNDCP-XID-Field: XID: type=%i, data_len=%i, data=%s\n",
|
||||
xid_field->type, xid_field->data_len,
|
||||
osmo_hexdump_nospc(xid_field->data,
|
||||
xid_field->data_len));
|
||||
if (xid_field->type == GPRS_LLC_XID_T_L3_PAR &&
|
||||
xid_field_request_l3) {
|
||||
sndcp_sn_xid_conf(xid_field,
|
||||
xid_field_request_l3, lle);
|
||||
}
|
||||
|
||||
/* Process LLC-XID fields: */
|
||||
|
@ -204,10 +213,6 @@ static int gprs_llc_process_xid_ind(uint8_t *bytes_request,
|
|||
struct gprs_llc_xid_field *xid_field;
|
||||
struct gprs_llc_xid_field *xid_field_response;
|
||||
|
||||
/* Flush eventually pending XID fields */
|
||||
talloc_free(lle->llme->xid);
|
||||
lle->llme->xid = NULL;
|
||||
|
||||
/* Parse and analyze XID-Request */
|
||||
xid_fields =
|
||||
gprs_llc_parse_xid(lle->llme, bytes_request, bytes_request_len);
|
||||
|
@ -239,6 +244,23 @@ static int gprs_llc_process_xid_ind(uint8_t *bytes_request,
|
|||
}
|
||||
}
|
||||
|
||||
/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
|
||||
llist_for_each_entry(xid_field, xid_fields, list) {
|
||||
if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) {
|
||||
|
||||
xid_field_response =
|
||||
talloc_zero(lle->llme,
|
||||
struct gprs_llc_xid_field);
|
||||
rc = sndcp_sn_xid_ind(xid_field,
|
||||
xid_field_response, lle);
|
||||
if (rc == 0)
|
||||
llist_add(&xid_field_response->list,
|
||||
xid_fields_response);
|
||||
else
|
||||
talloc_free(xid_field_response);
|
||||
}
|
||||
}
|
||||
|
||||
rc = gprs_llc_compile_xid(bytes_response,
|
||||
bytes_response_maxlen,
|
||||
xid_fields_response);
|
||||
|
@ -269,9 +291,13 @@ static void rx_llc_xid(struct gprs_llc_lle *lle,
|
|||
gprs_llc_process_xid_ind(gph->data, gph->data_len,
|
||||
response, sizeof(response),
|
||||
lle);
|
||||
xid = msgb_put(resp, response_len);
|
||||
memcpy(xid, response, response_len);
|
||||
|
||||
if (response_len < 0) {
|
||||
LOGP(DLLC, LOGL_ERROR,
|
||||
"invalid XID indication received!\n");
|
||||
} else {
|
||||
xid = msgb_put(resp, response_len);
|
||||
memcpy(xid, response, response_len);
|
||||
}
|
||||
gprs_llc_tx_xid(lle, resp, 0);
|
||||
} else {
|
||||
LOGP(DLLC, LOGL_NOTICE,
|
||||
|
@ -525,11 +551,16 @@ static struct gprs_llc_llme *llme_alloc(uint32_t tlli)
|
|||
|
||||
llist_add(&llme->list, &gprs_llc_llmes);
|
||||
|
||||
llme->comp.proto = gprs_sndcp_comp_alloc(llme);
|
||||
llme->comp.data = gprs_sndcp_comp_alloc(llme);
|
||||
|
||||
return llme;
|
||||
}
|
||||
|
||||
static void llme_free(struct gprs_llc_llme *llme)
|
||||
{
|
||||
gprs_sndcp_comp_free(llme->comp.proto);
|
||||
gprs_sndcp_comp_free(llme->comp.data);
|
||||
talloc_free(llme->xid);
|
||||
llist_del(&llme->list);
|
||||
talloc_free(llme);
|
||||
|
|
|
@ -35,6 +35,131 @@
|
|||
#include <openbsc/gprs_llc.h>
|
||||
#include <openbsc/sgsn.h>
|
||||
#include <openbsc/gprs_sndcp.h>
|
||||
#include <openbsc/gprs_llc_xid.h>
|
||||
#include <openbsc/gprs_sndcp_xid.h>
|
||||
#include <openbsc/gprs_sndcp_pcomp.h>
|
||||
#include <openbsc/gprs_sndcp_comp.h>
|
||||
|
||||
#define DEBUG_IP_PACKETS 0 /* 0=Disabled, 1=Enabled */
|
||||
|
||||
#if DEBUG_IP_PACKETS == 1
|
||||
/* Calculate TCP/IP 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;
|
||||
}
|
||||
|
||||
/* Show some ip packet details */
|
||||
static void debug_ip_packet(uint8_t *data, int len, int dir, char *info)
|
||||
{
|
||||
uint8_t tcp_flags;
|
||||
char flags_debugmsg[256];
|
||||
int len_short;
|
||||
static unsigned int packet_count = 0;
|
||||
static unsigned int tcp_csum_err_count = 0;
|
||||
static unsigned int ip_csum_err_count = 0;
|
||||
|
||||
packet_count++;
|
||||
|
||||
if (len > 80)
|
||||
len_short = 80;
|
||||
else
|
||||
len_short = len;
|
||||
|
||||
if (dir)
|
||||
DEBUGP(DSNDCP, "%s: MS => SGSN: %s\n", info,
|
||||
osmo_hexdump_nospc(data, len_short));
|
||||
else
|
||||
DEBUGP(DSNDCP, "%s: MS <= SGSN: %s\n", info,
|
||||
osmo_hexdump_nospc(data, len_short));
|
||||
|
||||
DEBUGP(DSNDCP, "%s: Length.: %d\n", info, len);
|
||||
DEBUGP(DSNDCP, "%s: NO.: %d\n", info, packet_count);
|
||||
|
||||
if (len < 20) {
|
||||
DEBUGP(DSNDCP, "%s: Error: Short IP packet!\n", info);
|
||||
return;
|
||||
}
|
||||
|
||||
if (calc_ip_csum(data, 20) != 0) {
|
||||
DEBUGP(DSNDCP, "%s: Bad IP-Header checksum!\n", info);
|
||||
ip_csum_err_count++;
|
||||
} else
|
||||
DEBUGP(DSNDCP, "%s: IP-Header checksum ok.\n", info);
|
||||
|
||||
if (data[9] == 0x06) {
|
||||
if (len < 40) {
|
||||
DEBUGP(DSNDCP, "%s: Error: Short TCP packet!\n", info);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUGP(DSNDCP, "%s: Protocol type: TCP\n", info);
|
||||
tcp_flags = data[33];
|
||||
|
||||
if (calc_tcpip_csum(NULL, data, len) != 0) {
|
||||
DEBUGP(DSNDCP, "%s: Bad TCP checksum!\n", info);
|
||||
tcp_csum_err_count++;
|
||||
} else
|
||||
DEBUGP(DSNDCP, "%s: TCP checksum ok.\n", info);
|
||||
|
||||
memset(flags_debugmsg, 0, sizeof(flags_debugmsg));
|
||||
if (tcp_flags & 1)
|
||||
strcat(flags_debugmsg, "FIN ");
|
||||
if (tcp_flags & 2)
|
||||
strcat(flags_debugmsg, "SYN ");
|
||||
if (tcp_flags & 4)
|
||||
strcat(flags_debugmsg, "RST ");
|
||||
if (tcp_flags & 8)
|
||||
strcat(flags_debugmsg, "PSH ");
|
||||
if (tcp_flags & 16)
|
||||
strcat(flags_debugmsg, "ACK ");
|
||||
if (tcp_flags & 32)
|
||||
strcat(flags_debugmsg, "URG ");
|
||||
DEBUGP(DSNDCP, "%s: FLAGS: %s\n", info, flags_debugmsg);
|
||||
} else if (data[9] == 0x11) {
|
||||
DEBUGP(DSNDCP, "%s: Protocol type: UDP\n", info);
|
||||
} else {
|
||||
DEBUGP(DSNDCP, "%s: Protocol type: (%02x)\n", info, data[9]);
|
||||
}
|
||||
|
||||
DEBUGP(DSNDCP, "%s: IP-Header checksum errors: %d\n", info,
|
||||
ip_csum_err_count);
|
||||
DEBUGP(DSNDCP, "%s: TCP-Checksum errors: %d\n", info,
|
||||
tcp_csum_err_count);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Chapter 7.2: SN-PDU Formats */
|
||||
struct sndcp_common_hdr {
|
||||
|
@ -77,6 +202,14 @@ struct defrag_queue_entry {
|
|||
|
||||
LLIST_HEAD(gprs_sndcp_entities);
|
||||
|
||||
/* Check if any compression parameters are set in the sgsn configuration */
|
||||
static inline int any_pcomp_or_dcomp_active(struct sgsn_instance *sgsn) {
|
||||
if (sgsn->cfg.pcomp_rfc1144.active || sgsn->cfg.pcomp_rfc1144.passive)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Enqueue a fragment into the defragment queue */
|
||||
static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr,
|
||||
uint8_t *data, uint32_t data_len)
|
||||
|
@ -143,6 +276,9 @@ static int defrag_segments(struct gprs_sndcp_entity *sne)
|
|||
struct msgb *msg;
|
||||
unsigned int seg_nr;
|
||||
uint8_t *npdu;
|
||||
int npdu_len;
|
||||
int rc;
|
||||
uint8_t *expnd = NULL;
|
||||
|
||||
LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u "
|
||||
"num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi,
|
||||
|
@ -173,16 +309,58 @@ static int defrag_segments(struct gprs_sndcp_entity *sne)
|
|||
talloc_free(dqe);
|
||||
}
|
||||
|
||||
npdu_len = sne->defrag.tot_len;
|
||||
|
||||
/* FIXME: cancel timer */
|
||||
|
||||
/* actually send the N-PDU to the SGSN core code, which then
|
||||
* hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */
|
||||
return sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
|
||||
sne->nsapi, msg, sne->defrag.tot_len, npdu);
|
||||
|
||||
/* Decompress packet */
|
||||
#if DEBUG_IP_PACKETS == 1
|
||||
DEBUGP(DSNDCP, " \n");
|
||||
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
|
||||
DEBUGP(DSNDCP, "===================================================\n");
|
||||
#endif
|
||||
if (any_pcomp_or_dcomp_active(sgsn)) {
|
||||
|
||||
expnd = talloc_zero_size(msg, npdu_len + MAX_HDRDECOMPR_INCR);
|
||||
memcpy(expnd, npdu, npdu_len);
|
||||
|
||||
/* Apply header decompression */
|
||||
rc = gprs_sndcp_pcomp_expand(expnd, npdu_len, sne->defrag.pcomp,
|
||||
sne->defrag.proto);
|
||||
if (rc < 0) {
|
||||
LOGP(DSNDCP, LOGL_ERROR,
|
||||
"TCP/IP Header decompression failed!\n");
|
||||
talloc_free(expnd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Modify npu length, expnd is handed directly handed
|
||||
* over to gsn_rx_sndcp_ud_ind(), see below */
|
||||
npdu_len = rc;
|
||||
} else
|
||||
expnd = npdu;
|
||||
#if DEBUG_IP_PACKETS == 1
|
||||
debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()");
|
||||
DEBUGP(DSNDCP, "===================================================\n");
|
||||
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
|
||||
DEBUGP(DSNDCP, " \n");
|
||||
#endif
|
||||
|
||||
/* Hand off packet to gtp */
|
||||
rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
|
||||
sne->nsapi, msg, npdu_len, expnd);
|
||||
|
||||
if (any_pcomp_or_dcomp_active(sgsn))
|
||||
talloc_free(expnd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, uint8_t *hdr,
|
||||
unsigned int len)
|
||||
static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg,
|
||||
uint8_t *hdr, unsigned int len)
|
||||
{
|
||||
struct sndcp_common_hdr *sch;
|
||||
struct sndcp_udata_hdr *suh;
|
||||
|
@ -343,7 +521,8 @@ struct sndcp_frag_state {
|
|||
};
|
||||
|
||||
/* returns '1' if there are more fragments to send, '0' if none */
|
||||
static int sndcp_send_ud_frag(struct sndcp_frag_state *fs)
|
||||
static int sndcp_send_ud_frag(struct sndcp_frag_state *fs,
|
||||
uint8_t pcomp, uint8_t dcomp)
|
||||
{
|
||||
struct gprs_sndcp_entity *sne = fs->sne;
|
||||
struct gprs_llc_lle *lle = sne->lle;
|
||||
|
@ -380,8 +559,8 @@ static int sndcp_send_ud_frag(struct sndcp_frag_state *fs)
|
|||
if (sch->first) {
|
||||
scomph = (struct sndcp_comp_hdr *)
|
||||
msgb_put(fmsg, sizeof(*scomph));
|
||||
scomph->pcomp = 0;
|
||||
scomph->dcomp = 0;
|
||||
scomph->pcomp = pcomp;
|
||||
scomph->dcomp = dcomp;
|
||||
}
|
||||
|
||||
/* append the user-data header */
|
||||
|
@ -446,9 +625,41 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi
|
|||
struct sndcp_comp_hdr *scomph;
|
||||
struct sndcp_udata_hdr *suh;
|
||||
struct sndcp_frag_state fs;
|
||||
uint8_t pcomp = 0;
|
||||
uint8_t dcomp = 0;
|
||||
int rc;
|
||||
|
||||
/* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
|
||||
|
||||
/* Compress packet */
|
||||
#if DEBUG_IP_PACKETS == 1
|
||||
DEBUGP(DSNDCP, " \n");
|
||||
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
|
||||
DEBUGP(DSNDCP, "===================================================\n");
|
||||
debug_ip_packet(msg->data, msg->len, 0, "sndcp_initdata_req()");
|
||||
#endif
|
||||
if (any_pcomp_or_dcomp_active(sgsn)) {
|
||||
|
||||
/* Apply header compression */
|
||||
rc = gprs_sndcp_pcomp_compress(msg->data, msg->len, &pcomp,
|
||||
lle->llme->comp.proto, nsapi);
|
||||
if (rc < 0) {
|
||||
LOGP(DSNDCP, LOGL_ERROR,
|
||||
"TCP/IP Header compression failed!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Fixup pointer locations and sizes in message buffer to match
|
||||
* the new, compressed buffer size */
|
||||
msgb_get(msg, msg->len);
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
#if DEBUG_IP_PACKETS == 1
|
||||
DEBUGP(DSNDCP, "===================================================\n");
|
||||
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
|
||||
DEBUGP(DSNDCP, " \n");
|
||||
#endif
|
||||
|
||||
sne = gprs_sndcp_entity_by_lle(lle, nsapi);
|
||||
if (!sne) {
|
||||
LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n");
|
||||
|
@ -469,7 +680,7 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi
|
|||
/* call function to generate and send fragments until all
|
||||
* of the N-PDU has been sent */
|
||||
while (1) {
|
||||
int rc = sndcp_send_ud_frag(&fs);
|
||||
int rc = sndcp_send_ud_frag(&fs,pcomp,dcomp);
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
if (rc < 0)
|
||||
|
@ -489,8 +700,8 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi
|
|||
sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff;
|
||||
|
||||
scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph));
|
||||
scomph->pcomp = 0;
|
||||
scomph->dcomp = 0;
|
||||
scomph->pcomp = pcomp;
|
||||
scomph->dcomp = dcomp;
|
||||
|
||||
/* prepend common SNDCP header */
|
||||
sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch));
|
||||
|
@ -512,6 +723,8 @@ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
|
|||
uint8_t *npdu;
|
||||
uint16_t npdu_num __attribute__((unused));
|
||||
int npdu_len;
|
||||
int rc;
|
||||
uint8_t *expnd = NULL;
|
||||
|
||||
sch = (struct sndcp_common_hdr *) hdr;
|
||||
if (sch->first) {
|
||||
|
@ -540,26 +753,70 @@ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
|
|||
/* FIXME: move this RA_ID up to the LLME or even higher */
|
||||
bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg));
|
||||
|
||||
if(scomph) {
|
||||
sne->defrag.pcomp = scomph->pcomp;
|
||||
sne->defrag.dcomp = scomph->dcomp;
|
||||
sne->defrag.proto = lle->llme->comp.proto;
|
||||
sne->defrag.data = lle->llme->comp.data;
|
||||
}
|
||||
|
||||
/* any non-first segment is by definition something to defragment
|
||||
* as is any segment that tells us there are more segments */
|
||||
if (!sch->first || sch->more)
|
||||
return defrag_input(sne, msg, hdr, len);
|
||||
|
||||
if (scomph && (scomph->pcomp || scomph->dcomp)) {
|
||||
LOGP(DSNDCP, LOGL_ERROR, "We don't support compression yet\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
npdu_num = (suh->npdu_high << 8) | suh->npdu_low;
|
||||
npdu = (uint8_t *)suh + sizeof(*suh);
|
||||
npdu_len = (msg->data + msg->len) - npdu;
|
||||
npdu_len = (msg->data + msg->len) - npdu - 3; /* -3 'removes' the FCS */
|
||||
|
||||
if (npdu_len <= 0) {
|
||||
LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len);
|
||||
return -EIO;
|
||||
}
|
||||
/* actually send the N-PDU to the SGSN core code, which then
|
||||
* hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */
|
||||
return sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu);
|
||||
|
||||
/* Decompress packet */
|
||||
#if DEBUG_IP_PACKETS == 1
|
||||
DEBUGP(DSNDCP, " \n");
|
||||
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
|
||||
DEBUGP(DSNDCP, "===================================================\n");
|
||||
#endif
|
||||
if (any_pcomp_or_dcomp_active(sgsn)) {
|
||||
|
||||
expnd = talloc_zero_size(msg, npdu_len + MAX_HDRDECOMPR_INCR);
|
||||
memcpy(expnd, npdu, npdu_len);
|
||||
|
||||
/* Apply header decompression */
|
||||
rc = gprs_sndcp_pcomp_expand(expnd, npdu_len, sne->defrag.pcomp,
|
||||
sne->defrag.proto);
|
||||
if (rc < 0) {
|
||||
LOGP(DSNDCP, LOGL_ERROR,
|
||||
"TCP/IP Header decompression failed!\n");
|
||||
talloc_free(expnd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Modify npu length, expnd is handed directly handed
|
||||
* over to gsn_rx_sndcp_ud_ind(), see below */
|
||||
npdu_len = rc;
|
||||
} else
|
||||
expnd = npdu;
|
||||
#if DEBUG_IP_PACKETS == 1
|
||||
debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()");
|
||||
DEBUGP(DSNDCP, "===================================================\n");
|
||||
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
|
||||
DEBUGP(DSNDCP, " \n");
|
||||
#endif
|
||||
|
||||
/* Hand off packet to gtp */
|
||||
rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli,
|
||||
sne->nsapi, msg, npdu_len, expnd);
|
||||
|
||||
if (any_pcomp_or_dcomp_active(sgsn))
|
||||
talloc_free(expnd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -619,3 +876,322 @@ static int sndcp_rx_llc_prim()
|
|||
case LL_STATUS_IND:
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate SNDCP-XID message */
|
||||
static int gprs_llc_gen_sndcp_xid(uint8_t *bytes, int bytes_len, uint8_t nsapi)
|
||||
{
|
||||
int entity = 0;
|
||||
LLIST_HEAD(comp_fields);
|
||||
struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params;
|
||||
struct gprs_sndcp_comp_field rfc1144_comp_field;
|
||||
|
||||
memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
|
||||
|
||||
/* Setup rfc1144 */
|
||||
if (sgsn->cfg.pcomp_rfc1144.active) {
|
||||
rfc1144_params.nsapi[0] = nsapi;
|
||||
rfc1144_params.nsapi_len = 1;
|
||||
rfc1144_params.s01 = sgsn->cfg.pcomp_rfc1144.s01;
|
||||
rfc1144_comp_field.p = 1;
|
||||
rfc1144_comp_field.entity = entity;
|
||||
rfc1144_comp_field.algo = 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;
|
||||
entity++;
|
||||
llist_add(&rfc1144_comp_field.list, &comp_fields);
|
||||
}
|
||||
|
||||
/* Compile bytestream */
|
||||
return gprs_sndcp_compile_xid(bytes, bytes_len, &comp_fields);
|
||||
}
|
||||
|
||||
/* Set of SNDCP-XID bnegotiation (See also: TS 144 065,
|
||||
* Section 6.8 XID parameter negotiation) */
|
||||
int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi)
|
||||
{
|
||||
/* Note: The specification requires the SNDCP-User to set of an
|
||||
* SNDCP xid request. See also 3GPP TS 44.065, 6.8 XID parameter
|
||||
* negotiation, Figure 11: SNDCP XID negotiation procedure. In
|
||||
* our case the SNDCP-User is sgsn_libgtp.c, which calls
|
||||
* sndcp_sn_xid_req directly. */
|
||||
|
||||
uint8_t l3params[1024];
|
||||
int xid_len;
|
||||
struct gprs_llc_xid_field xid_field_request;
|
||||
|
||||
/* Wipe off all compression entities and their states to
|
||||
* get rid of possible leftovers from a previous session */
|
||||
gprs_sndcp_comp_free(lle->llme->comp.proto);
|
||||
gprs_sndcp_comp_free(lle->llme->comp.data);
|
||||
lle->llme->comp.proto = gprs_sndcp_comp_alloc(lle->llme);
|
||||
lle->llme->comp.data = gprs_sndcp_comp_alloc(lle->llme);
|
||||
talloc_free(lle->llme->xid);
|
||||
lle->llme->xid = NULL;
|
||||
|
||||
/* Generate compression parameter bytestream */
|
||||
xid_len = gprs_llc_gen_sndcp_xid(l3params, sizeof(l3params), nsapi);
|
||||
|
||||
/* Send XID with the SNDCP-XID bytetsream included */
|
||||
if (xid_len > 0) {
|
||||
xid_field_request.type = GPRS_LLC_XID_T_L3_PAR;
|
||||
xid_field_request.data = l3params;
|
||||
xid_field_request.data_len = xid_len;
|
||||
return gprs_ll_xid_req(lle, &xid_field_request);
|
||||
}
|
||||
|
||||
/* When bytestream can not be generated, proceed without SNDCP-XID */
|
||||
return gprs_ll_xid_req(lle, NULL);
|
||||
|
||||
}
|
||||
|
||||
/* Handle header compression entites */
|
||||
static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field,
|
||||
struct gprs_llc_lle *lle)
|
||||
{
|
||||
/* Note: This functions also transforms the comp_field into its
|
||||
* echo form (strips comp values, resets propose bit etc...)
|
||||
* the processed comp_fields can then be sent back as XID-
|
||||
* Response without further modification. */
|
||||
|
||||
/* Delete propose bit */
|
||||
comp_field->p = 0;
|
||||
|
||||
/* Process proposed parameters */
|
||||
switch (comp_field->algo) {
|
||||
case RFC_1144:
|
||||
if (sgsn->cfg.pcomp_rfc1144.passive
|
||||
&& comp_field->rfc1144_params->nsapi_len > 0) {
|
||||
DEBUGP(DSNDCP,
|
||||
"Accepting RFC1144 header compression...\n");
|
||||
gprs_sndcp_comp_add(lle->llme, lle->llme->comp.proto,
|
||||
comp_field);
|
||||
} else {
|
||||
DEBUGP(DSNDCP,
|
||||
"Rejecting RFC1144 header compression...\n");
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.proto,
|
||||
comp_field->entity);
|
||||
comp_field->rfc1144_params->nsapi_len = 0;
|
||||
}
|
||||
break;
|
||||
case RFC_2507:
|
||||
/* RFC 2507 is not yet supported,
|
||||
* so we set applicable nsapis to zero */
|
||||
DEBUGP(DSNDCP, "Rejecting RFC2507 header compression...\n");
|
||||
comp_field->rfc2507_params->nsapi_len = 0;
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.proto,
|
||||
comp_field->entity);
|
||||
break;
|
||||
case ROHC:
|
||||
/* ROHC is not yet supported,
|
||||
* so we set applicable nsapis to zero */
|
||||
DEBUGP(DSNDCP, "Rejecting ROHC header compression...\n");
|
||||
comp_field->rohc_params->nsapi_len = 0;
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.proto,
|
||||
comp_field->entity);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Hanle data compression entites */
|
||||
static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field,
|
||||
struct gprs_llc_lle *lle)
|
||||
{
|
||||
/* See note in handle_pcomp_entities() */
|
||||
|
||||
/* Delete propose bit */
|
||||
comp_field->p = 0;
|
||||
|
||||
/* Process proposed parameters */
|
||||
switch (comp_field->algo) {
|
||||
case V42BIS:
|
||||
/* V42BIS is not yet supported,
|
||||
* so we set applicable nsapis to zero */
|
||||
LOGP(DSNDCP, LOGL_DEBUG,
|
||||
"Rejecting V.42bis data compression...\n");
|
||||
comp_field->v42bis_params->nsapi_len = 0;
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.data,
|
||||
comp_field->entity);
|
||||
break;
|
||||
case V44:
|
||||
/* V44 is not yet supported,
|
||||
* so we set applicable nsapis to zero */
|
||||
DEBUGP(DSNDCP, "Rejecting V.44 data compression...\n");
|
||||
comp_field->v44_params->nsapi_len = 0;
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.data,
|
||||
comp_field->entity);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Process SNDCP-XID indication
|
||||
* (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
|
||||
int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
|
||||
struct gprs_llc_xid_field *xid_field_response,
|
||||
struct gprs_llc_lle *lle)
|
||||
{
|
||||
/* Note: This function computes the SNDCP-XID response that is sent
|
||||
* back to the ms when a ms originated XID is received. The
|
||||
* Input XID fields are directly processed and the result is directly
|
||||
* handed back. */
|
||||
|
||||
int rc;
|
||||
int compclass;
|
||||
|
||||
struct llist_head *comp_fields;
|
||||
struct gprs_sndcp_comp_field *comp_field;
|
||||
|
||||
OSMO_ASSERT(xid_field_indication);
|
||||
OSMO_ASSERT(xid_field_response);
|
||||
OSMO_ASSERT(lle);
|
||||
|
||||
/* Parse SNDCP-CID XID-Field */
|
||||
comp_fields = gprs_sndcp_parse_xid(lle->llme,
|
||||
xid_field_indication->data,
|
||||
xid_field_indication->data_len,
|
||||
NULL);
|
||||
if (!comp_fields)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't bother with empty indications */
|
||||
if (llist_empty(comp_fields)) {
|
||||
xid_field_response->data = NULL;
|
||||
xid_field_response->data_len = 0;
|
||||
DEBUGP(DSNDCP,
|
||||
"SNDCP-XID indication did not contain any parameters!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle compression entites */
|
||||
DEBUGP(DSNDCP, "SNDCP-XID-IND (ms):\n");
|
||||
gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
|
||||
|
||||
llist_for_each_entry(comp_field, comp_fields, list) {
|
||||
compclass = gprs_sndcp_get_compression_class(comp_field);
|
||||
if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
|
||||
rc = handle_pcomp_entities(comp_field, lle);
|
||||
else if (compclass == SNDCP_XID_DATA_COMPRESSION)
|
||||
rc = handle_dcomp_entities(comp_field, lle);
|
||||
else {
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.proto,
|
||||
comp_field->entity);
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.data,
|
||||
comp_field->entity);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
talloc_free(comp_fields);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGP(DSNDCP, "SNDCP-XID-RES (sgsn):\n");
|
||||
gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
|
||||
|
||||
/* Reserve some memory to store the modified SNDCP-XID bytes */
|
||||
xid_field_response->data =
|
||||
talloc_zero_size(lle->llme, xid_field_indication->data_len);
|
||||
|
||||
/* Set Type flag for response */
|
||||
xid_field_response->type = GPRS_LLC_XID_T_L3_PAR;
|
||||
|
||||
/* Compile modified SNDCP-XID bytes */
|
||||
rc = gprs_sndcp_compile_xid(xid_field_response->data,
|
||||
xid_field_indication->data_len,
|
||||
comp_fields);
|
||||
|
||||
if (rc > 0)
|
||||
xid_field_response->data_len = rc;
|
||||
else {
|
||||
talloc_free(xid_field_response->data);
|
||||
xid_field_response->data = NULL;
|
||||
xid_field_response->data_len = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
talloc_free(comp_fields);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Process SNDCP-XID indication
|
||||
* (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
|
||||
int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
|
||||
struct gprs_llc_xid_field *xid_field_request,
|
||||
struct gprs_llc_lle *lle)
|
||||
{
|
||||
/* Note: This function handles an incomming SNDCP-XID confirmiation.
|
||||
* Since the confirmation fields may lack important parameters we
|
||||
* will reconstruct these missing fields using the original request
|
||||
* we have sent. After that we will create (or delete) the
|
||||
* compression entites */
|
||||
|
||||
struct llist_head *comp_fields_req;
|
||||
struct llist_head *comp_fields_conf;
|
||||
struct gprs_sndcp_comp_field *comp_field;
|
||||
int rc;
|
||||
int compclass;
|
||||
|
||||
/* We need both, the confirmation that is sent back by the ms,
|
||||
* and the original request we have sent. If one of this is missing
|
||||
* we can not process the confirmation, the caller must check if
|
||||
* request and confirmation fields are available. */
|
||||
OSMO_ASSERT(xid_field_conf);
|
||||
OSMO_ASSERT(xid_field_request);
|
||||
|
||||
/* Parse SNDCP-CID XID-Field */
|
||||
comp_fields_req = gprs_sndcp_parse_xid(lle->llme,
|
||||
xid_field_request->data,
|
||||
xid_field_request->data_len,
|
||||
NULL);
|
||||
if (!comp_fields_req)
|
||||
return -EINVAL;
|
||||
|
||||
DEBUGP(DSNDCP, "SNDCP-XID-REQ (sgsn):\n");
|
||||
gprs_sndcp_dump_comp_fields(comp_fields_req, LOGL_DEBUG);
|
||||
|
||||
/* Parse SNDCP-CID XID-Field */
|
||||
comp_fields_conf = gprs_sndcp_parse_xid(lle->llme,
|
||||
xid_field_conf->data,
|
||||
xid_field_conf->data_len,
|
||||
comp_fields_req);
|
||||
if (!comp_fields_conf)
|
||||
return -EINVAL;
|
||||
|
||||
DEBUGP(DSNDCP, "SNDCP-XID-CONF (ms):\n");
|
||||
gprs_sndcp_dump_comp_fields(comp_fields_conf, LOGL_DEBUG);
|
||||
|
||||
/* Handle compression entites */
|
||||
llist_for_each_entry(comp_field, comp_fields_conf, list) {
|
||||
compclass = gprs_sndcp_get_compression_class(comp_field);
|
||||
if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
|
||||
rc = handle_pcomp_entities(comp_field, lle);
|
||||
else if (compclass == SNDCP_XID_DATA_COMPRESSION)
|
||||
rc = handle_dcomp_entities(comp_field, lle);
|
||||
else {
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.proto,
|
||||
comp_field->entity);
|
||||
gprs_sndcp_comp_delete(lle->llme->comp.data,
|
||||
comp_field->entity);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
talloc_free(comp_fields_req);
|
||||
talloc_free(comp_fields_conf);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(comp_fields_req);
|
||||
talloc_free(comp_fields_conf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,320 @@
|
|||
/* 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 <openbsc/debug.h>
|
||||
#include <openbsc/gprs_sndcp_xid.h>
|
||||
#include <openbsc/gprs_sndcp_comp.h>
|
||||
#include <openbsc/gprs_sndcp_pcomp.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));
|
||||
} 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));
|
||||
} 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));
|
||||
} 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));
|
||||
} else if (comp_field->v44_params) {
|
||||
comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
|
||||
memcpy(comp_entity->nsapi,
|
||||
comp_field->v42bis_params->nsapi,
|
||||
sizeof(comp_entity->nsapi));
|
||||
} 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);
|
||||
}
|
||||
comp_entity->algo = comp_field->algo;
|
||||
|
||||
/* 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);
|
||||
|
||||
OSMO_ASSERT(comp_entity->compclass != -1);
|
||||
|
||||
/* Create an algorithm specific compression context */
|
||||
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
|
||||
if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
|
||||
talloc_free(comp_entity);
|
||||
comp_entity = NULL;
|
||||
}
|
||||
} else {
|
||||
LOGP(DSNDCP, LOGL_ERROR,
|
||||
"We don't support data compression yet!\n");
|
||||
talloc_free(comp_entity);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Display info message */
|
||||
if (comp_entity == NULL) {
|
||||
LOGP(DSNDCP, LOGL_ERROR,
|
||||
"Header compression entity (%d) creation failed!\n",
|
||||
comp_entity->entity);
|
||||
return NULL;
|
||||
}
|
||||
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
|
||||
LOGP(DSNDCP, LOGL_INFO,
|
||||
"New header compression entity (%d) created.\n",
|
||||
comp_entity->entity);
|
||||
} else {
|
||||
LOGP(DSNDCP, 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;
|
||||
|
||||
/* We expect the caller to take care of allocating a
|
||||
* compression entity list properly. Attempting to
|
||||
* free a non existing list clearly points out
|
||||
* a malfunction. */
|
||||
OSMO_ASSERT(comp_entities);
|
||||
|
||||
llist_for_each_entry(comp_entity, comp_entities, list) {
|
||||
/* Free compression entity */
|
||||
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
|
||||
LOGP(DSNDCP, LOGL_INFO,
|
||||
"Deleting header compression entity %d ...\n",
|
||||
comp_entity->entity);
|
||||
gprs_sndcp_pcomp_term(comp_entity);
|
||||
} else {
|
||||
LOGP(DSNDCP, LOGL_INFO,
|
||||
"Deleting data compression entity %d ...\n",
|
||||
comp_entity->entity);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
LOGP(DSNDCP, LOGL_INFO,
|
||||
"Deleting header compression entity %d ...\n",
|
||||
comp_entity_to_delete->entity);
|
||||
gprs_sndcp_pcomp_term(comp_entity_to_delete);
|
||||
} else {
|
||||
LOGP(DSNDCP, 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;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DSNDCP, 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;
|
||||
}
|
||||
|
||||
LOGP(DSNDCP, 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) {
|
||||
LOGP(DSNDCP, 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];
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/* 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 <openbsc/gprs_llc.h>
|
||||
#include <openbsc/sgsn.h>
|
||||
#include <openbsc/gprs_sndcp_xid.h>
|
||||
#include <openbsc/slhc.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gprs_sndcp_comp.h>
|
||||
#include <openbsc/gprs_sndcp_pcomp.h>
|
||||
|
||||
/* Initalize 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 == RFC_1144) {
|
||||
comp_entity->state =
|
||||
slhc_init(ctx, comp_field->rfc1144_params->s01 + 1,
|
||||
comp_field->rfc1144_params->s01 + 1);
|
||||
LOGP(DSNDCP, LOGL_INFO,
|
||||
"RFC1144 header compression initalized.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Just in case someone tries to initalize 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 == RFC_1144) {
|
||||
if (comp_entity->state) {
|
||||
slhc_free((struct slcompress *)comp_entity->state);
|
||||
comp_entity->state = NULL;
|
||||
}
|
||||
LOGP(DSNDCP, 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;
|
||||
case 1:
|
||||
type = SL_TYPE_UNCOMPRESSED_TCP;
|
||||
break;
|
||||
case 2:
|
||||
type = SL_TYPE_COMPRESSED_TCP;
|
||||
break;
|
||||
default:
|
||||
LOGP(DSNDCP, 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);
|
||||
|
||||
LOGP(DSNDCP, LOGL_DEBUG,
|
||||
"Header compression entity list: comp_entities=%p\n",
|
||||
comp_entities);
|
||||
|
||||
LOGP(DSNDCP, 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 == 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);
|
||||
|
||||
LOGP(DSNDCP, 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);
|
||||
|
||||
LOGP(DSNDCP, 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 == 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);
|
||||
|
||||
LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
|
||||
|
||||
LOGP(DSNDCP, LOGL_DEBUG,
|
||||
"Header compression done, old length=%d, new length=%d, entity=%p\n",
|
||||
len, rc, comp_entity);
|
||||
return rc;
|
||||
}
|
|
@ -49,6 +49,7 @@
|
|||
#include <openbsc/gprs_sgsn.h>
|
||||
#include <openbsc/gprs_gmm.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/gprs_sndcp.h>
|
||||
|
||||
#ifdef BUILD_IU
|
||||
#include <openbsc/iu.h>
|
||||
|
@ -317,6 +318,8 @@ static const struct cause_map gtp2sm_cause_map[] = {
|
|||
static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
|
||||
{
|
||||
struct sgsn_signal_data sig_data;
|
||||
int rc;
|
||||
struct gprs_llc_lle *lle;
|
||||
|
||||
/* Inform others about it */
|
||||
memset(&sig_data, 0, sizeof(sig_data));
|
||||
|
@ -324,7 +327,17 @@ static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
|
|||
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data);
|
||||
|
||||
/* Send PDP CTX ACT to MS */
|
||||
return gsm48_tx_gsm_act_pdp_acc(pctx);
|
||||
rc = gsm48_tx_gsm_act_pdp_acc(pctx);
|
||||
if(rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Send SNDCP XID to MS */
|
||||
lle = &pctx->mm->gb.llme->lle[pctx->sapi];
|
||||
rc = sndcp_sn_xid_req(lle,pctx->nsapi);
|
||||
if(rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The GGSN has confirmed the creation of a PDP Context */
|
||||
|
|
|
@ -269,6 +269,14 @@ static int config_write_sgsn(struct vty *vty)
|
|||
vty_out(vty, " timer t3395 %d%s", g_cfg->timers.T3395, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3397 %d%s", g_cfg->timers.T3397, VTY_NEWLINE);
|
||||
|
||||
if (g_cfg->pcomp_rfc1144.active) {
|
||||
vty_out(vty, " compression rfc1144 active slots %d%s",
|
||||
g_cfg->pcomp_rfc1144.s01 + 1, VTY_NEWLINE);
|
||||
} else if (g_cfg->pcomp_rfc1144.passive) {
|
||||
vty_out(vty, " compression rfc1144 passive%s", VTY_NEWLINE);
|
||||
} else
|
||||
vty_out(vty, " no compression rfc1144%s", VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1074,6 +1082,41 @@ DEFUN(cfg_cdr_interval, cfg_cdr_interval_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define COMPRESSION_STR "Configure compression\n"
|
||||
DEFUN(cfg_no_comp_rfc1144, cfg_no_comp_rfc1144_cmd,
|
||||
"no compression rfc1144",
|
||||
NO_STR COMPRESSION_STR "disable rfc1144 TCP/IP header compression\n")
|
||||
{
|
||||
g_cfg->pcomp_rfc1144.active = 0;
|
||||
g_cfg->pcomp_rfc1144.passive = 0;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_comp_rfc1144, cfg_comp_rfc1144_cmd,
|
||||
"compression rfc1144 active slots <1-256>",
|
||||
COMPRESSION_STR
|
||||
"RFC1144 Header compresion scheme\n"
|
||||
"Compression is actively proposed\n"
|
||||
"Number of compression state slots\n"
|
||||
"Number of compression state slots\n")
|
||||
{
|
||||
g_cfg->pcomp_rfc1144.active = 1;
|
||||
g_cfg->pcomp_rfc1144.passive = 1;
|
||||
g_cfg->pcomp_rfc1144.s01 = atoi(argv[0]) - 1;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_comp_rfc1144p, cfg_comp_rfc1144p_cmd,
|
||||
"compression rfc1144 passive",
|
||||
COMPRESSION_STR
|
||||
"RFC1144 Header compresion scheme\n"
|
||||
"Compression is available on request\n")
|
||||
{
|
||||
g_cfg->pcomp_rfc1144.active = 0;
|
||||
g_cfg->pcomp_rfc1144.passive = 1;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int sgsn_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_sgsn_cmd);
|
||||
|
@ -1128,6 +1171,10 @@ int sgsn_vty_init(void)
|
|||
install_element(SGSN_NODE, &cfg_sgsn_T3395_cmd);
|
||||
install_element(SGSN_NODE, &cfg_sgsn_T3397_cmd);
|
||||
|
||||
install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd);
|
||||
install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd);
|
||||
install_element(SGSN_NODE, &cfg_comp_rfc1144p_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ sgsn_test_LDADD = \
|
|||
$(top_builddir)/src/gprs/gprs_llc_xid.o \
|
||||
$(top_builddir)/src/gprs/gprs_sndcp_xid.o \
|
||||
$(top_builddir)/src/gprs/slhc.o \
|
||||
$(top_builddir)/src/gprs/gprs_sndcp_comp.o \
|
||||
$(top_builddir)/src/gprs/gprs_sndcp_pcomp.o \
|
||||
$(top_builddir)/src/libcommon/libcommon.a \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
|
|
Loading…
Reference in New Issue