Compare commits

...

4 Commits

Author SHA1 Message Date
Daniel Willmann
54fcd64fc7 WIP
Change-Id: Ia8d595095462c30a56b8a0b30c8d24632776e4cc
2024-03-06 11:53:37 +01:00
Daniel Willmann
6536376527 gtp: WIP SGSN Context Request/Response/Ack support
* SGSN Context Request parsing
* SGSN Context Response generation
* Link to libosmo-gsm

Change-Id: I3a5046b67f73a81eeb6b509777132857a9abcf0c
2024-03-06 11:26:19 +01:00
Daniel Willmann
6d85d2a3dd gtp: Add function gtp_resp2() which takes flow label and tei
Change-Id: Id9ff95e0e2a10a22e65ecf42b2a2b06a0f2d1a45
2024-03-06 11:24:49 +01:00
Daniel Willmann
cd970edee4 gtp: Make peer addr const in gtp_req/gtp_resp
Change-Id: I955b82ba8022754c3d23979d5c55bb61233047b1
2024-03-06 11:24:49 +01:00
6 changed files with 454 additions and 15 deletions

View File

@@ -155,6 +155,7 @@ adl_FUNC_GETOPT_LONG
AM_INIT_AUTOMAKE([foreign]) AM_INIT_AUTOMAKE([foreign])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)

View File

@@ -13,6 +13,7 @@ AM_CFLAGS = \
-Wall \ -Wall \
-DSBINDIR='"$(sbindir)"' \ -DSBINDIR='"$(sbindir)"' \
$(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(NULL) $(NULL)
libgtp_la_SOURCES = \ libgtp_la_SOURCES = \
@@ -31,4 +32,4 @@ libgtp_la_SOURCES = \
$(NULL) $(NULL)
libgtp_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined libgtp_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS) libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)

View File

@@ -244,6 +244,12 @@ int gtp_set_cb_data_ind(struct gsn_t *gsn,
gsn->cb_data_ind = cb_data_ind; gsn->cb_data_ind = cb_data_ind;
return 0; return 0;
} }
int gtp_set_cb_sgsn_context_request_ind(struct gsn_t *gsn,
int (*cb) (struct gsn_t *gsn, struct sockaddr_in *peer, uint16_t seq, const struct osmo_routing_area_id *rai, uint32_t teic, struct osmo_mobile_identity *mi, union gtpie_member **ie))
{
gsn->cb_sgsn_context_request_ind = cb;
return 0;
}
static int queue_timer_retrans(struct gsn_t *gsn) static int queue_timer_retrans(struct gsn_t *gsn)
{ {

View File

@@ -17,6 +17,8 @@
#include <osmocom/core/timer.h> #include <osmocom/core/timer.h>
#include <osmocom/core/tdef.h> #include <osmocom/core/tdef.h>
#include <osmocom/core/rate_ctr.h> #include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm48.h>
#include "pdp.h" #include "pdp.h"
@@ -107,6 +109,7 @@ struct gsn_t {
int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery); int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery); int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery); int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
int (*cb_sgsn_context_request_ind) (struct gsn_t *gsn, struct sockaddr_in *peer, uint16_t seq, const struct osmo_routing_area_id *rai, uint32_t teic, struct osmo_mobile_identity *mi, union gtpie_member **ie); /* Pass RAI and TLLI/TMSI/IMSI directly */
/* Counters */ /* Counters */
struct rate_ctr_group *ctrg; struct rate_ctr_group *ctrg;
@@ -130,6 +133,8 @@ extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp, extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp); void *cbp);
extern int gtp_create_sgsn_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp);
extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn, extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
int (*cb_create_context_ind) (struct int (*cb_create_context_ind) (struct
pdp_t * pdp_t *
@@ -152,6 +157,9 @@ extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn, extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie)); int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie));
extern int gtp_set_cb_sgsn_context_request_ind(struct gsn_t *gsn,
int (*cb) (struct gsn_t *gsn, struct sockaddr_in *peer, uint16_t seq, const struct osmo_routing_area_id *rai, uint32_t teic, struct osmo_mobile_identity *mi, union gtpie_member **ie));
extern int gtp_set_cb_conf(struct gsn_t *gsn, extern int gtp_set_cb_conf(struct gsn_t *gsn,
int (*cb) (int type, int cause, struct pdp_t * pdp, int (*cb) (int type, int cause, struct pdp_t * pdp,
void *cbp)); void *cbp));

