First implementation of the LLSK gtp SAPI
This first implementation uses libgtp to manage the GTPv1-U socket and pdp contexts, as a proof of concept to have something working for now. At a later step, it is expected to drop libgtp and use some adhoc code to handle GTP-U. Related: SYS#5516 Change-Id: I5a6f5dfc4e508c92adb35210b4dc576d64353366
This commit is contained in:
parent
b3af382157
commit
a7dc04ef26
|
@ -67,6 +67,7 @@ PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.5.0)
|
|||
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
|
||||
|
|
|
@ -34,6 +34,7 @@ osmo-build-dep.sh libosmo-abis
|
|||
osmo-build-dep.sh libosmo-netif
|
||||
osmo-build-dep.sh libosmo-sccp
|
||||
osmo-build-dep.sh libasn1c
|
||||
osmo-build-dep.sh osmo-ggsn
|
||||
osmo-build-dep.sh osmo-iuh
|
||||
|
||||
# Additional configure options and depends
|
||||
|
|
|
@ -46,6 +46,7 @@ BuildRequires: pkgconfig(libosmo-ranap) >= 0.8.0
|
|||
BuildRequires: pkgconfig(libosmo-rua) >= 0.8.0
|
||||
BuildRequires: pkgconfig(talloc)
|
||||
BuildRequires: pkgconfig(libasn1c) >= 0.9.30
|
||||
BuildRequires: pkgconfig(libgtp) >= 1.8.0
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
|
|
|
@ -13,6 +13,7 @@ Build-Depends: debhelper (>=9),
|
|||
python3-minimal,
|
||||
libtalloc-dev,
|
||||
libasn1c-dev (>= 0.9.30),
|
||||
libgtp-dev (>= 1.8.0),
|
||||
libsctp-dev,
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
libosmo-sigtran-dev (>= 1.5.0),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
noinst_HEADERS = \
|
||||
gtp.h \
|
||||
hnb_shutdown_fsm.h \
|
||||
hnb_prim.h \
|
||||
hnbap.h \
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/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_unbind(struct hnb_ue *ue);
|
||||
int hnb_ue_gtp_tx(struct hnb_ue *ue, void *gtpu_payload, unsigned gtpu_payload_len);
|
|
@ -232,3 +232,71 @@ struct hnb_audio_prim {
|
|||
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 */
|
||||
uint8_t 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 */
|
||||
uint8_t 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));
|
||||
|
|
|
@ -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 @@ enum {
|
|||
DSCTP,
|
||||
DLLSK,
|
||||
DRTP,
|
||||
DGTP,
|
||||
};
|
||||
extern const struct log_info hnb_log_info;
|
||||
|
||||
|
@ -65,12 +69,16 @@ struct hnb_ue {
|
|||
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);
|
||||
void hnb_ue_free(struct hnb_ue *ue);
|
||||
void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps);
|
||||
|
||||
|
||||
struct hnb {
|
||||
char *identity; /* HNB-Identity */
|
||||
struct osmo_plmn_id plmn;
|
||||
|
@ -103,6 +111,13 @@ struct hnb {
|
|||
int priority;
|
||||
} rtp;
|
||||
|
||||
struct gtp {
|
||||
char *cfg_local_addr;
|
||||
struct osmo_sockaddr local_addr;
|
||||
struct gsn_t *gsn;
|
||||
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 */
|
||||
|
||||
|
@ -117,6 +132,7 @@ struct hnb {
|
|||
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;
|
||||
|
|
|
@ -54,3 +54,8 @@ struct hnb_iuh_prim *hnb_iuh_makeprim_unitdata_ind(const uint8_t *data, uint32_t
|
|||
extern const struct value_string hnb_audio_prim_type_names[];
|
||||
int llsk_rx_audio(struct hnb *hnb, struct osmo_prim_hdr *oph);
|
||||
int llsk_audio_tx_conn_data_ind(struct hnb_ue *ue, const uint8_t *payload, uint32_t 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);
|
||||
|
|
|
@ -28,6 +28,7 @@ enum hnb_vty_nodes {
|
|||
HNODEB_NODE = _LAST_OSMOVTY_NODE,
|
||||
IUH_NODE,
|
||||
LLSK_NODE,
|
||||
GTP_NODE,
|
||||
};
|
||||
|
||||
void hnb_vty_init(void);
|
||||
|
|
|
@ -19,6 +19,7 @@ AM_CFLAGS = \
|
|||
$(LIBOSMORUA_CFLAGS) \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
$(LIBOSMOHNBAP_CFLAGS) \
|
||||
$(LIBGTP_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
|
@ -32,12 +33,14 @@ bin_PROGRAMS = \
|
|||
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 @@ osmo_hnodeb_LDADD = \
|
|||
$(LIBOSMORANAP_LIBS) \
|
||||
$(LIBOSMOHNBAP_LIBS) \
|
||||
$(LIBSCTP_LIBS) \
|
||||
$(LIBGTP_LIBS) \
|
||||
$(NULL)
|
||||
|
|
|
@ -57,6 +57,11 @@ static const struct log_info_cat log_cat[] = {
|
|||
.color = "\033[1;32m",
|
||||
.description = "RTP Core Network side",
|
||||
},
|
||||
[DGTP] = {
|
||||
.name = "DGTP", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "\033[1;30m",
|
||||
.description = "GPRS Tunnelling Protocol",
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info hnb_log_info = {
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/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;
|
||||
socklen_t alen = sizeof(*osa);
|
||||
|
||||
rc = getsockname(fd, (struct sockaddr *)&osa->u.sa, &alen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
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;
|
||||
|
||||
if (!ue || !ue->conn_ps.active) {
|
||||
LOGUE(ue, DGTP, LOGL_NOTICE, "Tx GTP-CONN_DATA.ind data=%p len=%u but UE conn_ps is not active!\n",
|
||||
packet, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGUE(ue, DGTP, LOGL_DEBUG, "Tx GTP-CONN_DATA.ind 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.ind 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_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));
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
osmo_fd_setup(&hnb->gtp.fd1u, gsn->fd1u, OSMO_FD_READ, hnb_gtp_fd_cb, hnb, 0);
|
||||
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:
|
||||
gtp_free(gsn);
|
||||
hnb->gtp.fd1u.fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void hnb_gtp_unbind(struct hnb *hnb)
|
||||
{
|
||||
osmo_fd_unregister(&hnb->gtp.fd1u);
|
||||
gtp_free(hnb->gtp.gsn);
|
||||
hnb->gtp.gsn = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
int hnb_ue_gtp_unbind(struct hnb_ue *ue)
|
||||
{
|
||||
if (!ue->conn_ps.pdp_lib)
|
||||
return -EINVAL;
|
||||
|
||||
pdp_freepdp(ue->conn_ps.pdp_lib);
|
||||
ue->conn_ps.pdp_lib = NULL;
|
||||
return 0;
|
||||
}
|
|
@ -34,6 +34,7 @@
|
|||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/rtp.h>
|
||||
#include <osmocom/hnodeb/gtp.h>
|
||||
|
||||
|
||||
struct hnb *hnb_alloc(void *tall_ctx)
|
||||
|
@ -59,6 +60,9 @@ struct hnb *hnb_alloc(void *tall_ctx)
|
|||
hnb->rtp.ip_dscp = -1;
|
||||
hnb->rtp.priority = -1;
|
||||
|
||||
hnb->gtp.cfg_local_addr = talloc_strdup(hnb, "0.0.0.0");
|
||||
hnb->gtp.fd1u.fd = -1;
|
||||
|
||||
hnb->shutdown_fi = osmo_fsm_inst_alloc(&hnb_shutdown_fsm, hnb, hnb,
|
||||
LOGL_INFO, NULL);
|
||||
|
||||
|
@ -86,6 +90,11 @@ void hnb_free(struct hnb *hnb)
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -116,6 +125,7 @@ void hnb_ue_free(struct hnb_ue *ue)
|
|||
void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps)
|
||||
{
|
||||
if (is_ps) {
|
||||
hnb_ue_gtp_unbind(ue);
|
||||
ue->conn_ps = (struct hnb_ue_ps_ctx){0};
|
||||
} else {
|
||||
hnb_ue_voicecall_release(ue);
|
||||
|
@ -134,6 +144,22 @@ struct hnb_ue *hnb_find_ue_by_id(const struct hnb *hnb, uint32_t conn_id)
|
|||
}
|
||||
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;
|
||||
|
|
|
@ -129,7 +129,8 @@ bool hnb_llsk_can_be_configured(struct hnb *hnb)
|
|||
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 @@ static int llsk_rx_cb(struct osmo_prim_srv *srv, struct osmo_prim_hdr *oph)
|
|||
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:
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/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) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: PS chan not active! rem_addr=%s\n",
|
||||
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) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: Failed to set up gtp socket rem_tei=%u rem_addr=%s\n",
|
||||
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) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: Failed to provide proper local address rem_addr=%s\n",
|
||||
rem_addrstr);
|
||||
rc = _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4);
|
||||
goto release_sock;
|
||||
}
|
||||
|
||||
/* Submit successful confirmation */
|
||||
LOGUE(ue, DLLSK, LOGL_INFO, "Tx GTP-CONN_ESTABLISH.cnf: error_code=0 rem_addr=%s rem_tei=%u loc_addr=%s local_tei=%u\n",
|
||||
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) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Failed sending GTP-CONN_ESTABLISH.cnf error_code=0\n");
|
||||
goto release_sock;
|
||||
}
|
||||
|
||||
ue->conn_ps.local_tei = loc_tei;
|
||||
ue->conn_ps.remote_tei = ce_req->remote_tei;
|
||||
|
||||
return rc;
|
||||
release_sock:
|
||||
hnb_ue_gtp_unbind(ue);
|
||||
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;
|
||||
}
|
||||
/* release GTP pdp ctx: */
|
||||
hnb_ue_gtp_unbind(ue);
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 @@ int main(int argc, char **argv)
|
|||
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 @@ int main(int argc, char **argv)
|
|||
osmo_select_main_ctx(0);
|
||||
}
|
||||
|
||||
hnb_gtp_unbind(g_hnb);
|
||||
|
||||
log_fini();
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,6 +51,10 @@ int hnb_vty_go_parent(struct vty *vty)
|
|||
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 @@ DEFUN(cfg_hnodeb_llsk_path, cfg_hnodeb_llsk_path_cmd,
|
|||
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 @@ static int config_write_hnodeb(struct vty *vty)
|
|||
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 @@ void hnb_vty_init(void)
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue