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>