Initial implementation of LowerLayer UD Socket
The LLSK socket is an interface allowing thid-parties to implement and hook their RLC/MAC/RRC stack to osmo-hnodbe, which takes care of interconnection with the rest of the RAN and core network (Iuh, RTP, GTP-U, etc.). Related: SYS#5516 Change-Id: Icaabb2206d6f141d4fba47dedf71f8ec37e6257d
This commit is contained in:
parent
d8da64bb46
commit
cd1baf0649
|
@ -0,0 +1,86 @@
|
|||
msc {
|
||||
hscale="3";
|
||||
ue [label="Customer"], trx [label="Lower Layer TRX"], hnodeb [label="osmo-hnodeb"], hnbgw [label="HNBGW"], ggsn [label="GGSN"], mgw [label="MGW"];
|
||||
|
||||
|||;
|
||||
--- [ label = "hNodeB starts up" ];
|
||||
hnodeb => hnbgw [label="HNBAP HnbRegisterRequest"];
|
||||
hnodeb <= hnbgw [label="HNBAP HnbRegisterResponse"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "TRX starts up & connects to hNodeB" ];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=CTL, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=CTL, VERSION)"];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=IUH, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=IUH, VERSION)"];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=GTP, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=GTP, VERSION)"];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=AUDIO, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=AUDIO, VERSION)"];
|
||||
trx <= hnodeb [label="IUH-CONFIGURE.ind(PLMN,LAC,SAC,RAC,CI,RNC_ID from HnbRegisterResponse)"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "Subscriber Paging" ];
|
||||
hnodeb <= hnbgw [label="RUA-InitiatingMessage(RANAP Paging)"];
|
||||
trx <= hnodeb [label="IUH-UNITDATA.ind[RANAP Paging]"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "Subscriber Sign Up" ];
|
||||
ue => trx [label="CM Service Req"];
|
||||
trx => hnodeb [label="IUH-CONN_ESTABLISH.req[RANAP CM ServiceReq]"];
|
||||
hnodeb => hnbgw [label="HNBAP UE Register Req(IMSI?)"];
|
||||
hnodeb <= hnbgw [label="HNBAP UE Register Acc(context_id)"];
|
||||
hnodeb => hnbgw [label="RUA-Connect(RANAP-GMM ServiceRequest)"];
|
||||
hnodeb <= hnbgw [label="RUA-DirectTransfer(RANAP GMM ServiceAccept)"];
|
||||
trx <= hnodeb [label="IUH-CONN_ESTABLISH.cnf(context_id])"];
|
||||
trx <= hnodeb [label="IUH-CONN_DATA.ind[context_id, RANAP GMM ServiceAccept]"];
|
||||
--- [ label = "Subscriber set up PS data:" ];
|
||||
hnodeb <= hnbgw [label="RANAP RAB-Assignment Request(TEI, ADDR)"];
|
||||
trx <= hnodeb [label="IUH-CONN_DATA.ind[RANAP RAB-Assignment Request(remote_ip, remote_port, remote_tei)]"];
|
||||
trx => hnodeb [label="GTP-CONN_ESTABLISH.req(remote_ip,remote_port,remote_tei)"];
|
||||
... [ label = "HnodeB sets up GTP-U connection" ];
|
||||
trx <= hnodeb [label="GTP-CONN_ESTABLISH.cnf(local_ip,local_port,local_tei,remote_tei)"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "PS data transmission over GTP-U:" ];
|
||||
ue => trx [label="..."];
|
||||
trx => hnodeb [label="GTP-CONN_DATA.req[remote_tei,payload]"];
|
||||
hnodeb => ggsn [label="GTP-U(remote_tei, local_addr, remote_addr, payload)"];
|
||||
hnodeb <= ggsn [label="GTP-U(local_tei, remote_addr, local_addr, payload)"];
|
||||
trx <= hnodeb [label="GTP-CONN_DATA.ind[local_tei,payload]"];
|
||||
ue <= trx [label="..."];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
--- [ label = "MO/MT PS data Release:" ];
|
||||
ue => trx [label="..."];
|
||||
trx => hnodeb [label="IUH-CONN_DATA.req[RANAP IU Release Request]"];
|
||||
hnodeb => hnbgw [label="RANAP IU Release Request"];
|
||||
hnodeb <= hnbgw [label="RANAP IU Release Command"];
|
||||
trx <= hnodeb [label="IUH-CONN_DATA.ind[RANAP IU Release Command]"];
|
||||
...;
|
||||
trx => hnodeb [label="GTP-CONN_RELEASE.req(remote_tei)"];
|
||||
|
||||
trx => hnodeb [label="IUH-CONN_RELEASE.req[RANAP IU Release Complete]"];
|
||||
hnodeb => hnbgw [label="RUA-Disconnect(RANAP IU Release Complete)"];
|
||||
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
--- [ label = "For voice call (CS): Similar to SAPI_GTP, but using SAPI_AUDIO and osmo-hnodeb sets up RTP stream" ];
|
||||
|
||||
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
--- [ label = "Iuh SCTP link goes down" ];
|
||||
trx <= hnodeb [label="UD socket is closed, osmo-hnodeb cleans up state and attempts reconnect. lowerlayer can re-connect and wait for next CONFIGURE.req"];
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
noinst_HEADERS = \
|
||||
hnb_shutdown_fsm.h \
|
||||
hnb_prim.h \
|
||||
hnbap.h \
|
||||
hnodeb.h \
|
||||
iuh.h \
|
||||
llsk.h \
|
||||
ranap.h \
|
||||
rua.h \
|
||||
vty.h \
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/* (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/>.
|
||||
*
|
||||
*/
|
||||
/* This header includes information relative to protocol and message structure
|
||||
* spoken between osmo-hnodeb, facing the HNBGW and other RAN/CN nodes, and a
|
||||
* Lower Layer program (aka the TRX), implementing the RLC/MAC/RRC towards the
|
||||
* UE in the Uu interface. This protocol is usually referenced as HNBLLIF. The
|
||||
* protocol is primitive based and follows the concepts described in ITU-T
|
||||
* X.210, with osmo-hnodeb taking the "service provider" role and the TRX taking
|
||||
* the "user" role in this case.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/prim.h>
|
||||
|
||||
#define HNB_PRIM_API_VERSION 0
|
||||
#define HNB_PRIM_UD_SOCK_DEFAULT "/tmp/hnb_prim_sock"
|
||||
|
||||
#define HNB_PRIM_SAPI_IUH 1
|
||||
#define HNB_PRIM_SAPI_GTP 2
|
||||
#define HNB_PRIM_SAPI_AUDIO 3
|
||||
|
||||
/*! \brief HNB_IUH primitives */
|
||||
enum hnb_iuh_prim_type {
|
||||
HNB_IUH_PRIM_CONFIGURE,
|
||||
HNB_IUH_PRIM_CONN_ESTABLISH,
|
||||
HNB_IUH_PRIM_CONN_RELEASE,
|
||||
HNB_IUH_PRIM_CONN_DATA,
|
||||
HNB_IUH_PRIM_UNITDATA,
|
||||
_HNB_IUH_PRIM_MAX
|
||||
};
|
||||
|
||||
/* HNB_IUH_PRIM_CONFIGURE.ind, DL */
|
||||
struct hnb_iuh_configure_ind_param {
|
||||
uint16_t mcc;
|
||||
uint16_t mnc;
|
||||
uint16_t cell_identity;
|
||||
uint16_t lac;
|
||||
uint8_t rac;
|
||||
uint8_t reserved;
|
||||
uint16_t sac;
|
||||
uint16_t rnc_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_HNB_IUH_PRIM_CONN_ESTABLISH.ind, DL */
|
||||
struct hnb_iuh_conn_establish_ind_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t cause;
|
||||
uint8_t csg_membership_status;
|
||||
uint8_t spare1;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_HNB_IUH_PRIM_CONN_ESTABLISH.req, UL */
|
||||
struct hnb_iuh_conn_establish_req_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t est_cause;
|
||||
/* TODO: Check if we can copy it as an encoded buffer RRC <-> RUA
|
||||
* RRC: 3GPP TS 25.331 10.3.1.6 Intra Domain NAS Node Selector
|
||||
* RUA: 3GPP TS 25.468 9.2.4 */
|
||||
uint16_t reserved; //uint16_t nas_node_selector_bitlen;
|
||||
//uint8_t nas_node_selector[128]; /* TODO: check whether we can decrease this buffer size */
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_HNB_IUH_PRIM_CONN_ESTABLISH.cnf, DL */
|
||||
struct hnb_iuh_conn_establish_cnf_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t cause; /* 0 = success, !0 = failure */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_RELEASE.req, DL */
|
||||
struct hnb_iuh_conn_release_req_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint8_t cause_type; /* 3GPP TS 25.468 9.2.7 Cause */
|
||||
uint8_t cause; /* 3GPP TS 25.468 9.2.7 Cause */
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_RELEASE.ind, UL */
|
||||
struct hnb_iuh_conn_release_ind_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint8_t cause_type;
|
||||
uint8_t cause;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_DATA.req, DL */
|
||||
struct hnb_iuh_conn_data_req_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint16_t spare2;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_DATA.ind, UL */
|
||||
struct hnb_iuh_conn_data_ind_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint16_t spare2;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_UNITDATA.req, UL */
|
||||
struct hnb_iuh_unitdata_req_param {
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_UNITDATA.ind, DL */
|
||||
struct hnb_iuh_unitdata_ind_param {
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct hnb_iuh_prim {
|
||||
struct osmo_prim_hdr hdr;
|
||||
union {
|
||||
struct hnb_iuh_configure_ind_param configure_ind;
|
||||
struct hnb_iuh_conn_establish_req_param conn_establish_req;
|
||||
struct hnb_iuh_conn_establish_ind_param conn_establish_ind;
|
||||
struct hnb_iuh_conn_establish_cnf_param conn_establish_cnf;
|
||||
struct hnb_iuh_conn_release_req_param conn_release_req;
|
||||
struct hnb_iuh_conn_release_ind_param conn_release_ind;
|
||||
struct hnb_iuh_conn_data_req_param conn_data_req;
|
||||
struct hnb_iuh_conn_data_ind_param conn_data_ind;
|
||||
struct hnb_iuh_unitdata_req_param unitdata_req;
|
||||
struct hnb_iuh_unitdata_ind_param unitdata_ind;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
|
@ -19,29 +19,51 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
|
||||
enum {
|
||||
DMAIN,
|
||||
DHNBAP,
|
||||
DRUA,
|
||||
DRANAP,
|
||||
DSCTP,
|
||||
DLLSK,
|
||||
};
|
||||
extern const struct log_info hnb_log_info;
|
||||
|
||||
struct hnb_chan {
|
||||
int is_ps;
|
||||
struct hnb;
|
||||
|
||||
struct hnb_ue {
|
||||
struct llist_head list; /* Item in struct hnb->ue_list */
|
||||
struct hnb *hnb; /* backpointer */
|
||||
uint32_t conn_id;
|
||||
char *imsi;
|
||||
char imsi[OSMO_IMSI_BUF_SIZE];
|
||||
struct hnb_ue_cs_ctx {
|
||||
bool active; /* Is this chan in use? */
|
||||
bool conn_est_cnf_pending; /* Did we send CONN_ESTABLISH_CNF to lower layers? */
|
||||
} conn_cs;
|
||||
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? */
|
||||
} 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 */
|
||||
|
@ -58,20 +80,27 @@ struct hnb {
|
|||
struct osmo_stream_cli *client;
|
||||
} iuh;
|
||||
|
||||
/* Lower Layer UD socket */
|
||||
struct osmo_prim_srv_link *llsk_link;
|
||||
struct osmo_prim_srv *llsk;
|
||||
uint8_t llsk_valid_sapi_mask;
|
||||
struct osmo_timer_list llsk_defer_configure_ind_timer;
|
||||
|
||||
uint16_t rnc_id;
|
||||
bool registered; /* Set to true once HnbRegisterAccept was received from Iuh. rnc_id is valid iif registered==true */
|
||||
|
||||
uint32_t ctx_id;
|
||||
|
||||
struct osmo_fsm_inst *shutdown_fi; /* FSM instance to manage shutdown procedure during process exit */
|
||||
bool shutdown_fi_exit_proc; /* exit process when shutdown_fsm is finished? */
|
||||
|
||||
struct {
|
||||
struct hnb_chan *chan;
|
||||
} cs;
|
||||
struct llist_head ue_list; /* list of struct hnb_ue */
|
||||
};
|
||||
|
||||
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_imsi(const struct hnb *hnb, char *imsi);
|
||||
|
||||
extern void *tall_hnb_ctx;
|
||||
extern struct hnb *g_hnb;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/* (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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/netif/prim.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
|
||||
struct hnb;
|
||||
|
||||
int hnb_llsk_alloc(struct hnb *hnb);
|
||||
bool hnb_llsk_connected(const struct hnb *hnb);
|
||||
bool hnb_llsk_can_be_configured(struct hnb *hnb);
|
||||
|
||||
|
||||
extern const struct value_string hnb_iuh_prim_type_names[];
|
||||
int llsk_rx_iuh(struct hnb *hnb, struct osmo_prim_hdr *oph);
|
||||
int llsk_iuh_tx_configure_ind(struct hnb *hnb);
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_establish_cnf(uint32_t context_id, uint8_t domain,
|
||||
uint8_t cause);
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_data_ind(uint32_t context_id,
|
||||
uint8_t domain,
|
||||
const uint8_t *data,
|
||||
uint32_t data_len);
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_unitdata_ind(const uint8_t *data, uint32_t data_len);
|
|
@ -19,4 +19,6 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
struct msgb *hnb_ranap_msgb_alloc(void);
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#include <asn1c/ANY.h>
|
||||
|
||||
struct hnb;
|
||||
struct hnb_ue;
|
||||
struct msgb;
|
||||
|
||||
struct msgb *hnb_rua_msgb_alloc(void);
|
||||
|
||||
int hnb_rua_rx(struct hnb *hnb, struct msgb *msg);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
enum hnb_vty_nodes {
|
||||
HNODEB_NODE = _LAST_OSMOVTY_NODE,
|
||||
IUH_NODE,
|
||||
LLSK_NODE,
|
||||
};
|
||||
|
||||
void hnb_vty_init(void);
|
||||
|
|
|
@ -35,6 +35,8 @@ osmo_hnodeb_SOURCES = \
|
|||
hnb.c \
|
||||
hnb_shutdown_fsm.c \
|
||||
iuh.c \
|
||||
llsk.c \
|
||||
llsk_iuh.c \
|
||||
ranap.c \
|
||||
rua.c \
|
||||
vty.c \
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
static const struct log_info_cat log_cat[] = {
|
||||
[DMAIN] = {
|
||||
.name = "DMAIN", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "",
|
||||
.color = "\033[1;37m",
|
||||
.description = "Main program",
|
||||
},
|
||||
[DHNBAP] = {
|
||||
|
@ -47,6 +47,11 @@ static const struct log_info_cat log_cat[] = {
|
|||
.color = "\033[1;36m",
|
||||
.description = "SCTP connection on the Iuh link",
|
||||
},
|
||||
[DLLSK] = {
|
||||
.name = "DLLSK", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "\033[1;31m",
|
||||
.description = "Lower Layer Unix Domain Socket",
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info hnb_log_info = {
|
||||
|
|
|
@ -23,10 +23,12 @@
|
|||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
#include <osmocom/netif/prim.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
|
||||
|
||||
struct hnb *hnb_alloc(void *tall_ctx)
|
||||
|
@ -37,15 +39,18 @@ struct hnb *hnb_alloc(void *tall_ctx)
|
|||
if (!hnb)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&hnb->ue_list);
|
||||
|
||||
hnb->identity = talloc_strdup(hnb, "OsmoHNodeB");
|
||||
hnb->plmn = (struct osmo_plmn_id){
|
||||
.mcc = 1,
|
||||
.mnc = 1,
|
||||
};
|
||||
|
||||
hnb->shutdown_fi = osmo_fsm_inst_alloc(&hnb_shutdown_fsm, hnb, hnb,
|
||||
LOGL_INFO, NULL);
|
||||
|
||||
hnb_llsk_alloc(hnb);
|
||||
|
||||
hnb_iuh_alloc(hnb);
|
||||
|
||||
return hnb;
|
||||
|
@ -53,10 +58,78 @@ struct hnb *hnb_alloc(void *tall_ctx)
|
|||
|
||||
void hnb_free(struct hnb *hnb)
|
||||
{
|
||||
struct hnb_ue *ue, *ue_tmp;
|
||||
|
||||
llist_for_each_entry_safe(ue, ue_tmp, &hnb->ue_list, list)
|
||||
hnb_ue_free(ue);
|
||||
|
||||
if (hnb->shutdown_fi) {
|
||||
osmo_fsm_inst_free(hnb->shutdown_fi);
|
||||
hnb->shutdown_fi = NULL;
|
||||
}
|
||||
hnb_iuh_free(hnb);
|
||||
|
||||
osmo_timer_del(&hnb->llsk_defer_configure_ind_timer);
|
||||
osmo_prim_srv_link_free(hnb->llsk_link);
|
||||
hnb->llsk_link = NULL;
|
||||
|
||||
talloc_free(hnb);
|
||||
}
|
||||
|
||||
struct hnb_ue *hnb_ue_alloc(struct hnb *hnb, uint32_t conn_id)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
|
||||
ue = talloc_zero(hnb, struct hnb_ue);
|
||||
if (!ue)
|
||||
return NULL;
|
||||
|
||||
ue->hnb = hnb;
|
||||
ue->conn_id = conn_id;
|
||||
|
||||
llist_add(&ue->list, &hnb->ue_list);
|
||||
|
||||
return ue;
|
||||
}
|
||||
|
||||
void hnb_ue_free(struct hnb_ue *ue)
|
||||
{
|
||||
llist_del(&ue->list);
|
||||
talloc_free(ue);
|
||||
}
|
||||
|
||||
void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps)
|
||||
{
|
||||
if (is_ps)
|
||||
ue->conn_ps = (struct hnb_ue_ps_ctx){0};
|
||||
else
|
||||
ue->conn_cs = (struct hnb_ue_cs_ctx){0};
|
||||
}
|
||||
|
||||
struct hnb_ue *hnb_find_ue_by_id(const struct hnb *hnb, uint32_t conn_id)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
|
||||
llist_for_each_entry(ue, &hnb->ue_list, list) {
|
||||
if (ue->conn_id != conn_id)
|
||||
continue;
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
struct hnb_ue *hnb_find_ue_by_imsi(const struct hnb *hnb, char *imsi)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
|
||||
if (!imsi || imsi[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(ue, &hnb->ue_list, list) {
|
||||
if (ue->imsi[0] == '\0')
|
||||
continue;
|
||||
if (strncmp(&ue->imsi[0], imsi, ARRAY_SIZE(ue->imsi)) != 0)
|
||||
continue;
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@
|
|||
static void st_none_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)fi->priv;
|
||||
|
||||
/* Reset state: */
|
||||
hnb->registered = false;
|
||||
hnb->rnc_id = 0;
|
||||
|
||||
hnb_iuh_connect(hnb); /* Start reconnect once we are done with shutdown and we didn't exit process */
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,9 @@
|
|||
#include <osmocom/hnodeb/hnbap.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
|
||||
static int hnb_rx_hnb_register_acc(struct hnb *hnb, ANY_t *in)
|
||||
{
|
||||
|
@ -42,13 +44,19 @@ static int hnb_rx_hnb_register_acc(struct hnb *hnb, ANY_t *in)
|
|||
|
||||
rc = hnbap_decode_hnbregisteraccepties(&accept, in);
|
||||
if (rc < 0) {
|
||||
hnb_shutdown(hnb, "Failed decoding HnbRegisterAccept IEs", false);
|
||||
return rc;
|
||||
}
|
||||
|
||||
hnb->rnc_id = accept.rnc_id;
|
||||
hnb->registered = true;
|
||||
LOGP(DHNBAP, LOGL_INFO, "Rx HNB Register accept with RNC ID %u\n", hnb->rnc_id);
|
||||
|
||||
hnbap_free_hnbregisteraccepties(&accept);
|
||||
return 0;
|
||||
|
||||
if (hnb_llsk_can_be_configured(hnb))
|
||||
llsk_iuh_tx_configure_ind(hnb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_rx_hnb_register_rej(struct hnb *hnb, ANY_t *in)
|
||||
|
@ -56,6 +64,8 @@ static int hnb_rx_hnb_register_rej(struct hnb *hnb, ANY_t *in)
|
|||
int rc;
|
||||
HNBAP_HNBRegisterRejectIEs_t reject;
|
||||
|
||||
hnb->registered = false;
|
||||
|
||||
rc = hnbap_decode_hnbregisterrejecties(&reject, in);
|
||||
if (rc < 0) {
|
||||
LOGP(DHNBAP, LOGL_NOTICE, "Rx HNB Register Reject: parse failure\n");
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/* (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 <osmocom/core/prim.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
|
||||
static int llsk_opened_cb(struct osmo_prim_srv *srv)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(srv);
|
||||
|
||||
if (hnb->llsk) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "New connection opened while one is already active, dropping it\n");
|
||||
osmo_prim_srv_close(srv);
|
||||
return 0;
|
||||
}
|
||||
LOGP(DLLSK, LOGL_NOTICE, "LLSK conn is UP\n");
|
||||
hnb->llsk = srv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int llsk_closed_cb(struct osmo_prim_srv *srv)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(srv);
|
||||
|
||||
if (!hnb->llsk) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "closed_cb received but we have no active llsk conn!\n");
|
||||
return 0;
|
||||
}
|
||||
/* If a later conn different than active one is dropped (because we closed it): */
|
||||
if (hnb->llsk != srv)
|
||||
return 0;
|
||||
LOGP(DLLSK, LOGL_NOTICE, "LLSK conn is DOWN\n");
|
||||
|
||||
hnb->llsk = NULL;
|
||||
hnb->llsk_valid_sapi_mask = 0x0;
|
||||
osmo_timer_del(&hnb->llsk_defer_configure_ind_timer);
|
||||
hnb_shutdown(hnb, "LLSK conn dropped", false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool hnb_llsk_connected(const struct hnb *hnb)
|
||||
{
|
||||
return !!hnb->llsk;
|
||||
}
|
||||
|
||||
bool hnb_llsk_can_be_configured(struct hnb *hnb)
|
||||
{
|
||||
if (!hnb->registered)
|
||||
return false;
|
||||
if (!hnb->llsk)
|
||||
return false;
|
||||
|
||||
if (hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_IUH))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void llsk_defer_configure_ind_timer_cb(void *data)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)data;
|
||||
llsk_iuh_tx_configure_ind(hnb);
|
||||
}
|
||||
|
||||
static int llsk_rx_sapi_version_cb(struct osmo_prim_srv *prim_srv, uint32_t sapi, uint16_t rem_version)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(prim_srv);
|
||||
if (sapi > sizeof(hnb->llsk_valid_sapi_mask)*8 - 1)
|
||||
return -1;
|
||||
hnb->llsk_valid_sapi_mask |= (1 << sapi);
|
||||
|
||||
/* Defer CONFIGURE.req after we have confirmed the versions */
|
||||
if (hnb_llsk_can_be_configured(hnb))
|
||||
osmo_timer_schedule(&hnb->llsk_defer_configure_ind_timer, 0, 0);
|
||||
|
||||
return rem_version;
|
||||
}
|
||||
|
||||
static int llsk_rx_cb(struct osmo_prim_srv *srv, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(srv);
|
||||
LOGP(DLLSK, LOGL_DEBUG, "llsk_rx_cb() SAP=%u (%u bytes): %s\n", oph->sap,
|
||||
msgb_length(oph->msg), osmo_hexdump(msgb_data(oph->msg), msgb_length(oph->msg)));
|
||||
|
||||
switch (oph->sap) {
|
||||
case HNB_PRIM_SAPI_IUH:
|
||||
return llsk_rx_iuh(hnb, oph);
|
||||
case HNB_PRIM_SAPI_GTP:
|
||||
case HNB_PRIM_SAPI_AUDIO:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx SAPI %u not yet implemented (len=%u)\n",
|
||||
oph->sap, msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx for unknwon SAPI %u (len=%u)\n",
|
||||
oph->sap, msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int hnb_llsk_alloc(struct hnb *hnb)
|
||||
{
|
||||
hnb->llsk_link = osmo_prim_srv_link_alloc(hnb);
|
||||
osmo_prim_srv_link_set_priv(hnb->llsk_link, hnb);
|
||||
osmo_prim_srv_link_set_log_category(hnb->llsk_link, DLLSK);
|
||||
osmo_prim_srv_link_set_addr(hnb->llsk_link, HNB_PRIM_UD_SOCK_DEFAULT);
|
||||
osmo_prim_srv_link_set_opened_conn_cb(hnb->llsk_link, llsk_opened_cb);
|
||||
osmo_prim_srv_link_set_closed_conn_cb(hnb->llsk_link, llsk_closed_cb);
|
||||
osmo_prim_srv_link_set_rx_sapi_version_cb(hnb->llsk_link, llsk_rx_sapi_version_cb);
|
||||
osmo_prim_srv_link_set_rx_cb(hnb->llsk_link, llsk_rx_cb);
|
||||
osmo_timer_setup(&hnb->llsk_defer_configure_ind_timer, llsk_defer_configure_ind_timer_cb, hnb);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
/* (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 <assert.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/rua/rua_msg_factory.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/ranap.h>
|
||||
|
||||
static size_t llsk_iuh_prim_size_tbl[4][_HNB_IUH_PRIM_MAX] = {
|
||||
[PRIM_OP_REQUEST] = {
|
||||
[HNB_IUH_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_iuh_conn_establish_req_param),
|
||||
[HNB_IUH_PRIM_CONN_RELEASE] = sizeof(struct hnb_iuh_conn_release_req_param),
|
||||
[HNB_IUH_PRIM_CONN_DATA] = sizeof(struct hnb_iuh_conn_data_req_param),
|
||||
[HNB_IUH_PRIM_UNITDATA] = sizeof(struct hnb_iuh_unitdata_req_param),
|
||||
},
|
||||
[PRIM_OP_RESPONSE] = {},
|
||||
[PRIM_OP_INDICATION] = {
|
||||
[HNB_IUH_PRIM_CONFIGURE] = sizeof(struct hnb_iuh_configure_ind_param),
|
||||
[HNB_IUH_PRIM_CONN_DATA] = sizeof(struct hnb_iuh_conn_data_ind_param),
|
||||
[HNB_IUH_PRIM_UNITDATA] = sizeof(struct hnb_iuh_unitdata_ind_param),
|
||||
},
|
||||
[PRIM_OP_CONFIRM] = {
|
||||
[HNB_IUH_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_iuh_conn_establish_cnf_param),
|
||||
},
|
||||
};
|
||||
static inline size_t llsk_iuh_prim_size(enum hnb_iuh_prim_type ptype, enum osmo_prim_operation op)
|
||||
{
|
||||
size_t val = llsk_iuh_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_iuh_prim_type_names[] = {
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONFIGURE),
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONN_ESTABLISH),
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONN_RELEASE),
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONN_DATA),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_prim_alloc(enum hnb_iuh_prim_type ptype, enum osmo_prim_operation op, size_t extra_len)
|
||||
{
|
||||
struct osmo_prim_hdr *oph;
|
||||
size_t len = llsk_iuh_prim_size(ptype, op);
|
||||
|
||||
oph = osmo_prim_msgb_alloc(HNB_PRIM_SAPI_IUH, ptype, op, sizeof(*oph) + len + extra_len);
|
||||
if (!oph)
|
||||
return NULL;
|
||||
msgb_put(oph->msg, len);
|
||||
|
||||
return (struct hnb_iuh_prim *)oph;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_configure_ind(uint16_t mcc, uint16_t mnc,
|
||||
uint16_t cell_identity,
|
||||
uint16_t lac, uint8_t rac,
|
||||
uint16_t sac, uint16_t rnc_id)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_CONFIGURE, PRIM_OP_INDICATION, 0);
|
||||
iuh_prim->u.configure_ind.mcc = mcc;
|
||||
iuh_prim->u.configure_ind.mnc = mnc;
|
||||
iuh_prim->u.configure_ind.cell_identity = cell_identity;
|
||||
iuh_prim->u.configure_ind.lac = lac;
|
||||
iuh_prim->u.configure_ind.rac = rac;
|
||||
iuh_prim->u.configure_ind.sac = sac;
|
||||
iuh_prim->u.configure_ind.rnc_id = rnc_id;
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
int llsk_iuh_tx_configure_ind(struct hnb *hnb)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
int rc;
|
||||
|
||||
LOGP(DLLSK, LOGL_INFO, "Tx IUH-CONFIGURE.ind\n");
|
||||
/* We are already registered, so configure the lower layers right now */
|
||||
iuh_prim = hnb_iuh_makeprim_configure_ind(hnb->plmn.mcc, hnb->plmn.mnc,
|
||||
hnb->cell_identity, hnb->lac,
|
||||
hnb->rac, hnb->sac, hnb->rnc_id);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk, iuh_prim->hdr.msg)) < 0)
|
||||
LOGP(DLLSK, LOGL_ERROR, "Failed sending IUH-CONFIGURE.ind\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_establish_cnf(uint32_t context_id, uint8_t domain,
|
||||
uint8_t cause)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_CONN_ESTABLISH, PRIM_OP_CONFIRM, 0);
|
||||
iuh_prim->u.conn_establish_cnf.context_id = context_id;
|
||||
iuh_prim->u.conn_establish_cnf.domain = domain;
|
||||
iuh_prim->u.conn_establish_cnf.cause = cause;
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_data_ind(uint32_t context_id,
|
||||
uint8_t domain,
|
||||
const uint8_t *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_CONN_DATA, PRIM_OP_INDICATION, data_len);
|
||||
iuh_prim->u.conn_data_ind.context_id = context_id;
|
||||
iuh_prim->u.conn_data_ind.domain = domain;
|
||||
iuh_prim->u.conn_data_ind.data_len = data_len;
|
||||
if (data_len) {
|
||||
msgb_put(iuh_prim->hdr.msg, data_len);
|
||||
memcpy(iuh_prim->u.conn_data_ind.data, data, data_len);
|
||||
}
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_unitdata_ind(const uint8_t *data, uint32_t data_len)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_UNITDATA, PRIM_OP_INDICATION, data_len);
|
||||
iuh_prim->u.unitdata_ind.data_len = data_len;
|
||||
if (data_len) {
|
||||
msgb_put(iuh_prim->hdr.msg, data_len);
|
||||
memcpy(iuh_prim->u.unitdata_ind.data, data, data_len);
|
||||
}
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_conn_establish_req(struct hnb *hnb, struct hnb_iuh_conn_establish_req_param *ce_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_INFO, "Rx IUH-CONN_ESTABLISH.req ctx=%u is_ps=%u est_cause=%u data_len=%u\n",
|
||||
ce_req->context_id, ce_req->domain, ce_req->est_cause, ce_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Ignoring Rx IUH-CONN_ESTABLISH.req: HNB not registered to HNBGW!\n");
|
||||
/* TODO: NACK it to lower layers */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, ce_req->context_id);
|
||||
if (!ue) {
|
||||
ue = hnb_ue_alloc(hnb, ce_req->context_id);
|
||||
if (ce_req->domain) {
|
||||
ue->conn_ps.active = true;
|
||||
ue->conn_ps.conn_est_cnf_pending = true; /* Set to false once we receive first DT from HNBGW and send CONN_EST.cnf */
|
||||
|
||||
} else {
|
||||
ue->conn_cs.active = true;
|
||||
ue->conn_cs.conn_est_cnf_pending = true; /* Set to false once we receive first DT from HNBGW and send CONN_EST.cnf */
|
||||
}
|
||||
}
|
||||
if (ce_req->data_len) {
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg = hnb_ranap_msgb_alloc();
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA CONNECT ctx=%u is_ps=%u data_len=%u\n",
|
||||
ce_req->context_id, ce_req->domain, ce_req->data_len);
|
||||
memcpy(msgb_put(ranap_msg, ce_req->data_len), ce_req->data, ce_req->data_len);
|
||||
rua_msg = rua_new_conn(ce_req->domain, ce_req->context_id, ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_conn_release_req(struct hnb *hnb, struct hnb_iuh_conn_release_req_param *rel_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx IUH-CONN_RELEASE.req ctx=%u is_ps=%u data_len=%u\n",
|
||||
rel_req->context_id, rel_req->domain, rel_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Rx IUH-CONN_RELEASE.req: Ignoring, HNB not registered to HNBGW!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, rel_req->context_id);
|
||||
if (!ue) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx IUH-CONN_RELEASE.req: conn unknown! ctx=%u is_ps=%u data_len=%u\n",
|
||||
rel_req->context_id, rel_req->domain, rel_req->data_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
hnb_ue_reset_chan(ue, !!rel_req->domain);
|
||||
if (!ue->conn_cs.active && !ue->conn_ps.active) {
|
||||
hnb_ue_free(ue);
|
||||
ue = NULL;
|
||||
}
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA DISC ctx=%u is_ps=%u data_len=%u\n",
|
||||
rel_req->context_id, rel_req->domain, rel_req->data_len);
|
||||
ranap_msg = hnb_ranap_msgb_alloc();
|
||||
if (rel_req->data_len)
|
||||
memcpy(msgb_put(ranap_msg, rel_req->data_len), rel_req->data, rel_req->data_len);
|
||||
|
||||
rua_msg = rua_new_disc(rel_req->domain, rel_req->context_id, ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_conn_data_req(struct hnb *hnb, struct hnb_iuh_conn_data_req_param *data_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx IUH-CONN_DATA.req ctx=%u is_ps=%u data_len=%u\n",
|
||||
data_req->context_id, data_req->domain, data_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Rx IUH-CONN_DATA.req: Ignoring, HNB not registered to HNBGW!\n");
|
||||
/* TODO: NACK it to lower layers */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, data_req->context_id);
|
||||
if (!ue) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx IUH-CONN_DATA.req: conn unknown! ctx=%u is_ps=%u data_len=%u\n",
|
||||
data_req->context_id, data_req->domain, data_req->data_len);
|
||||
/* TODO: NACK it to lower layers */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA DT ctx=%u is_ps=%u data_len=%u\n",
|
||||
data_req->context_id, data_req->domain, data_req->data_len);
|
||||
ranap_msg = hnb_ranap_msgb_alloc();
|
||||
if (data_req->data_len)
|
||||
memcpy(msgb_put(ranap_msg, data_req->data_len), data_req->data, data_req->data_len);
|
||||
|
||||
rua_msg = rua_new_dt(data_req->domain, data_req->context_id, ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_unitdata_req(struct hnb *hnb, struct hnb_iuh_unitdata_req_param *ud_req)
|
||||
{
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx IUH-UNITDATA.req data_len=%u\n", ud_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Rx IUH-UNITDATA.req: Ignoring, HNB not registered to HNBGW!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA UDT data_len=%u\n", ud_req->data_len);
|
||||
|
||||
ranap_msg = hnb_ranap_msgb_alloc();
|
||||
if (ud_req->data_len)
|
||||
memcpy(msgb_put(ranap_msg, ud_req->data_len), ud_req->data, ud_req->data_len);
|
||||
|
||||
rua_msg = rua_new_udt(ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int llsk_rx_iuh(struct hnb *hnb, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
size_t prim_size = llsk_iuh_prim_size(oph->primitive, oph->operation);
|
||||
|
||||
if (msgb_length(oph->msg) < prim_size) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx IUH-%s.%s with length %u < %zu\n",
|
||||
get_value_string(hnb_iuh_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_IUH_PRIM_CONN_ESTABLISH:
|
||||
return llsk_rx_iuh_conn_establish_req(hnb, (struct hnb_iuh_conn_establish_req_param *)msgb_data(oph->msg));
|
||||
case HNB_IUH_PRIM_CONN_RELEASE:
|
||||
return llsk_rx_iuh_conn_release_req(hnb, (struct hnb_iuh_conn_release_req_param *)msgb_data(oph->msg));
|
||||
case HNB_IUH_PRIM_CONN_DATA:
|
||||
return llsk_rx_iuh_conn_data_req(hnb, (struct hnb_iuh_conn_data_req_param *)msgb_data(oph->msg));
|
||||
case HNB_IUH_PRIM_UNITDATA:
|
||||
return llsk_rx_iuh_unitdata_req(hnb, (struct hnb_iuh_unitdata_req_param *)msgb_data(oph->msg));
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx llsk-iuh 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-iuh unexpected primitive operation %s::%s (len=%u)\n",
|
||||
get_value_string(hnb_iuh_prim_type_names, oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation),
|
||||
msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
|
@ -265,6 +265,13 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
/* Start listening on lower layer unix domain socket: */
|
||||
rc = osmo_prim_srv_link_open(g_hnb->llsk_link);
|
||||
if (rc < 0) {
|
||||
perror("Error opening lower layer socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = hnb_iuh_connect(g_hnb);
|
||||
if (rc < 0) {
|
||||
perror("Error connecting to Iuh port");
|
||||
|
|
|
@ -27,3 +27,8 @@
|
|||
|
||||
#include <osmocom/hnodeb/rua.h>
|
||||
#include <osmocom/hnodeb/ranap.h>
|
||||
|
||||
struct msgb *hnb_ranap_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc(1000, "ranap_tx");
|
||||
}
|
||||
|
|
|
@ -32,10 +32,23 @@
|
|||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
|
||||
|
||||
struct msgb *hnb_rua_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc(1000, "rua_tx");
|
||||
}
|
||||
|
||||
static void hnb_rua_dt_handle(struct hnb *hnb, ANY_t *in)
|
||||
{
|
||||
RUA_DirectTransferIEs_t ies;
|
||||
int rc;
|
||||
struct hnb_ue *ue;
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
uint32_t context_id;
|
||||
bool is_ps;
|
||||
uint8_t *ranap_buf;
|
||||
size_t ranap_buf_len;
|
||||
bool *conn_est_cnf_pending;
|
||||
|
||||
rc = rua_decode_directtransferies(&ies, in);
|
||||
if (rc < 0) {
|
||||
|
@ -43,7 +56,44 @@ static void hnb_rua_dt_handle(struct hnb *hnb, ANY_t *in)
|
|||
return;
|
||||
}
|
||||
|
||||
context_id = asn1bitstr_to_u24(&ies.context_ID);
|
||||
is_ps = (ies.cN_DomainIndicator == RUA_CN_DomainIndicator_ps_domain);
|
||||
ranap_buf = ies.ranaP_Message.buf;
|
||||
ranap_buf_len = ies.ranaP_Message.size;
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Rx RUA DT context_id=%u is_ps=%u ranap_len=%zu\n",
|
||||
context_id, is_ps, ranap_buf_len);
|
||||
|
||||
if (!(ue = hnb_find_ue_by_id(hnb, context_id))) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Rx RUA DT: Failed finding ue context_id=%u is_ps=%u\n",
|
||||
context_id, is_ps);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
conn_est_cnf_pending = is_ps ? &ue->conn_ps.conn_est_cnf_pending :
|
||||
&ue->conn_cs.conn_est_cnf_pending;
|
||||
if (*conn_est_cnf_pending) {
|
||||
*conn_est_cnf_pending = false;
|
||||
LOGP(DLLSK, LOGL_INFO, "Tx IUH-CONN_ESTABLISH.cnf context_id=%u is_ps=%u\n",
|
||||
context_id, is_ps);
|
||||
iuh_prim = hnb_iuh_makeprim_conn_establish_cnf(context_id, is_ps, 0);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk, iuh_prim->hdr.msg)) < 0) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Failed sending IUH-CONN_ESTABLISH.cnf context_id=%u is_ps=%u\n",
|
||||
context_id, is_ps);
|
||||
goto free_ret;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Tx IUH-CONN_DATA.ind context_id=%u is_ps=%u ranap_len=%zu\n",
|
||||
context_id, is_ps, ranap_buf_len);
|
||||
iuh_prim = hnb_iuh_makeprim_conn_data_ind(context_id, is_ps, ranap_buf, ranap_buf_len);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk, iuh_prim->hdr.msg)) < 0) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Failed sending IUH-CONN_DATA.ind context_id=%u is_ps=%u ranap_len=%zu\n",
|
||||
context_id, is_ps, ranap_buf_len);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
free_ret:
|
||||
/* FIXME: what to do with the asn1c-allocated memory */
|
||||
rua_free_directtransferies(&ies);
|
||||
}
|
||||
|
@ -52,18 +102,81 @@ static void hnb_rua_cl_handle(struct hnb *hnb, ANY_t *in)
|
|||
{
|
||||
RUA_ConnectionlessTransferIEs_t ies;
|
||||
int rc;
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
uint8_t *ranap_buf;
|
||||
size_t ranap_buf_len;
|
||||
|
||||
rc = rua_decode_connectionlesstransferies(&ies, in);
|
||||
if (rc < 0) {
|
||||
LOGP(DRUA, LOGL_INFO, "failed to decode RUA CL IEs\n");
|
||||
return;
|
||||
}
|
||||
ranap_buf = ies.ranaP_Message.buf;
|
||||
ranap_buf_len = ies.ranaP_Message.size;
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Rx RUA UDT ranap_len=%zu\n", ranap_buf_len);
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Tx IUH-UNITDATA.ind ranap_len=%zu\n", ranap_buf_len);
|
||||
iuh_prim = hnb_iuh_makeprim_unitdata_ind(ranap_buf, ranap_buf_len);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk, iuh_prim->hdr.msg)) < 0) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Failed sending IUH-CONN_DATA.ind ranap_len=%zu\n",
|
||||
ranap_buf_len);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
free_ret:
|
||||
/* FIXME: what to do with the asn1c-allocated memory */
|
||||
rua_free_connectionlesstransferies(&ies);
|
||||
}
|
||||
|
||||
|
||||
static int hnb_rua_rx_initiating(struct hnb *hnb, RUA_InitiatingMessage_t *init)
|
||||
{
|
||||
switch (init->procedureCode) {
|
||||
case RUA_ProcedureCode_id_ConnectionlessTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Initiating ConnectionlessTransfer\n");
|
||||
hnb_rua_cl_handle(hnb, &init->value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_DirectTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Initiating DirectTransfer\n");
|
||||
hnb_rua_dt_handle(hnb, &init->value);
|
||||
default:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx unknown Initiating message\n");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hnb_rua_rx_successful(struct hnb *hnb, RUA_SuccessfulOutcome_t *success)
|
||||
{
|
||||
switch (success->procedureCode) {
|
||||
case RUA_ProcedureCode_id_ConnectionlessTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome ConnectionlessTransfer\n");
|
||||
hnb_rua_cl_handle(hnb, &success->value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Connect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome Connect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_DirectTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome DirectTransfer\n");
|
||||
hnb_rua_dt_handle(hnb, &success->value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Disconnect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome Disconnect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_ErrorIndication:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome ErrorIndication\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_privateMessage:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome privateMessage\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx unknown SuccessfulOutcome message\n");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hnb_rua_rx(struct hnb *hnb, struct msgb *msg)
|
||||
{
|
||||
RUA_RUA_PDU_t _pdu, *pdu = &_pdu;
|
||||
|
@ -79,11 +192,9 @@ int hnb_rua_rx(struct hnb *hnb, struct msgb *msg)
|
|||
|
||||
switch (pdu->present) {
|
||||
case RUA_RUA_PDU_PR_successfulOutcome:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA_RUA_PDU_PR_successfulOutcome\n");
|
||||
break;
|
||||
return hnb_rua_rx_successful(hnb, &pdu->choice.successfulOutcome);
|
||||
case RUA_RUA_PDU_PR_initiatingMessage:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA_RUA_PDU_PR_initiatingMessage\n");
|
||||
break;
|
||||
return hnb_rua_rx_initiating(hnb, &pdu->choice.initiatingMessage);
|
||||
case RUA_RUA_PDU_PR_NOTHING:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA_RUA_PDU_PR_NOTHING\n");
|
||||
break;
|
||||
|
@ -95,31 +206,5 @@ int hnb_rua_rx(struct hnb *hnb, struct msgb *msg)
|
|||
break;
|
||||
}
|
||||
|
||||
switch (pdu->choice.successfulOutcome.procedureCode) {
|
||||
case RUA_ProcedureCode_id_ConnectionlessTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Connectionless Transfer\n");
|
||||
hnb_rua_cl_handle(hnb, &pdu->choice.successfulOutcome.value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Connect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Connect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_DirectTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx DirectTransfer\n");
|
||||
hnb_rua_dt_handle(hnb, &pdu->choice.successfulOutcome.value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Disconnect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Disconnect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_ErrorIndication:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx ErrorIndication\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_privateMessage:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx privateMessage\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx unknown message\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,10 @@ int hnb_vty_go_parent(struct vty *vty)
|
|||
vty->node = HNODEB_NODE;
|
||||
vty->index = g_hnb;
|
||||
break;
|
||||
case LLSK_NODE:
|
||||
vty->node = HNODEB_NODE;
|
||||
vty->index = g_hnb;
|
||||
break;
|
||||
case HNODEB_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = g_hnb;
|
||||
|
@ -247,6 +251,36 @@ DEFUN(cfg_hnodeb_iuh_remote_port, cfg_hnodeb_iuh_remote_port_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_node llsk_node = {
|
||||
LLSK_NODE,
|
||||
"%s(config-ll-socket)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
#define LLSK_STR "Configure the Lower Layer Unix Domain Socket\n"
|
||||
|
||||
DEFUN(cfg_hnodeb_llsk,
|
||||
cfg_hnodeb_llsk_cmd,
|
||||
"ll-socket", LLSK_STR)
|
||||
{
|
||||
OSMO_ASSERT(g_hnb);
|
||||
vty->index = g_hnb;
|
||||
vty->node = LLSK_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_hnodeb_llsk_path, cfg_hnodeb_llsk_path_cmd,
|
||||
"path PATH",
|
||||
"Configure the Lower Layer Unix Domain Socket path\n"
|
||||
"UNIX socket path\n")
|
||||
{
|
||||
osmo_prim_srv_link_set_addr(g_hnb->llsk_link, argv[0]);
|
||||
|
||||
/* FIXME: re-open the interface? */
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int config_write_hnodeb(struct vty *vty)
|
||||
{
|
||||
|
@ -266,10 +300,11 @@ static int config_write_hnodeb(struct vty *vty)
|
|||
vty_out(vty, " local-port %u%s", g_hnb->iuh.local_port, VTY_NEWLINE);
|
||||
vty_out(vty, " remote-ip %s%s", g_hnb->iuh.remote_addr, VTY_NEWLINE);
|
||||
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);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#define RANAP_STR "RANAP related commands\n"
|
||||
#define CSPS_STR "Circuit Switched\n" "Packet Switched\n"
|
||||
|
||||
|
@ -321,6 +356,9 @@ void hnb_vty_init(void)
|
|||
install_element(IUH_NODE, &cfg_hnodeb_iuh_local_port_cmd);
|
||||
install_element(IUH_NODE, &cfg_hnodeb_iuh_remote_ip_cmd);
|
||||
install_element(IUH_NODE, &cfg_hnodeb_iuh_remote_port_cmd);
|
||||
install_element(HNODEB_NODE, &cfg_hnodeb_llsk_cmd);
|
||||
install_node(&llsk_node, NULL);
|
||||
install_element(LLSK_NODE, &cfg_hnodeb_llsk_path_cmd);
|
||||
|
||||
install_element_ve(&asn_dbg_cmd);
|
||||
install_element_ve(&ranap_reset_cmd);
|
||||
|
|
Loading…
Reference in New Issue