This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
pespin gerrit-no-reply at lists.osmocom.orgpespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-hnodeb/+/26503 )
Change subject: First implementation of the LLSK gtp SAPI
......................................................................
First implementation of the LLSK gtp SAPI
Change-Id: I5a6f5dfc4e508c92adb35210b4dc576d64353366
---
M configure.ac
M include/osmocom/hnodeb/Makefile.am
A include/osmocom/hnodeb/gtp.h
M include/osmocom/hnodeb/hnb_prim.h
M include/osmocom/hnodeb/hnodeb.h
M include/osmocom/hnodeb/llsk.h
M include/osmocom/hnodeb/vty.h
M src/osmo-hnodeb/Makefile.am
M src/osmo-hnodeb/debug.c
A src/osmo-hnodeb/gtp.c
M src/osmo-hnodeb/hnb.c
M src/osmo-hnodeb/llsk.c
A src/osmo-hnodeb/llsk_gtp.c
M src/osmo-hnodeb/main.c
M src/osmo-hnodeb/vty.c
15 files changed, 722 insertions(+), 4 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-hnodeb refs/changes/03/26503/1
diff --git a/configure.ac b/configure.ac
index e96a6a1..8a34b82 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,7 @@
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 0.8.0)
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 0.8.0)
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 0.8.0)
+PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.8.0)
dnl checks for header files
diff --git a/include/osmocom/hnodeb/Makefile.am b/include/osmocom/hnodeb/Makefile.am
index dcb3868..381cff9 100644
--- a/include/osmocom/hnodeb/Makefile.am
+++ b/include/osmocom/hnodeb/Makefile.am
@@ -1,4 +1,5 @@
noinst_HEADERS = \
+ gtp.h \
hnb_shutdown_fsm.h \
hnb_prim.h \
hnbap.h \
diff --git a/include/osmocom/hnodeb/gtp.h b/include/osmocom/hnodeb/gtp.h
new file mode 100644
index 0000000..8af69c8
--- /dev/null
+++ b/include/osmocom/hnodeb/gtp.h
@@ -0,0 +1,32 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at 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/lienses/>.
+ *
+ */
+#pragma once
+
+#include <osmocom/core/socket.h>
+
+struct hnb;
+struct hnb_ue;
+
+int hnb_gtp_bind(struct hnb *hnb);
+void hnb_gtp_unbind(struct hnb *hnb);
+
+int hnb_ue_gtp_bind(struct hnb_ue *ue, const struct osmo_sockaddr *rem_addr, uint32_t rem_tei,
+ struct osmo_sockaddr *loc_addr, uint32_t *loc_tei);
+
+int hnb_ue_gtp_tx(struct hnb_ue *ue, void *gtpu_payload, unsigned gtpu_payload_len);
diff --git a/include/osmocom/hnodeb/hnb_prim.h b/include/osmocom/hnodeb/hnb_prim.h
index 8f9466d..fbcd07a 100644
--- a/include/osmocom/hnodeb/hnb_prim.h
+++ b/include/osmocom/hnodeb/hnb_prim.h
@@ -223,3 +223,71 @@
struct hnb_audio_conn_data_ind_param conn_data_ind;
} u;
} __attribute__ ((packed));
+
+/****************************
+ * GTP
+ ***************************/
+/*! \brief HNB_GTP primitives */
+enum hnb_gtp_prim_type {
+ HNB_GTP_PRIM_CONN_ESTABLISH,
+ HNB_GTP_PRIM_CONN_RELEASE,
+ HNB_GTP_PRIM_CONN_DATA,
+ _HNB_GTP_PRIM_MAX
+};
+
+/* HNB_GTP_PRIM_CONN_ESTABLISH, UL */
+struct hnb_gtp_conn_establish_req_param {
+ uint32_t context_id;
+ uint32_t remote_tei;
+ uint8_t spare1;
+ uint8_t remote_gtpu_address_type;
+ union u_addr remote_gtpu_addr;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_ESTABLISH, DL */
+struct hnb_gtp_conn_establish_cnf_param {
+ uint32_t context_id;
+ uint32_t local_tei;
+ uint8_t error_code; /* 0 = success, !0 = failure */
+ uint8_t local_gtpu_address_type; /* enum u_addr_type */
+ union u_addr local_gtpu_addr;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_RELEASE, UL */
+struct hnb_gtp_conn_release_req_param {
+ uint32_t context_id;
+ uint32_t remote_tei;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_RELEASE, DL */
+struct hnb_gtp_conn_release_ind_param {
+ uint32_t context_id;
+ uint32_t local_tei;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_DATA, DL */
+struct hnb_gtp_conn_data_ind_param {
+ uint32_t context_id;
+ uint32_t local_tei;
+ uint32_t data_len; /* GTP-U payload length in bytes */
+ char data[0]; /* GTP-U payload (aka IP packet) */
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_DATA, UL */
+struct hnb_gtp_conn_data_req_param {
+ uint32_t context_id;
+ uint32_t remote_tei;
+ uint32_t data_len; /* GTP-U payload length in bytes */
+ char data[0]; /* GTP-U payload (aka IP packet) */
+} __attribute__ ((packed));
+
+struct hnb_gtp_prim {
+ struct osmo_prim_hdr hdr;
+ union {
+ struct hnb_gtp_conn_establish_req_param conn_establish_req;
+ struct hnb_gtp_conn_establish_cnf_param conn_establish_cnf;
+ struct hnb_gtp_conn_release_req_param conn_release_req;
+ struct hnb_gtp_conn_data_req_param conn_data_req;
+ struct hnb_gtp_conn_data_ind_param conn_data_ind;
+ } u;
+} __attribute__ ((packed));
diff --git a/include/osmocom/hnodeb/hnodeb.h b/include/osmocom/hnodeb/hnodeb.h
index f3bf655..1dd1027 100644
--- a/include/osmocom/hnodeb/hnodeb.h
+++ b/include/osmocom/hnodeb/hnodeb.h
@@ -35,6 +35,9 @@
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/netif/stream.h>
+#include <gtp.h>
+#include <pdp.h>
+
#include <osmocom/hnodeb/llsk.h>
enum {
@@ -45,6 +48,7 @@
DSCTP,
DLLSK,
DRTP,
+ DGTP,
};
extern const struct log_info hnb_log_info;
@@ -65,6 +69,9 @@
struct hnb_ue_ps_ctx {
bool active; /* Is this chan in use? */
bool conn_est_cnf_pending; /* Did we send CONN_ESTABLISH_CNF to lower layers? */
+ uint32_t local_tei;
+ uint32_t remote_tei;
+ struct pdp_t *pdp_lib;
} conn_ps;
};
struct hnb_ue *hnb_ue_alloc(struct hnb *hnb, uint32_t conn_id);
@@ -72,6 +79,7 @@
void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps);
int hnb_ue_voicecall_setup(struct hnb_ue *ue, const struct osmo_sockaddr *rem_addr, struct osmo_sockaddr *loc_addr);
+
struct hnb {
char *identity; /* HNB-Identity */
struct osmo_plmn_id plmn;
@@ -104,6 +112,15 @@
int priority;
} rtp;
+ struct gtp {
+ char *cfg_local_addr;
+ struct osmo_sockaddr local_addr;
+ struct gsn_t *gsn;
+ struct osmo_fd fd0;
+ struct osmo_fd fd1c;
+ struct osmo_fd fd1u;
+ } gtp;
+
uint16_t rnc_id;
bool registered; /* Set to true once HnbRegisterAccept was received from Iuh. rnc_id is valid iif registered==true */
@@ -118,6 +135,7 @@
struct hnb *hnb_alloc(void *tall_ctx);
void hnb_free(struct hnb *hnb);
struct hnb_ue *hnb_find_ue_by_id(const struct hnb *hnb, uint32_t conn_id);
+struct hnb_ue *hnb_find_ue_by_tei(const struct hnb *hnb, uint32_t tei, bool is_remote);
struct hnb_ue *hnb_find_ue_by_imsi(const struct hnb *hnb, char *imsi);
extern void *tall_hnb_ctx;
diff --git a/include/osmocom/hnodeb/llsk.h b/include/osmocom/hnodeb/llsk.h
index 081ee88..a6d8a57 100644
--- a/include/osmocom/hnodeb/llsk.h
+++ b/include/osmocom/hnodeb/llsk.h
@@ -56,3 +56,8 @@
uint8_t domain,
const uint8_t *data,
uint32_t data_len);
+
+extern const struct value_string hnb_gtp_prim_type_names[];
+int llsk_rx_gtp(struct hnb *hnb, struct osmo_prim_hdr *oph);
+struct hnb_gtp_prim *hnb_gtp_makeprim_conn_data_ind(uint32_t context_id, uint32_t local_tei,
+ const uint8_t *data, uint32_t data_len);
diff --git a/include/osmocom/hnodeb/vty.h b/include/osmocom/hnodeb/vty.h
index 627436d..5c11302 100644
--- a/include/osmocom/hnodeb/vty.h
+++ b/include/osmocom/hnodeb/vty.h
@@ -28,6 +28,7 @@
HNODEB_NODE = _LAST_OSMOVTY_NODE,
IUH_NODE,
LLSK_NODE,
+ GTP_NODE,
};
void hnb_vty_init(void);
diff --git a/src/osmo-hnodeb/Makefile.am b/src/osmo-hnodeb/Makefile.am
index 4182814..6edbf88 100644
--- a/src/osmo-hnodeb/Makefile.am
+++ b/src/osmo-hnodeb/Makefile.am
@@ -19,6 +19,7 @@
$(LIBOSMORUA_CFLAGS) \
$(LIBOSMORANAP_CFLAGS) \
$(LIBOSMOHNBAP_CFLAGS) \
+ $(LIBGTP_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
@@ -32,12 +33,14 @@
osmo_hnodeb_SOURCES = \
main.c \
debug.c \
+ gtp.c \
hnbap.c \
hnb.c \
hnb_shutdown_fsm.c \
iuh.c \
llsk.c \
llsk_audio.c \
+ llsk_gtp.c \
llsk_iuh.c \
ranap.c \
rtp.c \
@@ -60,4 +63,5 @@
$(LIBOSMORANAP_LIBS) \
$(LIBOSMOHNBAP_LIBS) \
$(LIBSCTP_LIBS) \
+ $(LIBGTP_LIBS) \
$(NULL)
diff --git a/src/osmo-hnodeb/debug.c b/src/osmo-hnodeb/debug.c
index 5d0dabd..f122b86 100644
--- a/src/osmo-hnodeb/debug.c
+++ b/src/osmo-hnodeb/debug.c
@@ -57,6 +57,11 @@
.color = "\033[1;32m",
.description = "RTP Core Network side",
},
+ [DRTP] = {
+ .name = "DRTP", .loglevel = LOGL_NOTICE, .enabled = 1,
+ .color = "\033[1;30m",
+ .description = "GPRS Tunnelling Protocol",
+ },
};
const struct log_info hnb_log_info = {
diff --git a/src/osmo-hnodeb/gtp.c b/src/osmo-hnodeb/gtp.c
new file mode 100644
index 0000000..ef7b3f8
--- /dev/null
+++ b/src/osmo-hnodeb/gtp.c
@@ -0,0 +1,233 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at 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/lienses/>.
+ *
+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <osmocom/hnodeb/gtp.h>
+#include <osmocom/hnodeb/hnodeb.h>
+#include <osmocom/hnodeb/llsk.h>
+
+#include <gtp.h>
+#include <pdp.h>
+
+/* Get osa of locally bound GTP-U socket */
+int sk_get_bound_addr(int fd, struct osmo_sockaddr *osa)
+{
+ int rc;
+ //uint16_t port;
+ socklen_t alen = sizeof(*osa);
+
+ rc = getsockname(fd, (struct sockaddr *)&osa->u.sa, &alen);
+ if (rc < 0)
+ return rc;
+
+ /* Is this really needed? or getsockname already gathers the port? */
+ /*port = rtp_session_get_local_port(rs->sess);
+ switch (osa->u.sa.sa_family) {
+ case AF_INET6:
+ osa->u.sin6.sin6_port = htons(port);
+ case AF_INET:
+ osa->u.sin.sin_port = htons(port);
+ default:
+ return -1;
+ }*/
+
+ return 0;
+}
+
+/* Called whenever we receive a DATA packet */
+static int hnb_gtp_cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
+{
+ struct hnb_gtp_prim *gtp_prim;
+ struct hnb_ue *ue = lib->priv;
+ struct hnb *hnb = ue->hnb;
+ int rc;
+
+ LOGUE(ue, DGTP, LOGL_DEBUG, "Rx GTP-U packet %p len=%u\n", packet, len);
+
+ if (!ue || !ue->conn_ps.active) {
+ LOGUE(ue, DGTP, LOGL_NOTICE, "Rx GTP-U packet %p len=%u but UE conn_ps is not active!\n", packet, len);
+ return -EINVAL;
+ }
+
+ LOGUE(ue, DGTP, LOGL_DEBUG, "Tx GTP-CONN_DATA.req data=%p len=%u\n", packet, len);
+ gtp_prim = hnb_gtp_makeprim_conn_data_ind(ue->conn_id, ue->conn_ps.local_tei, packet, len);
+ if ((rc = osmo_prim_srv_send(hnb->llsk, gtp_prim->hdr.msg)) < 0) {
+ LOGUE(ue, DGTP, LOGL_ERROR, "Failed Tx GTP-CONN_DATA.req data=%p len=%u\n",
+ packet, len);
+ }
+ return rc;
+}
+
+/* libgtp select loop integration */
+static int hnb_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
+{
+ struct hnb *hnb = fd->data;
+ int rc;
+
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ switch (fd->priv_nr) {
+ case 0:
+ rc = gtp_decaps0(hnb->gtp.gsn);
+ break;
+ case 1:
+ rc = gtp_decaps1c(hnb->gtp.gsn);
+ break;
+ case 2:
+ rc = gtp_decaps1u(hnb->gtp.gsn);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+int hnb_gtp_bind(struct hnb *hnb)
+{
+ int rc;
+ struct gsn_t *gsn;
+ struct in_addr inaddr;
+
+ rc = inet_pton(AF_INET, hnb->gtp.cfg_local_addr, &inaddr);
+ if (rc <= 0)
+ return -EINVAL;
+
+ /* TODO: add new mode GTP_MODE_GTPU_ONLY to set up gtpu side only (and ignore statedir) */
+ rc = gtp_new(&gsn, "/tmp", &inaddr, GTP_MODE_SGSN);
+ if (rc < 0) {
+ LOGP(DGTP, LOGL_ERROR, "Failed to set up GTP socket: %s\n", strerror(-rc));
+ return rc;
+ }
+
+ rc = sk_get_bound_addr(gsn->fd1u, &hnb->gtp.local_addr);
+ if (rc < 0) {
+ LOGP(DGTP, LOGL_ERROR, "Failed to get GTP-U socket bound address: %s\n", strerror(-rc));
+ return rc;
+ }
+
+ osmo_fd_setup(&hnb->gtp.fd0, gsn->fd0, OSMO_FD_READ, hnb_gtp_fd_cb, hnb, 0);
+ if ((rc = osmo_fd_register(&hnb->gtp.fd0)) < 0)
+ goto free_ret;
+
+ osmo_fd_setup(&hnb->gtp.fd1c, gsn->fd1c, OSMO_FD_READ, hnb_gtp_fd_cb, hnb, 1);
+ if ((rc = osmo_fd_register(&hnb->gtp.fd1c)) < 0)
+ goto free_ret;
+
+ osmo_fd_setup(&hnb->gtp.fd1u, gsn->fd1u, OSMO_FD_READ, hnb_gtp_fd_cb, hnb, 2);
+ if ((rc = osmo_fd_register(&hnb->gtp.fd1u)) < 0)
+ goto free_ret;
+
+ gtp_set_cb_data_ind(gsn, hnb_gtp_cb_data_ind);
+
+ hnb->gtp.gsn = gsn;
+ return 0;
+
+free_ret:
+ osmo_fd_unregister(&hnb->gtp.fd1u);
+ osmo_fd_unregister(&hnb->gtp.fd1c);
+ osmo_fd_unregister(&hnb->gtp.fd0);
+ gtp_free(gsn);
+ hnb->gtp.fd0.fd = -1;
+ hnb->gtp.fd1c.fd = -1;
+ hnb->gtp.fd1u.fd = -1;
+ return rc;
+}
+
+void hnb_gtp_unbind(struct hnb *hnb)
+{
+ osmo_fd_unregister(&hnb->gtp.fd1u);
+ osmo_fd_unregister(&hnb->gtp.fd1c);
+ osmo_fd_unregister(&hnb->gtp.fd0);
+ gtp_free(hnb->gtp.gsn);
+ hnb->gtp.gsn = NULL;
+ hnb->gtp.fd0.fd = -1;
+ hnb->gtp.fd1c.fd = -1;
+ hnb->gtp.fd1u.fd = -1;
+}
+
+int hnb_ue_gtp_bind(struct hnb_ue *ue, const struct osmo_sockaddr *rem_addr, uint32_t rem_tei,
+ struct osmo_sockaddr *loc_addr, uint32_t *loc_tei)
+{
+ int rc;
+ struct hnb *hnb = ue->hnb;
+ struct pdp_t *pdp;
+ struct in_addr rem_in;
+
+ LOGUE(ue, DGTP, LOGL_INFO, "Creating PDP context\n");
+
+
+ if (rem_addr->u.sa.sa_family != AF_INET) {
+ LOGUE(ue, DGTP, LOGL_ERROR, "Failed creating PDP context: unsupported proto family %u\n",
+ rem_addr->u.sa.sa_family);
+ return -ENOTSUP;
+ }
+
+ rem_in = rem_addr->u.sin.sin_addr;
+
+ rc = gtp_pdp_newpdp(hnb->gtp.gsn, &pdp, ue->conn_id, 0 /* TODO: NSAPI? */, NULL);
+ if (rc < 0) {
+ LOGUE(ue, DGTP, LOGL_ERROR, "Failed creating PDP context: %s\n", strerror(-rc));
+ return rc;
+ }
+ pdp->priv = ue;
+ ue->conn_ps.pdp_lib = pdp;
+
+ pdp->teid_gn = rem_tei;
+ pdp->version = 1;
+ pdp->hisaddr0 = rem_in;
+ pdp->hisaddr1 = rem_in;
+
+ pdp->gsnru.l = sizeof(rem_in);
+ memcpy(pdp->gsnru.v, &rem_in, sizeof(rem_in));
+
+
+ pdp->gsnlu.l = sizeof(hnb->gtp.local_addr.u.sin.sin_addr);
+ memcpy(pdp->gsnlu.v, &hnb->gtp.local_addr.u.sin.sin_addr,
+ sizeof(hnb->gtp.local_addr.u.sin.sin_addr));
+
+ *loc_addr = hnb->gtp.local_addr;
+ //loc_addr->u.sin.sin_family = AF_INET;
+ //loc_addr->u.sin.sin_addr = hnb->gtp.gsn->gsnu;
+ //loc_addr->u.sin.sin_port = GTP1U_PORT;
+ *loc_tei = pdp->teid_own;
+ return 0;
+}
+
+int hnb_ue_gtp_tx(struct hnb_ue *ue, void *gtpu_payload, unsigned gtpu_payload_len)
+{
+ int rc;
+ struct hnb *hnb = ue->hnb;
+
+ if (!hnb->gtp.gsn) {
+ LOGUE(ue, DGTP, LOGL_ERROR, "Tx: GTP socket not bound\n");
+ return -EINVAL;
+ }
+
+ if (!ue || !ue->conn_ps.pdp_lib) {
+ LOGUE(ue, DGTP, LOGL_ERROR, "Tx: UE PDP Ctx not available\n");
+ return -EINVAL;
+ }
+
+ rc = gtp_data_req(hnb->gtp.gsn, ue->conn_ps.pdp_lib, gtpu_payload, gtpu_payload_len);
+ return rc;
+}
diff --git a/src/osmo-hnodeb/hnb.c b/src/osmo-hnodeb/hnb.c
index f8878bd..787985a 100644
--- a/src/osmo-hnodeb/hnb.c
+++ b/src/osmo-hnodeb/hnb.c
@@ -57,6 +57,11 @@
hnb->rtp.ip_dscp = -1;
hnb->rtp.priority = -1;
+ hnb->gtp.cfg_local_addr = talloc_strdup(hnb, "0.0.0.0");
+ hnb->gtp.fd0.fd = -1;
+ hnb->gtp.fd1c.fd = -1;
+ hnb->gtp.fd1u.fd = -1;
+
hnb->shutdown_fi = osmo_fsm_inst_alloc(&hnb_shutdown_fsm, hnb, hnb,
LOGL_INFO, NULL);
@@ -84,6 +89,11 @@
osmo_prim_srv_link_free(hnb->llsk_link);
hnb->llsk_link = NULL;
+ if (hnb->gtp.gsn) {
+ gtp_free(hnb->gtp.gsn);
+ hnb->gtp.gsn = NULL;
+ }
+
talloc_free(hnb);
}
@@ -114,6 +124,8 @@
void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps)
{
if (is_ps) {
+ if (ue->conn_ps.pdp_lib)
+ pdp_freepdp(ue->conn_ps.pdp_lib);
ue->conn_ps = (struct hnb_ue_ps_ctx){0};
} else {
if (ue->conn_cs.rtp.socket)
@@ -199,6 +211,22 @@
}
return NULL;
}
+
+struct hnb_ue *hnb_find_ue_by_tei(const struct hnb *hnb, uint32_t tei, bool is_remote)
+{
+ struct hnb_ue *ue;
+
+ llist_for_each_entry(ue, &hnb->ue_list, list) {
+ if (!ue->conn_ps.active)
+ continue;
+ uint32_t ue_tei = is_remote ? ue->conn_ps.remote_tei : ue->conn_ps.local_tei;
+ if (tei != ue_tei)
+ continue;
+ return ue;
+ }
+ return NULL;
+}
+
struct hnb_ue *hnb_find_ue_by_imsi(const struct hnb *hnb, char *imsi)
{
struct hnb_ue *ue;
diff --git a/src/osmo-hnodeb/llsk.c b/src/osmo-hnodeb/llsk.c
index 121a3a8..92e5e91 100644
--- a/src/osmo-hnodeb/llsk.c
+++ b/src/osmo-hnodeb/llsk.c
@@ -129,7 +129,8 @@
return false;
if (hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_IUH) &&
- hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_AUDIO))
+ hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_AUDIO) &&
+ hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_GTP))
return true;
return false;
}
@@ -164,9 +165,7 @@
case HNB_PRIM_SAPI_IUH:
return llsk_rx_iuh(hnb, oph);
case HNB_PRIM_SAPI_GTP:
- LOGP(DLLSK, LOGL_ERROR, "Rx SAPI %u not yet implemented (len=%u)\n",
- oph->sap, msgb_length(oph->msg));
- return -EINVAL;
+ return llsk_rx_gtp(hnb, oph);
case HNB_PRIM_SAPI_AUDIO:
return llsk_rx_audio(hnb, oph);
default:
diff --git a/src/osmo-hnodeb/llsk_gtp.c b/src/osmo-hnodeb/llsk_gtp.c
new file mode 100644
index 0000000..f526426
--- /dev/null
+++ b/src/osmo-hnodeb/llsk_gtp.c
@@ -0,0 +1,278 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at 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/lienses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <inttypes.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+
+#include <osmocom/hnodeb/hnodeb.h>
+#include <osmocom/hnodeb/llsk.h>
+#include <osmocom/hnodeb/hnb_prim.h>
+#include <osmocom/hnodeb/gtp.h>
+
+static size_t llsk_gtp_prim_size_tbl[4][_HNB_GTP_PRIM_MAX] = {
+ [PRIM_OP_REQUEST] = {
+ [HNB_GTP_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_gtp_conn_establish_req_param),
+ [HNB_GTP_PRIM_CONN_RELEASE] = sizeof(struct hnb_gtp_conn_release_req_param),
+ [HNB_GTP_PRIM_CONN_DATA] = sizeof(struct hnb_gtp_conn_data_req_param),
+ },
+ [PRIM_OP_RESPONSE] = {},
+ [PRIM_OP_INDICATION] = {
+ [HNB_GTP_PRIM_CONN_DATA] = sizeof(struct hnb_gtp_conn_data_ind_param),
+ },
+ [PRIM_OP_CONFIRM] = {
+ [HNB_GTP_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_gtp_conn_establish_cnf_param),
+ },
+};
+static inline size_t llsk_gtp_prim_size(enum hnb_gtp_prim_type ptype, enum osmo_prim_operation op)
+{
+ size_t val = llsk_gtp_prim_size_tbl[op][ptype];
+ if (val == 0) {
+ LOGP(DLLSK, LOGL_FATAL, "Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
+ osmo_panic("Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
+ }
+ return val;
+}
+
+const struct value_string hnb_gtp_prim_type_names[] = {
+ OSMO_VALUE_STRING(HNB_GTP_PRIM_CONN_ESTABLISH),
+ OSMO_VALUE_STRING(HNB_GTP_PRIM_CONN_RELEASE),
+ OSMO_VALUE_STRING(HNB_GTP_PRIM_CONN_DATA),
+ { 0, NULL }
+};
+
+static struct hnb_gtp_prim *hnb_gtp_prim_alloc(enum hnb_gtp_prim_type ptype, enum osmo_prim_operation op, size_t extra_len)
+{
+ struct osmo_prim_hdr *oph;
+ size_t len = llsk_gtp_prim_size(ptype, op);
+
+ oph = osmo_prim_msgb_alloc(HNB_PRIM_SAPI_GTP, ptype, op, sizeof(*oph) + len + extra_len);
+ if (!oph)
+ return NULL;
+ msgb_put(oph->msg, len);
+
+ return (struct hnb_gtp_prim *)oph;
+}
+
+static struct hnb_gtp_prim *hnb_gtp_makeprim_conn_establish_cnf(uint32_t context_id, uint8_t error_code,
+ uint32_t local_tei, uint8_t local_gtpu_address_type,
+ const union u_addr *local_gtpu_addr)
+{
+ struct hnb_gtp_prim *gtp_prim;
+
+ gtp_prim = hnb_gtp_prim_alloc(HNB_GTP_PRIM_CONN_ESTABLISH, PRIM_OP_CONFIRM, 0);
+ gtp_prim->u.conn_establish_cnf.context_id = context_id;
+ gtp_prim->u.conn_establish_cnf.local_tei = local_tei;
+ gtp_prim->u.conn_establish_cnf.error_code = error_code;
+ gtp_prim->u.conn_establish_cnf.local_gtpu_address_type = local_gtpu_address_type;
+ if (local_gtpu_addr)
+ gtp_prim->u.conn_establish_cnf.local_gtpu_addr = *local_gtpu_addr;
+
+ return gtp_prim;
+}
+
+struct hnb_gtp_prim *hnb_gtp_makeprim_conn_data_ind(uint32_t context_id, uint32_t local_tei,
+ const uint8_t *data, uint32_t data_len)
+{
+ struct hnb_gtp_prim *gtp_prim;
+
+ gtp_prim = hnb_gtp_prim_alloc(HNB_GTP_PRIM_CONN_DATA, PRIM_OP_INDICATION, data_len);
+ gtp_prim->u.conn_data_ind.context_id = context_id;
+ gtp_prim->u.conn_data_ind.local_tei = local_tei;
+ gtp_prim->u.conn_data_ind.data_len = data_len;
+ if (data_len) {
+ msgb_put(gtp_prim->hdr.msg, data_len);
+ memcpy(gtp_prim->u.conn_data_ind.data, data, data_len);
+ }
+
+ return gtp_prim;
+}
+
+static int _send_conn_establish_cnf_failed(struct hnb *hnb, uint32_t context_id, uint8_t error_code)
+{
+ struct hnb_gtp_prim *gtp_prim;
+ int rc;
+ LOGP(DLLSK, LOGL_ERROR, "Tx GTP-CONN_ESTABLISH.cnf: ctx=%u error_code=%u\n",
+ context_id, error_code);
+ gtp_prim = hnb_gtp_makeprim_conn_establish_cnf(context_id, error_code, 0, HNB_PRIM_ADDR_TYPE_UNSPEC, NULL);
+ if ((rc = osmo_prim_srv_send(hnb->llsk, gtp_prim->hdr.msg)) < 0) {
+ LOGP(DLLSK, LOGL_ERROR, "Failed sending GTP-CONN_ESTABLISH.cnf context_id=%u error_code=%u\n",
+ context_id, error_code);
+ }
+ return rc;
+}
+
+static int llsk_rx_gtp_conn_establish_req(struct hnb *hnb, struct hnb_gtp_conn_establish_req_param *ce_req)
+{
+ struct hnb_ue *ue;
+ int rc = 0;
+ struct hnb_gtp_prim *gtp_prim;
+ int af;
+ char rem_addrstr[INET6_ADDRSTRLEN+32];
+ struct osmo_sockaddr rem_osa = {0};
+ struct osmo_sockaddr loc_osa = {0};
+ union u_addr loc_uaddr = {0};
+ uint32_t loc_tei;
+
+ rc = ll_addr2osa(ce_req->remote_gtpu_address_type, &ce_req->remote_gtpu_addr, GTP1U_PORT, &rem_osa);
+ if (rc < 0) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n",
+ ce_req->context_id, ce_req->remote_gtpu_address_type);
+ return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 1);
+ }
+ osmo_sockaddr_to_str_buf(rem_addrstr, sizeof(rem_addrstr), &rem_osa);
+
+ LOGP(DLLSK, LOGL_INFO, "Rx GTP-CONN_ESTABLISH.req ctx=%u rem_tei=%u rem_addr=%s\n",
+ ce_req->context_id, ce_req->remote_tei, rem_addrstr);
+
+ if ((af = ll_addr_type2af(ce_req->remote_gtpu_address_type)) < 0) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n",
+ ce_req->context_id, ce_req->remote_gtpu_address_type);
+ return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 1);
+ }
+
+ ue = hnb_find_ue_by_id(hnb, ce_req->context_id);
+ if (!ue) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: UE not found! ctx=%u rem_addr=%s\n",
+ ce_req->context_id, rem_addrstr);
+ return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 2);
+ }
+ if (!ue->conn_ps.active) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: PS chan not active! ctx=%u rem_addr=%s\n",
+ ce_req->context_id, rem_addrstr);
+ return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 3);
+ }
+
+ /* Create the socket: */
+ if ((rc = hnb_ue_gtp_bind(ue, &rem_osa, ce_req->remote_tei, &loc_osa, &loc_tei)) < 0) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: Failed to set up gtp socket ctx=%u rem_tei=%u rem_addr=%s\n",
+ ce_req->context_id, ce_req->remote_tei, rem_addrstr);
+ return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4);
+ }
+
+ /* Convert resulting local address back to LLSK format: */
+ if (osa2_ll_addr(&loc_osa, &loc_uaddr, NULL) != ce_req->remote_gtpu_address_type) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: Failed to provide proper local address ctx=%u rem_addr=%s\n",
+ ce_req->context_id, rem_addrstr);
+ rc = _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4);
+ goto release_sock;
+ }
+
+ /* Submit successful confirmation */
+ LOGP(DLLSK, LOGL_INFO, "Tx GTP-CONN_ESTABLISH.cnf: ctx=%u error_code=0 rem_addr=%s rem_tei=%u loc_addr=%s local_tei=%u\n",
+ ce_req->context_id, rem_addrstr, ce_req->remote_tei, osmo_sockaddr_to_str(&loc_osa), loc_tei);
+ gtp_prim = hnb_gtp_makeprim_conn_establish_cnf(ce_req->context_id, 0, loc_tei, ce_req->remote_gtpu_address_type, &loc_uaddr);
+ if ((rc = osmo_prim_srv_send(hnb->llsk, gtp_prim->hdr.msg)) < 0) {
+ LOGP(DLLSK, LOGL_ERROR, "Failed sending GTP-CONN_ESTABLISH.cnf context_id=%u error_code=0\n",
+ ce_req->context_id);
+ goto release_sock;
+ }
+
+ ue->conn_ps.local_tei = loc_tei;
+ ue->conn_ps.remote_tei = ce_req->remote_tei;
+
+ return rc;
+release_sock: /* TODO: release socket here */
+ return rc;
+}
+
+static int llsk_rx_gtp_conn_release_req(struct hnb *hnb, struct hnb_gtp_conn_release_req_param *rel_req)
+{
+ struct hnb_ue *ue;
+ int rc = 0;
+
+ LOGP(DLLSK, LOGL_DEBUG, "Rx GTP-CONN_RELEASE.req ctx=%u\n", rel_req->context_id);
+
+ ue = hnb_find_ue_by_id(hnb, rel_req->context_id);
+ if (!ue) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_RELEASE.req: UE not found! ctx=%u\n",
+ rel_req->context_id);
+ return -EINVAL;
+ }
+ /* TODO: release GTP socket */
+ return rc;
+}
+
+static int llsk_rx_gtp_conn_data_req(struct hnb *hnb, struct hnb_gtp_conn_data_req_param *data_req)
+{
+ struct hnb_ue *ue;
+ int rc = 0;
+
+ LOGP(DLLSK, LOGL_DEBUG, "Rx GTP-CONN_DATA.req ctx=%u rem_tei=%u data_len=%u\n",
+ data_req->context_id, data_req->remote_tei, data_req->data_len);
+
+ ue = hnb_find_ue_by_id(hnb, data_req->context_id);
+ if (!ue) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_DATA.req: UE not found! ctx=%u data_len=%u\n",
+ data_req->context_id, data_req->data_len);
+ return -EINVAL;
+ }
+
+ rc = hnb_ue_gtp_tx(ue, data_req->data, data_req->data_len);
+ return rc;
+}
+
+int llsk_rx_gtp(struct hnb *hnb, struct osmo_prim_hdr *oph)
+{
+ size_t prim_size = llsk_gtp_prim_size(oph->primitive, oph->operation);
+
+ if (msgb_length(oph->msg) < prim_size) {
+ LOGP(DLLSK, LOGL_ERROR, "Rx GTP-%s.%s with length %u < %zu\n",
+ get_value_string(hnb_gtp_prim_type_names, oph->primitive),
+ get_value_string(osmo_prim_op_names, oph->operation),
+ msgb_length(oph->msg), prim_size);
+ return -EINVAL;
+ }
+
+ switch (oph->operation) {
+ case PRIM_OP_REQUEST:
+ switch (oph->primitive) {
+ case HNB_GTP_PRIM_CONN_ESTABLISH:
+ return llsk_rx_gtp_conn_establish_req(hnb, (struct hnb_gtp_conn_establish_req_param *)msgb_data(oph->msg));
+ case HNB_GTP_PRIM_CONN_RELEASE:
+ return llsk_rx_gtp_conn_release_req(hnb, (struct hnb_gtp_conn_release_req_param *)msgb_data(oph->msg));
+ case HNB_GTP_PRIM_CONN_DATA:
+ return llsk_rx_gtp_conn_data_req(hnb, (struct hnb_gtp_conn_data_req_param *)msgb_data(oph->msg));
+ default:
+ LOGP(DLLSK, LOGL_ERROR, "Rx llsk-gtp unknown primitive %u (len=%u)\n",
+ oph->primitive, msgb_length(oph->msg));
+ return -EINVAL;
+ }
+ break;
+
+ case PRIM_OP_RESPONSE:
+ case PRIM_OP_INDICATION:
+ case PRIM_OP_CONFIRM:
+ default:
+ LOGP(DLLSK, LOGL_ERROR, "Rx llsk-gtp unexpected primitive operation %s::%s (len=%u)\n",
+ get_value_string(hnb_gtp_prim_type_names, oph->primitive),
+ get_value_string(osmo_prim_op_names, oph->operation),
+ msgb_length(oph->msg));
+ return -EINVAL;
+ }
+}
diff --git a/src/osmo-hnodeb/main.c b/src/osmo-hnodeb/main.c
index 9af83ea..5d35c18 100644
--- a/src/osmo-hnodeb/main.c
+++ b/src/osmo-hnodeb/main.c
@@ -51,6 +51,7 @@
#include <osmocom/hnodeb/vty.h>
#include <osmocom/hnodeb/hnodeb.h>
#include <osmocom/hnodeb/iuh.h>
+#include <osmocom/hnodeb/gtp.h>
static const char * const osmohnodeb_copyright =
"OsmoHNodeB - Osmocom 3G Home NodeB implementation\r\n"
@@ -280,6 +281,12 @@
exit(1);
}
+ rc = hnb_gtp_bind(g_hnb);
+ if (rc < 0) {
+ perror("Error listening on GTP port");
+ exit(1);
+ }
+
rc = hnb_iuh_connect(g_hnb);
if (rc < 0) {
perror("Error connecting to Iuh port");
@@ -305,6 +312,8 @@
osmo_select_main_ctx(0);
}
+ hnb_gtp_unbind(g_hnb);
+
log_fini();
/**
diff --git a/src/osmo-hnodeb/vty.c b/src/osmo-hnodeb/vty.c
index 131dc6b..e84e330 100644
--- a/src/osmo-hnodeb/vty.c
+++ b/src/osmo-hnodeb/vty.c
@@ -51,6 +51,10 @@
vty->node = HNODEB_NODE;
vty->index = g_hnb;
break;
+ case GTP_NODE:
+ vty->node = HNODEB_NODE;
+ vty->index = g_hnb;
+ break;
case HNODEB_NODE:
vty->node = CONFIG_NODE;
vty->index = g_hnb;
@@ -281,6 +285,33 @@
return CMD_SUCCESS;
}
+static struct cmd_node gtp_node = {
+ GTP_NODE,
+ "%s(config-gtp)# ",
+ 1,
+};
+
+#define GTP_STR "Configure the GPRS Tunnelling Protocol parameters\n"
+
+DEFUN(cfg_hnodeb_gtp,
+ cfg_hnodeb_gtp_cmd,
+ "gtp", GTP_STR)
+{
+ OSMO_ASSERT(g_hnb);
+ vty->index = g_hnb;
+ vty->node = GTP_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnodeb_gtp_local_ip, cfg_hnodeb_gtp_local_ip_cmd,
+ "local-ip " VTY_IPV4_CMD,
+ "Configure the GTP-U bind address\n"
+ "GTP-U local IPv4 address\n")
+{
+ osmo_talloc_replace_string(g_hnb, &g_hnb->gtp.cfg_local_addr, argv[0]);
+ return CMD_SUCCESS;
+}
static int config_write_hnodeb(struct vty *vty)
{
@@ -302,6 +333,8 @@
vty_out(vty, " remote-port %u%s", g_hnb->iuh.remote_port, VTY_NEWLINE);
vty_out(vty, " ll-socket%s", VTY_NEWLINE);
vty_out(vty, " path %s%s", osmo_prim_srv_link_get_addr(g_hnb->llsk_link), VTY_NEWLINE);
+ vty_out(vty, " gtp%s", VTY_NEWLINE);
+ vty_out(vty, " local-ip %s%s", g_hnb->gtp.cfg_local_addr, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -359,6 +392,9 @@
install_element(HNODEB_NODE, &cfg_hnodeb_llsk_cmd);
install_node(&llsk_node, NULL);
install_element(LLSK_NODE, &cfg_hnodeb_llsk_path_cmd);
+ install_element(HNODEB_NODE, &cfg_hnodeb_gtp_cmd);
+ install_node(>p_node, NULL);
+ install_element(GTP_NODE, &cfg_hnodeb_gtp_local_ip_cmd);
install_element_ve(&asn_dbg_cmd);
install_element_ve(&ranap_reset_cmd);
--
To view, visit https://gerrit.osmocom.org/c/osmo-hnodeb/+/26503
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-hnodeb
Gerrit-Branch: master
Gerrit-Change-Id: I5a6f5dfc4e508c92adb35210b4dc576d64353366
Gerrit-Change-Number: 26503
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20211210/9fd0c999/attachment.htm>