diff --git a/doc/hnodeb.msc b/doc/hnodeb.msc new file mode 100644 index 0000000..6ad6e7c --- /dev/null +++ b/doc/hnodeb.msc @@ -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"]; +} diff --git a/include/osmocom/hnodeb/Makefile.am b/include/osmocom/hnodeb/Makefile.am index 6fa587d..62af5bb 100644 --- a/include/osmocom/hnodeb/Makefile.am +++ b/include/osmocom/hnodeb/Makefile.am @@ -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 \ diff --git a/include/osmocom/hnodeb/hnb_prim.h b/include/osmocom/hnodeb/hnb_prim.h new file mode 100644 index 0000000..6d597c4 --- /dev/null +++ b/include/osmocom/hnodeb/hnb_prim.h @@ -0,0 +1,165 @@ +/* (C) 2021 by sysmocom - s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 . + * + */ +/* 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 +#include +#include + +#include + +#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)); diff --git a/include/osmocom/hnodeb/hnodeb.h b/include/osmocom/hnodeb/hnodeb.h index a629b4e..fd7d1e9 100644 --- a/include/osmocom/hnodeb/hnodeb.h +++ b/include/osmocom/hnodeb/hnodeb.h @@ -19,29 +19,51 @@ */ #pragma once +#include +#include + #include #include +#include #include #include #include #include +#include #include +#include + 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; diff --git a/include/osmocom/hnodeb/llsk.h b/include/osmocom/hnodeb/llsk.h new file mode 100644 index 0000000..ab39439 --- /dev/null +++ b/include/osmocom/hnodeb/llsk.h @@ -0,0 +1,44 @@ +/* (C) 2021 by sysmocom - s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 . + * + */ +#pragma once + +#include +#include + +#include +#include +#include + +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); diff --git a/include/osmocom/hnodeb/ranap.h b/include/osmocom/hnodeb/ranap.h index 474de70..6e9c9b7 100644 --- a/include/osmocom/hnodeb/ranap.h +++ b/include/osmocom/hnodeb/ranap.h @@ -19,4 +19,6 @@ */ #pragma once +#include +struct msgb *hnb_ranap_msgb_alloc(void); diff --git a/include/osmocom/hnodeb/rua.h b/include/osmocom/hnodeb/rua.h index ea7e81c..e780207 100644 --- a/include/osmocom/hnodeb/rua.h +++ b/include/osmocom/hnodeb/rua.h @@ -22,6 +22,9 @@ #include struct hnb; +struct hnb_ue; struct msgb; +struct msgb *hnb_rua_msgb_alloc(void); + int hnb_rua_rx(struct hnb *hnb, struct msgb *msg); diff --git a/include/osmocom/hnodeb/vty.h b/include/osmocom/hnodeb/vty.h index e30019e..627436d 100644 --- a/include/osmocom/hnodeb/vty.h +++ b/include/osmocom/hnodeb/vty.h @@ -27,6 +27,7 @@ enum hnb_vty_nodes { HNODEB_NODE = _LAST_OSMOVTY_NODE, IUH_NODE, + LLSK_NODE, }; void hnb_vty_init(void); diff --git a/src/osmo-hnodeb/Makefile.am b/src/osmo-hnodeb/Makefile.am index 82ccd37..b6d2d49 100644 --- a/src/osmo-hnodeb/Makefile.am +++ b/src/osmo-hnodeb/Makefile.am @@ -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 \ diff --git a/src/osmo-hnodeb/debug.c b/src/osmo-hnodeb/debug.c index 45ccc0c..f34851b 100644 --- a/src/osmo-hnodeb/debug.c +++ b/src/osmo-hnodeb/debug.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 = { diff --git a/src/osmo-hnodeb/hnb.c b/src/osmo-hnodeb/hnb.c index b7be4ea..1ac2efc 100644 --- a/src/osmo-hnodeb/hnb.c +++ b/src/osmo-hnodeb/hnb.c @@ -23,10 +23,12 @@ #include #include #include +#include #include #include #include +#include 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; +} diff --git a/src/osmo-hnodeb/hnb_shutdown_fsm.c b/src/osmo-hnodeb/hnb_shutdown_fsm.c index 55de64c..5583834 100644 --- a/src/osmo-hnodeb/hnb_shutdown_fsm.c +++ b/src/osmo-hnodeb/hnb_shutdown_fsm.c @@ -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 */ } diff --git a/src/osmo-hnodeb/hnbap.c b/src/osmo-hnodeb/hnbap.c index 3287eeb..ccd521e 100644 --- a/src/osmo-hnodeb/hnbap.c +++ b/src/osmo-hnodeb/hnbap.c @@ -33,7 +33,9 @@ #include #include #include +#include #include +#include 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"); diff --git a/src/osmo-hnodeb/llsk.c b/src/osmo-hnodeb/llsk.c new file mode 100644 index 0000000..5f0f9dc --- /dev/null +++ b/src/osmo-hnodeb/llsk.c @@ -0,0 +1,134 @@ +/* (C) 2021 by sysmocom - s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 . + * + */ + +#include + +#include +#include + +#include +#include +#include +#include + +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; +} diff --git a/src/osmo-hnodeb/llsk_iuh.c b/src/osmo-hnodeb/llsk_iuh.c new file mode 100644 index 0000000..00c399a --- /dev/null +++ b/src/osmo-hnodeb/llsk_iuh.c @@ -0,0 +1,347 @@ +/* (C) 2021 by sysmocom - s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +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; + } +} diff --git a/src/osmo-hnodeb/main.c b/src/osmo-hnodeb/main.c index 12d98ff..6728a07 100644 --- a/src/osmo-hnodeb/main.c +++ b/src/osmo-hnodeb/main.c @@ -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"); diff --git a/src/osmo-hnodeb/ranap.c b/src/osmo-hnodeb/ranap.c index 99c4bab..3313f7b 100644 --- a/src/osmo-hnodeb/ranap.c +++ b/src/osmo-hnodeb/ranap.c @@ -27,3 +27,8 @@ #include #include + +struct msgb *hnb_ranap_msgb_alloc(void) +{ + return msgb_alloc(1000, "ranap_tx"); +} diff --git a/src/osmo-hnodeb/rua.c b/src/osmo-hnodeb/rua.c index 9bf5403..3a73f0c 100644 --- a/src/osmo-hnodeb/rua.c +++ b/src/osmo-hnodeb/rua.c @@ -32,10 +32,23 @@ #include #include + +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; } diff --git a/src/osmo-hnodeb/vty.c b/src/osmo-hnodeb/vty.c index 93ec25f..131dc6b 100644 --- a/src/osmo-hnodeb/vty.c +++ b/src/osmo-hnodeb/vty.c @@ -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);