445
gtp/gtp.c
View File

@@ -25,6 +25,8 @@
#include <osmocom/core/logging.h> #include <osmocom/core/logging.h>
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/gsm23003.h>
#if defined(__FreeBSD__) #if defined(__FreeBSD__)
#include <sys/endian.h> #include <sys/endian.h>
@@ -338,7 +340,7 @@ static uint32_t get_tei(void *pack)
static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp, static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
union gtp_packet *packet, int len, union gtp_packet *packet, int len,
struct in_addr *inetaddr, void *cbp) const struct in_addr *inetaddr, void *cbp)
{ {
uint8_t ver = GTPHDR_F_GET_VER(packet->flags); uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
struct sockaddr_in addr; struct sockaddr_in addr;
@@ -451,9 +453,7 @@ static int gtp_conf(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer
return 0; return 0;
} }
static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp, static int gtp_resp2(struct gsn_t *gsn, union gtp_packet *packet, int len, struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid, uint16_t flow, uint32_t tei)
union gtp_packet *packet, int len,
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
{ {
uint8_t ver = GTPHDR_F_GET_VER(packet->flags); uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
struct qmsg_t *qmsg; struct qmsg_t *qmsg;
@@ -462,18 +462,11 @@ static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE); packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
packet->gtp0.h.seq = hton16(seq); packet->gtp0.h.seq = hton16(seq);
packet->gtp0.h.tid = htobe64(tid); packet->gtp0.h.tid = htobe64(tid);
if (pdp && ((packet->gtp0.h.type == GTP_GPDU) || packet->gtp0.h.flow = hton16(flow);
(packet->gtp0.h.type == GTP_ERROR)))
packet->gtp0.h.flow = hton16(pdp->flru);
else if (pdp)
packet->gtp0.h.flow = hton16(pdp->flrc);
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */ } else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT); packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
packet->gtp1l.h.seq = hton16(seq); packet->gtp1l.h.seq = hton16(seq);
if (pdp && (fd == gsn->fd1u)) packet->gtp1l.h.tei = hton32(tei);
packet->gtp1l.h.tei = hton32(pdp->teid_gn);
else if (pdp)
packet->gtp1l.h.tei = hton32(pdp->teic_gn);
} else { } else {
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags); LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
return -1; return -1;
@@ -485,7 +478,7 @@ static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
} }
if (sendto(fd, packet, len, 0, if (sendto(fd, packet, len, 0,
(struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) { (const struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) {
rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SENDTO); rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SENDTO);
LOGP(DLGTP, LOGL_ERROR, LOGP(DLGTP, LOGL_ERROR,
"Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd,
@@ -521,6 +514,32 @@ static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
return 0; return 0;
} }
static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
union gtp_packet *packet, int len,
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
{
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
uint16_t flow = 0, tei = 0;
if (ver == 0) { /* Version 0 */
if (pdp && ((packet->gtp0.h.type == GTP_GPDU) ||
(packet->gtp0.h.type == GTP_ERROR)))
flow = pdp->flru;
else if (pdp)
flow = pdp->flrc;
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
if (pdp && (fd == gsn->fd1u))
tei = pdp->teid_gn;
else if (pdp)
tei = pdp->teic_gn;
} else {
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
return -1;
}
return gtp_resp2(gsn, packet, len, peer, fd, seq, tid, flow, tei);
}
static int gtp_notification(struct gsn_t *gsn, uint8_t version, static int gtp_notification(struct gsn_t *gsn, uint8_t version,
union gtp_packet *packet, int len, union gtp_packet *packet, int len,
const struct sockaddr_in *peer, int fd, uint16_t seq) const struct sockaddr_in *peer, int fd, uint16_t seq)
@@ -820,6 +839,395 @@ int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
return gtp_notification(gsn, 1, &packet, length, peer, gsn->fd1c, 0); return gtp_notification(gsn, 1, &packet, length, peer, gsn->fd1c, 0);
} }
#define GSM_MI_TYPE_TLLI 0x05
/* Send an SGSN Context Request for an MI */
int gtp_sgsn_context_req(struct gsn_t *gsn, const struct in_addr *peer,
const struct osmo_mobile_identity *mi, uint16_t tlli, uint32_t teic,
const struct ul16_t *sgsn_addr, const struct ul255_t *rai, void *cbp)
{
union gtp_packet packet;
/* GTP 1 is the highest supported protocol */
unsigned int length = get_default_gtp(1, GTP_SGSN_CONTEXT_REQ, &packet);
gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_RAI, rai->l, rai->v);
gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, teic);
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, sgsn_addr->l, sgsn_addr->v);
switch (mi->type) {
case GSM_MI_TYPE_IMSI:
{
uint64_t imsi = gtp_imsi_str2gtp(mi->imsi);
imsi = ntoh64(imsi);
gtpie_tv8(&packet, &length, GTP_MAX, GTPIE_IMSI, imsi);
break;
}
case GSM_MI_TYPE_TLLI:
gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TLLI, mi->tmsi);
break;
case GSM_MI_TYPE_TMSI:
gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_P_TMSI, mi->tmsi);
break;
default:
return -1;
/* TODO: Error */
break;
}
return gtp_req(gsn, 1, NULL, &packet, length, peer, cbp);
}
/* Handle an SGSN Context Request */
static int gtp_sgsn_context_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
void *pack, unsigned len)
{
/* Check if we have a context with TLLI/P-TMSI/IMSI in the RAI */
struct osmo_mobile_identity mi = {0};
uint64_t imsi;
union gtpie_member *ie[GTPIE_SIZE];
uint32_t teic;
uint8_t seq = get_seq(pack);
struct ul16_t sgsn_addr;
struct ul255_t rai = { .l = 6 };
struct osmo_routing_area_id rai_parsed;
if (version != 1) {
LOGP(DLGTP, LOGL_NOTICE,
"SGSN Context Request expected only on GTPCv1: %u\n", version);
return -EINVAL;
}
int hlen = get_hlen(pack);
/* Decode information elements */
if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_INVALID);
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Invalid message format\n");
return -EINVAL;
}
/* RAI */
if (gtpie_gettv0(ie, GTPIE_RAI, 0, &rai.v, 6)) {
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing RAI\n");
goto missing_ie;
}
if (osmo_routing_area_id_decode(&rai_parsed, rai.v, 6) < 0) {
rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_INVALID);
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Invalid RAI\n");
}
/* TEI-C */
if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &teic)) {
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing TEI-C\n");
goto missing_ie;
}
/* SGSN address for signalling (mandatory) */
if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &sgsn_addr.l,
&sgsn_addr.v, sizeof(sgsn_addr.v))) {
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing GSN Addr\n");
goto missing_ie;
}
/* Get PTMSI, TLLI or IMSI - only one IE allowed */
if (!gtpie_gettv4(ie, GTPIE_TLLI, 0, &mi.tmsi)) {
mi.type = GSM_MI_TYPE_TLLI;
goto done;
}
if (!gtpie_gettv4(ie, GTPIE_P_TMSI, 0, &mi.tmsi)) {
mi.type = GSM_MI_TYPE_TMSI;
goto done;
}
if (!gtpie_gettv8(ie, GTPIE_IMSI, 0, &imsi)) {
mi.type = GSM_MI_TYPE_IMSI;
/* NOTE: gtpie_gettv8 already converts to host byte order, but imsi_gtp2str seems to prefer big endian */
imsi = ntoh64(imsi);
const char *imsi_str = imsi_gtp2str(&imsi);
memcpy(mi.imsi, imsi_str, sizeof(mi.imsi));
} else {
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing Identity information\n");
goto missing_ie;
}
done:
if (gsn->cb_sgsn_context_request_ind)
gsn->cb_sgsn_context_request_ind(gsn, peer, seq, &rai_parsed, teic, &mi, ie);
return 0;
missing_ie:
rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_MISSING);
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing mandatory IE\n");
/* TODO: Send response with cause */
return -EINVAL;
}
static bool is_ext_ua(const struct pdp_t *pdp)
{
return pdp->eua.l > 0 && pdp->eua.v[0] == PDP_EUA_TYPE_v4v6;
}
static int gtp_pdp_ctx(uint8_t *buf, unsigned int size, const struct pdp_t *pdp, uint16_t sapi)
{
uint32_t tmp32;
uint16_t tmp16;
uint8_t *ptr = buf;
#define CHECK_SPACE_ERR(bytes) \
if ((ptr - buf) + (bytes) > size) { \
printf("Failed size check: %lu + %lu > %lu\n", (ptr- buf), bytes, size); \
return -1; \
}
#define MEMCPY_CHK(dst, src, len) \
CHECK_SPACE_ERR((len)) \
memcpy((dst), (uint8_t *)(src), (len)); \
(dst) += (len);
// Flags - FIXME: No ASI
*ptr++ = (is_ext_ua(pdp) << 7) | ((!!pdp->vplmn_allow) << 6) | ((!!pdp->reorder) << 4) | (pdp->nsapi & 0x0f);
// SAPI
*ptr++ = sapi & 0x0f;
// QoS Sub
if (pdp->qos_sub.l < 4) {
/* Work around qos_sub never being set */
*ptr++ = 4;
*ptr++ = 0;
*ptr++ = 0x23;
*ptr++ = 0x02;
*ptr++ = 0x00;
} else {
*ptr++ = pdp->qos_sub.l;
MEMCPY_CHK(ptr, pdp->qos_sub.v, pdp->qos_sub.l);
}
// QoS Req
*ptr++ = pdp->qos_req.l;
MEMCPY_CHK(ptr, pdp->qos_req.v, pdp->qos_req.l);
// QoS Neg
*ptr++ = pdp->qos_neg.l;
MEMCPY_CHK(ptr, pdp->qos_neg.v, pdp->qos_neg.l);
// SND
tmp16 = osmo_htons(pdp->pdcpsndd);
MEMCPY_CHK(ptr, &tmp16, sizeof(tmp16));
// SNU
tmp16 = osmo_htons(pdp->pdcpsndu);
MEMCPY_CHK(ptr, &tmp16, sizeof(tmp16));
// Send N-PDU
*ptr++ = pdp->gtpsntx;
// Recv N-PDU
*ptr++ = pdp->gtpsnrx;
// Uplink TEIC
tmp32 = osmo_htonl(pdp->teic_gn);
MEMCPY_CHK(ptr, &tmp32, sizeof(tmp32));
// Uplink TEIDI
tmp32 = osmo_htonl(pdp->teid_gn);
MEMCPY_CHK(ptr, &tmp32, sizeof(tmp32));
// PDP Ctx Id
*ptr++ = pdp->pdp_id;
// PDP Type Org
*ptr++ = PDP_EUA_ORG_IETF;
// PDP Type No.
// PDP Address
switch (pdp->eua.v[1]) {
case PDP_EUA_TYPE_v4:
case PDP_EUA_TYPE_v4v6:
/* v4v6 expects an IPv4 addr first followed by an IPv6 addr*/
*ptr++ = PDP_EUA_TYPE_v4;
if (pdp->eua.l < 6)
return -1;
*ptr++ = 4;
MEMCPY_CHK(ptr, &pdp->eua.v[2], 4);
break;
case PDP_EUA_TYPE_v6:
*ptr++ = PDP_EUA_TYPE_v6;
if (pdp->eua.l < 18)
return -1;
*ptr++ = 16;
MEMCPY_CHK(ptr, &pdp->eua.v[2], 16);
break;
default:
return -EINVAL;
//Panic
}
// GGSN Address Ctrl
*ptr++ = pdp->gsnrc.l;
MEMCPY_CHK(ptr, pdp->gsnrc.v, pdp->gsnrc.l);
// GGSN Address User
*ptr++ = pdp->gsnru.l;
MEMCPY_CHK(ptr, pdp->gsnru.v, pdp->gsnru.l);
// APN
*ptr++ = pdp->apn_use.l;
MEMCPY_CHK(ptr, pdp->apn_use.v, pdp->apn_use.l);
// TransId
*ptr++ = (pdp->ti >> 8) & 0x0f;
*ptr++ = pdp->ti & 0xff;
if (is_ext_ua(pdp)) {
*ptr++ = PDP_EUA_TYPE_v6;
if (pdp->eua.l < 22)
return -1;
*ptr++ = 16;
MEMCPY_CHK(ptr, &pdp->eua.v[6], 16);
}
return ptr - buf;
#undef CHECK_SPACE_ERR
#undef MEMCPY_CHK
}
int gtp_sgsn_context_conf(struct gsn_t *gsn, struct sockaddr_in *peer, uint16_t seq,
uint32_t teic, uint8_t cause, uint64_t imsi, const struct in_addr *sgsn_addr, struct pdp_t *pdpctx, uint16_t sapi, uint8_t *mmctx, int mm_len, void *cbp)
{
union gtp_packet packet;
struct ul255_t pdp;
/* GTP 1 is the highest supported protocol */
unsigned int length = get_default_gtp(1, GTP_SGSN_CONTEXT_RSP, &packet);
// Cause - TV1
gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause);
if (cause != GTPCAUSE_ACC_REQ)
return gtp_resp2(gsn, &packet, length, peer, gsn->fd1c, seq, 0, 0, teic);
// IMSI - TV8
gtpie_tv8(&packet, &length, GTP_MAX, GTPIE_IMSI, imsi);
// TEIC - TV4
gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, pdpctx->teic_own);
// MM Ctx - TLV
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MM_CONTEXT, mm_len, mmctx);
// PDP Ctx - TLV
if (pdpctx) {
int rc = gtp_pdp_ctx(pdp.v, 255, pdpctx, sapi);
if (rc > 0)
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PDP_CONTEXT, rc, pdp.v);
}
// SGSN Addr Ctrl - TLV
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, 4, &sgsn_addr->s_addr);
// Alt GGSN Addr Ctrl (per PDP Ctx)
// Alt GGSN Addr User (per PDP Ctx)
/* The encoding for this is a bit insane */
return gtp_resp2(gsn, &packet, length, peer, gsn->fd1c, seq, 0, 0, teic);
}
/* Handle an SGSN Context Response */
static int gtp_sgsn_context_conf_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
void *pack, unsigned len)
{
/** If cause is "Request accepted": Send ACK,
else: Log and handle error */
/* Check if we have a context with TLLI/P-TMSI/IMSI in the RAI */
void *cbp = NULL;
uint8_t type = 0;
uint8_t cause;
union gtpie_member *ie[GTPIE_SIZE];
struct pdp_t *pdp = NULL;
uint32_t teic;
struct ul16_t sgsn_addr;
if (version != 1) {
LOGP(DLGTP, LOGL_NOTICE,
"SGSN Context Request expected only on GTPCv1: %u\n", version);
return -EINVAL;
}
int hlen = get_hlen(pack);
/* Remove packet from queue */
if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp))
return EOF;
/* Extract information elements into a pointer array */
if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_INVALID);
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Invalid message format\n");
if (gsn->cb_conf)
gsn->cb_conf(type, EOF, NULL, cbp);
return EOF;
}
/* Extract cause value (mandatory) */
if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) {
rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_MISSING);
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing mandatory information field\n");
if (gsn->cb_conf)
gsn->cb_conf(type, EOF, NULL, cbp);
return EOF;
}
/* Check all conditional information elements */
if (!gtp_cause_successful(cause)) {
GTP_LOGPKG(LOGL_NOTICE, peer, pack, len,
"SGSN Context response with cause (%u)\n", cause);
if (gsn->cb_conf)
gsn->cb_conf(type, cause, NULL, cbp);
}
/* Gather UE context and PDP data, create pdp context */
//gtp_pdp_newpdp(gsn, &pdp, imsi, nsapi, NULL)
if (gsn->cb_conf)
gsn->cb_conf(type, cause, pdp, cbp);
return 0;
}
/* Handle an SGSN Context Acknowledge */
static int gtp_sgsn_context_ack(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
void *pack, unsigned len)
{
/* If cause is != Request accepted, log error */
return -ENOTSUP;
}
/* Handle an SGSN Context request */
int gtp_context_request(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
void *pack, unsigned len)
{
union gtpie_member *ie[GTPIE_SIZE];
if (version != 1) {
LOGP(DLGTP, LOGL_NOTICE,
"SGSN Context Request expected only on GTPCv1: %u\n", version);
return -EINVAL;
}
int hlen = get_hlen(pack);
/* Decode information elements */
if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_INVALID);
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Invalid message format (AN Information Relay)\n");
return -EINVAL;
}
if (gsn->cb_ran_info_relay_ind)
gsn->cb_ran_info_relay_ind(peer, ie);
return 0;
}
/* *********************************************************** /* ***********************************************************
* Session management messages * Session management messages
* Messages: create, update and delete PDP context * Messages: create, update and delete PDP context
@@ -2826,6 +3234,15 @@ int gtp_decaps1c(struct gsn_t *gsn)
case GTP_RAN_INFO_RELAY: case GTP_RAN_INFO_RELAY:
gtp_ran_info_relay_ind(gsn, version, &peer, buffer, status); gtp_ran_info_relay_ind(gsn, version, &peer, buffer, status);
break; break;
case GTP_SGSN_CONTEXT_REQ:
gtp_sgsn_context_ind(gsn, version, &peer, buffer, status);
break;
case GTP_SGSN_CONTEXT_RSP:
gtp_sgsn_context_conf_ind(gsn, version, &peer, buffer, status);
break;
case GTP_SGSN_CONTEXT_ACK:
gtp_sgsn_context_ack(gsn, version, &peer, buffer, status);
break;
default: default:
rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_UNKNOWN); rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_UNKNOWN);
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,

View File

@@ -262,6 +262,12 @@ extern int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in *p
const uint8_t *rim_route_addr, size_t rim_route_addr_len, const uint8_t *rim_route_addr, size_t rim_route_addr_len,
uint8_t rim_route_addr_discr); uint8_t rim_route_addr_discr);
extern int gtp_sgsn_context_req(struct gsn_t *gsn, const struct in_addr *peer,
const struct osmo_mobile_identity *mi, uint16_t tlli, uint32_t teic,
const struct ul16_t *sgsn_addr, const struct ul255_t *rai, void *cbp);
extern int gtp_sgsn_context_conf(struct gsn_t *gsn, struct sockaddr_in *peer, uint16_t seq,
uint32_t teic, uint8_t cause, uint64_t imsi, const struct in_addr *sgsn_addr, struct pdp_t *pdpctx, uint16_t sapi, uint8_t *mmctx, int mm_len, void *cbp);
extern int gtp_decaps0(struct gsn_t *gsn); extern int gtp_decaps0(struct gsn_t *gsn);
extern int gtp_decaps1c(struct gsn_t *gsn); extern int gtp_decaps1c(struct gsn_t *gsn);
extern int gtp_decaps1u(struct gsn_t *gsn); extern int gtp_decaps1u(struct gsn_t *gsn);