Change in osmo-hnodeb[master]: WIP

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Wed Nov 24 20:12:51 UTC 2021


pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-hnodeb/+/26357 )


Change subject: WIP
......................................................................

WIP

Change-Id: Icaabb2206d6f141d4fba47dedf71f8ec37e6257d
---
A doc/hnodeb.msc
M include/osmocom/hnodeb/Makefile.am
A include/osmocom/hnodeb/hnb_prim.h
M include/osmocom/hnodeb/hnodeb.h
A include/osmocom/hnodeb/llsk.h
M include/osmocom/hnodeb/vty.h
M src/osmo-hnodeb/Makefile.am
M src/osmo-hnodeb/debug.c
M src/osmo-hnodeb/hnb.c
M src/osmo-hnodeb/hnb_shutdown_fsm.c
M src/osmo-hnodeb/hnbap.c
A src/osmo-hnodeb/llsk.c
A src/osmo-hnodeb/llsk_ctl.c
A src/osmo-hnodeb/llsk_iuh.c
M src/osmo-hnodeb/main.c
M src/osmo-hnodeb/vty.c
16 files changed, 1,041 insertions(+), 2 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-hnodeb refs/changes/57/26357/1

diff --git a/doc/hnodeb.msc b/doc/hnodeb.msc
new file mode 100644
index 0000000..a5f7a86
--- /dev/null
+++ b/doc/hnodeb.msc
@@ -0,0 +1,62 @@
+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"];
+	trx <= hnodeb [label="REQ(SAPI_IUH, HNB_IUH_PRIM_CONFIGURE)(PLMN,LAC,SAC,RAC,CI,RNC_ID from HnbRegisterResponse)"];
+	|||;
+	...;
+	|||;
+	|||;
+	---  [ label = "Subscriber Sign Up" ];
+	ue => trx [label="..."];
+	trx => hnodeb [label="IND(SAPI_IUH, CONN_ESTABLISH)[RANAP?]"];
+	hnodeb => hnbgw [label="HNBAP UE Register Req(IMSI?)"];
+	hnodeb <= hnbgw [label="HNBAP UE Register Acc(context_id)"];
+	trx <= hnodeb [label="CNF(SAPI_IUH, CONN_ESTABLISH)(context_id])"];
+	trx => hnodeb [label="IND(SAPI_IUH, CONN_DATA)[RANAP GMM ServiceRequest]"];
+	hnodeb => hnbgw [label="RANAP GMM ServiceRequest"];
+	hnodeb <= hnbgw [label="RANAP GMM ServiceAccept"];
+	trx <= hnodeb [label="REQ(SAPI_IUH, CONN_DATA)[RANAP GMM ServiceAccept]"];
+	---  [ label = "Subscriber set up PS data:" ];
+	hnodeb <= hnbgw [label="RANAP RAB-Assignment Request(TEI, ADDR)"];
+	trx <= hnodeb [label="REQ(SAPI_IUH, CONN_DATA)[RANAP RAB-Assignment Request(remote_ip, remote_port, remote_tei)]"];
+	trx => hnodeb [label="IND(SAPI_GTP, CONN_ESTABLISH)(remote_ip,remote_port,remote_tei)"];
+	...  [ label = "HnodeB sets up GTP-U connection" ];
+	trx <= hnodeb [label="CNF(SAPI_GTP, CONN_ESTABLISH)(local_ip,local_port,local_tei,remote_tei)"];
+	|||;
+	...;
+	|||;
+	---  [ label = "PS data transmission over GTP-U:" ];
+	ue => trx [label="..."];
+	trx => hnodeb [label="IND(SAPI_GTP, CONN_DATA)[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="REQ(SAPI_GTP, CONN_DATA)[local_tei,payload]"];
+	ue <= trx [label="..."];
+	|||;
+	...;
+	|||;
+	---  [ label = "MO/MT PS data Release:" ];
+	ue => trx [label="..."];
+	trx => hnodeb [label="IND(SAPI_IUH, CONN_DATA)[RANAP IU Release Request]"];
+	hnodeb => hnbgw [label="RANAP IU Release Request"];
+	hnodeb <= hnbgw [label="RANAP IU Release Command"];
+	trx <= hnodeb [label="REQ(SAPI_IUH, CONN_DATA)[RANAP IU Release Command]"];
+	...;
+	trx => hnodeb [label="IND(SAPI_GTP, CONN_RELEASE)(remote_tei)"];
+	trx <= hnodeb [label="CNF(SAPI_GTP, CONN_RELEASE)(remote_tei)"];
+
+	trx => hnodeb [label="IND(SAPI_IUH, CONN_RELEASE)[RANAP IU Release Complete]"];
+	hnodeb => hnbgw [label="RANAP IU Release Complete"];
+	trx => hnodeb [label="CNF(SAPI_GTP, CONN_RELEASE)"];
+
+	|||;
+	...;
+	|||;
+	---  [ label = "For voice call (CS): Similar to SAPI_GTP, but using SAPI_AUDIO and osmo-hnodeb sets up RTP stream" ];
+
+}
diff --git a/include/osmocom/hnodeb/Makefile.am b/include/osmocom/hnodeb/Makefile.am
index b1acb7e..56d1033 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 \
 	nas.h \
 	ranap.h \
 	rua.h \
diff --git a/include/osmocom/hnodeb/hnb_prim.h b/include/osmocom/hnodeb/hnb_prim.h
new file mode 100644
index 0000000..8cede29
--- /dev/null
+++ b/include/osmocom/hnodeb/hnb_prim.h
@@ -0,0 +1,271 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/lienses/>.
+ *
+ */
+#pragma once
+
+#include <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_CTL 0
+#define HNB_PRIM_SAPI_IUH 1
+#define HNB_PRIM_SAPI_GTP 2
+#define HNB_PRIM_SAPI_AUDIO 3
+
+struct hnb_prim_hdr {
+	uint32_t sap;    /*!< Service Access Point Identifier */
+	uint32_t primitive;    /*!< Primitive number */
+	uint32_t operation; /*! Primitive Operation */
+} __attribute__ ((packed));
+
+/*! \brief HNB_CTL primitives */
+enum hnb_ctl_prim_type {
+	HNB_CTL_PRIM_HELLO,
+	_HNB_CTL_PRIM_MAX
+};
+
+/* HNB_CTL_PRIM_HELLO, UL */
+struct hnb_ctl_hello_ind_param {
+	uint16_t api_version; /* see HNB_PRIM_API_VERSION */
+} __attribute__ ((packed));
+
+/* HNB_CTL_PRIM_HELLO, DL */
+struct hnb_ctl_hello_cnf_param {
+	uint16_t api_version; /* see HNB_PRIM_API_VERSION */
+} __attribute__ ((packed));
+
+struct hnb_ctl_prim {
+	struct hnb_prim_hdr hdr;
+	union {
+		struct hnb_ctl_hello_ind_param hello_ind;
+		struct hnb_ctl_hello_cnf_param hello_cnf;
+	} u;
+} __attribute__ ((packed));
+
+
+/*! \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, DL */
+struct hnb_iuh_configure_req_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_IUH_PRIM_CONN_ESTABLISH, DL */
+struct hnb_iuh_conn_establish_req_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 */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+/* HNB_IUH_PRIM_CONN_ESTABLISH, UL */
+struct hnb_iuh_conn_establish_ind_param {
+	uint32_t context_id;
+	uint8_t domain;
+	uint8_t 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 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 */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+/* HNB_IUH_PRIM_CONN_RELEASE, DL */
+struct hnb_iuh_conn_release_req_param {
+	uint32_t context_id;
+	uint8_t domain;
+	uint8_t cause;
+	uint16_t spare1;
+	uint32_t data_len; /* RANAP message length in bytes */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+/* HNB_IUH_PRIM_CONN_RELEASE, UL */
+struct hnb_iuh_conn_release_ind_param {
+	uint32_t context_id;
+	uint8_t domain;
+	uint8_t cause;
+	uint16_t spare1;
+	uint32_t data_len; /* RANAP message length in bytes */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+/* HNB_IUH_PRIM_CONN_DATA, 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 */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+/* HNB_IUH_PRIM_CONN_DATA, 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 */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+/* HNB_IUH_PRIM_UNITDATA, DL */
+struct hnb_iuh_unitdata_req_param {
+	uint8_t domain;
+	uint8_t spare1;
+	uint16_t spare2;
+	uint32_t data_len; /* RANAP message length in bytes */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+/* HNB_IUH_PRIM_UNITDATA, UL */
+struct hnb_iuh_unitdata_ind_param {
+	uint8_t domain;
+	uint8_t spare1;
+	uint16_t spare2;
+	uint32_t data_len; /* RANAP message length in bytes */
+	char data[0]; /* RANAP message */
+} __attribute__ ((packed));
+
+struct hnb_iuh_prim {
+	struct hnb_prim_hdr hdr;
+	union {
+		struct hnb_iuh_configure_req_param configure_req;
+		struct hnb_iuh_conn_establish_req_param conn_establish_req;
+		struct hnb_iuh_conn_establish_ind_param conn_establish_ind;
+		struct hnb_iuh_conn_release_req_param conn_reelase_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));
+
+struct hnb_iuh_prim *hnb_iuh_makeprim_conn_establish_req(uint32_t context_id,
+							 uint8_t domain,
+							 uint8_t cause,
+							 uint8_t csg_membership_status,
+							 uint8_t *data,
+							 uint32_t data_len);
+struct hnb_iuh_prim *hnb_iuh_makeprim_conn_release_req(uint32_t context_id,
+						       uint8_t domain,
+						       uint8_t cause,
+						       uint8_t *data,
+						       uint32_t data_len);
+struct hnb_iuh_prim *hnb_iuh_makeprim_conn_data_req(uint32_t context_id,
+						    uint8_t domain,
+						    uint8_t *data,
+						    uint32_t data_len);
+struct hnb_iuh_prim *hnb_iuh_makeprim_unitdata_req(uint8_t domain,
+						   uint8_t *data,
+						   uint32_t data_len);
+
+
+/*! \brief HNB_GTP primitives */
+enum hnb_gtp_prim_type {
+	HNB_GTP_PRIM_CONN_ESTABLISH,
+	HNB_GTP_PRIM_CONN_RELEASE,
+	HNB_GTP_PRIM_CONN_DATA
+};
+union u_addr {
+	struct in_addr v4;
+	struct in6_addr v6;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_ESTABLISH, UL */
+struct hnb_gtp_conn_establish_ind_param {
+	uint32_t remote_tei;
+	uint16_t remote_gtpu_port;
+	uint8_t remote_gtpu_address_type;
+	uint8_t spare1;
+	union u_addr remote_gtpu_addr;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_ESTABLISH, DL */
+struct hnb_gtp_conn_establish_cnf_param {
+	uint32_t remote_tei;
+	uint32_t local_tei;
+	uint16_t local_gtpu_port;
+	uint8_t local_gtpu_address_type;
+	uint8_t spare1;
+	union u_addr remote_gtpu_addr;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_RELEASE, UL */
+struct hnb_gtp_conn_release_ind_param {
+	uint32_t remote_tei;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_RELEASE, DL */
+struct hnb_gtp_conn_release_cnf_param {
+	uint32_t remote_tei;
+} __attribute__ ((packed));
+struct hnb_gtp_conn_release_req_param {
+	uint32_t remote_tei;
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_DATA, DL */
+struct hnb_gtp_conn_data_req_param {
+	uint32_t local_tei;
+	uint32_t data_len; /* GTP-U payload length in bytes */
+	char data[0]; /* GTP-U payload (aka IP packet) */
+} __attribute__ ((packed));
+
+/* HNB_GTP_PRIM_CONN_DATA, UL */
+struct hnb_gtp_conn_data_ind_param {
+	uint32_t remote_tei;
+	uint32_t data_len; /* GTP-U payload length in bytes */
+	char data[0]; /* GTP-U payload (aka IP packet) */
+} __attribute__ ((packed));
+
+struct hnb_gtp_prim {
+	struct hnb_prim_hdr hdr;
+	union {
+		struct hnb_gtp_conn_establish_ind_param conn_establish_ind;
+		struct hnb_gtp_conn_establish_cnf_param conn_establish_cnf;
+		struct hnb_gtp_conn_data_req_param conn_data_req;
+		struct hnb_gtp_conn_data_ind_param conn_data_ind;
+	} u;
+} __attribute__ ((packed));
diff --git a/include/osmocom/hnodeb/hnodeb.h b/include/osmocom/hnodeb/hnodeb.h
index 3bc2fb3..186798b 100644
--- a/include/osmocom/hnodeb/hnodeb.h
+++ b/include/osmocom/hnodeb/hnodeb.h
@@ -28,12 +28,15 @@
 #include <osmocom/gsm/gsm23003.h>
 #include <osmocom/netif/stream.h>
 
+#include <osmocom/hnodeb/llsk.h>
+
 enum {
 	DMAIN,
 	DHNBAP,
 	DRUA,
 	DRANAP,
 	DSCTP,
+	DLLSK,
 	DNAS,
 };
 extern const struct log_info hnb_log_info;
@@ -59,7 +62,14 @@
 		struct osmo_stream_cli *client;
 	} iuh;
 
+	/* Lower Layer UD socket */
+	struct {
+		char *sock_path;
+		struct llsk_state *state;
+	} llsk;
+
 	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;
 
diff --git a/include/osmocom/hnodeb/llsk.h b/include/osmocom/hnodeb/llsk.h
new file mode 100644
index 0000000..a08c006
--- /dev/null
+++ b/include/osmocom/hnodeb/llsk.h
@@ -0,0 +1,56 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/lienses/>.
+ *
+ */
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/hnodeb/hnb_prim.h>
+
+struct hnb;
+struct msgb;
+
+struct llsk_state {
+	struct hnb *hnb;
+	struct osmo_fd listen_bfd;	/* fd for listen socket */
+	struct osmo_fd conn_bfd;	/* fd for connection to lcr */
+	struct llist_head upqueue;	/* queue for sending messages */
+};
+
+struct llsk_state *llsk_alloc(struct hnb *hnb);
+int llsk_open(struct llsk_state *state, const char *path);
+void llsk_free(struct llsk_state *state);
+
+int llsk_send(struct llsk_state *state, struct msgb *msg);
+bool llsk_connected(const struct llsk_state *state);
+
+
+extern const struct value_string hnb_ctl_prim_type_names[];
+int llsk_rx_ctl(struct hnb *hnb, struct hnb_ctl_prim *ctl, unsigned len);
+
+
+extern const struct value_string hnb_iuh_prim_type_names[];
+int llsk_rx_iuh(struct hnb *hnb, struct hnb_iuh_prim *iuh, unsigned len);
+struct msgb *hnb_iuh_makeprim_configure_req(uint16_t mcc, uint16_t mnc,
+						   uint16_t cell_identity,
+						   uint16_t lac, uint8_t rac,
+						   uint16_t sac, uint16_t rnc_id);
diff --git a/include/osmocom/hnodeb/vty.h b/include/osmocom/hnodeb/vty.h
index 7144d3f..1624bfb 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,
 	CHAN_NODE,
 };
 
diff --git a/src/osmo-hnodeb/Makefile.am b/src/osmo-hnodeb/Makefile.am
index 7325728..0117f28 100644
--- a/src/osmo-hnodeb/Makefile.am
+++ b/src/osmo-hnodeb/Makefile.am
@@ -35,6 +35,9 @@
 	hnb.c \
 	hnb_shutdown_fsm.c \
 	iuh.c \
+	llsk.c \
+	llsk_ctl.c \
+	llsk_iuh.c \
 	nas.c \
 	ranap.c \
 	rua.c \
diff --git a/src/osmo-hnodeb/debug.c b/src/osmo-hnodeb/debug.c
index 10d1655..e610ff0 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 @@
 		.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",
+	},
 	[DNAS] = {
 		.name = "NAS", .loglevel = LOGL_NOTICE, .enabled = 1,
 		.color = "\033[1;32m",
diff --git a/src/osmo-hnodeb/hnb.c b/src/osmo-hnodeb/hnb.c
index b7be4ea..d7cf886 100644
--- a/src/osmo-hnodeb/hnb.c
+++ b/src/osmo-hnodeb/hnb.c
@@ -27,6 +27,7 @@
 #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)
@@ -43,6 +44,9 @@
 		.mnc = 1,
 	};
 
+	hnb->llsk.sock_path = talloc_strdup(hnb, HNB_PRIM_UD_SOCK_DEFAULT);
+	hnb->llsk.state = llsk_alloc(hnb);
+
 	hnb->shutdown_fi = osmo_fsm_inst_alloc(&hnb_shutdown_fsm, hnb, hnb,
 					       LOGL_INFO, NULL);
 
@@ -58,5 +62,9 @@
 		hnb->shutdown_fi = NULL;
 	}
 	hnb_iuh_free(hnb);
+
+	llsk_free(hnb->llsk.state);
+	hnb->llsk.state = NULL;
+
 	talloc_free(hnb);
 }
diff --git a/src/osmo-hnodeb/hnb_shutdown_fsm.c b/src/osmo-hnodeb/hnb_shutdown_fsm.c
index 7df591e..14e8b32 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 0495ce0..4ea1342 100644
--- a/src/osmo-hnodeb/hnbap.c
+++ b/src/osmo-hnodeb/hnbap.c
@@ -39,16 +39,31 @@
 {
 	int rc;
 	HNBAP_HNBRegisterAcceptIEs_t accept;
+	struct msgb *llsk_msg;
 
 	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 (llsk_connected(hnb->llsk.state)) {
+		/* We are attached to the HNBGW, configure lower layers: */
+		llsk_msg = hnb_iuh_makeprim_configure_req(hnb->plmn.mcc, hnb->plmn.mnc,
+							  hnb->cell_identity, hnb->lac,
+							  hnb->rac, hnb->sac, hnb->rnc_id);
+		rc = llsk_send(hnb->llsk.state, llsk_msg);
+		if (rc < 0) {
+			LOGP(DHNBAP, LOGL_NOTICE, "Failed configuring lower layers after HnbRegisterAccept\n");
+		}
+	}
+	return rc;
 }
 
 static int hnb_rx_hnb_register_rej(struct hnb *hnb, ANY_t *in)
@@ -56,6 +71,8 @@
 	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..dee7477
--- /dev/null
+++ b/src/osmo-hnodeb/llsk.c
@@ -0,0 +1,306 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/lienses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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/hnodeb/hnodeb.h>
+#include <osmocom/hnodeb/llsk.h>
+#include <osmocom/hnodeb/hnb_prim.h>
+
+static void llsk_close(struct llsk_state *state);
+
+struct llsk_state *llsk_alloc(struct hnb *hnb)
+{
+	struct llsk_state *state;
+	state = talloc_zero(NULL, struct llsk_state);
+	if (!state)
+		return NULL;
+
+	INIT_LLIST_HEAD(&state->upqueue);
+	state->hnb = hnb;
+	state->conn_bfd.fd = -1;
+	state->listen_bfd.fd = -1;
+	return state;
+}
+
+void llsk_free(struct llsk_state *state)
+{
+	struct osmo_fd *bfd, *conn_bfd;
+
+	if (!state)
+		return;
+
+	conn_bfd = &state->conn_bfd;
+	if (conn_bfd->fd > 0)
+		llsk_close(state);
+	bfd = &state->listen_bfd;
+	close(bfd->fd);
+	osmo_fd_unregister(bfd);
+	talloc_free(state);
+}
+
+static int llsk_rx(struct hnb *hnb, struct hnb_prim_hdr *hdr, unsigned len)
+{
+	switch (hdr->sap) {
+	case HNB_PRIM_SAPI_CTL:
+		return llsk_rx_ctl(hnb, ((struct hnb_ctl_prim *)hdr), len);
+	case HNB_PRIM_SAPI_IUH:
+		return llsk_rx_iuh(hnb, ((struct hnb_iuh_prim *)hdr), len);
+	case HNB_PRIM_SAPI_GTP:
+	case HNB_PRIM_SAPI_AUDIO:
+		LOGP(DLLSK, LOGL_ERROR, "Rx SAPI %u not yet implemented (len=%u)\n", hdr->sap, len);
+		return -EINVAL;
+	default:
+		LOGP(DLLSK, LOGL_ERROR, "Rx for unknwon SAPI %u (len=%u)\n", hdr->sap, len);
+		return -EINVAL;
+	}
+}
+
+static int llsk_read(struct osmo_fd *bfd)
+{
+	struct llsk_state *state = (struct llsk_state *)bfd->data;
+	struct hnb_prim_hdr *hdr;
+	struct msgb *msg;
+	int rc;
+
+	msg = msgb_alloc(1000, "llsk_rx");
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = (struct hnb_prim_hdr *) msg->tail;
+
+	rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
+	if (rc == 0)
+		goto close;
+
+	if (rc < 0) {
+		if (errno == EAGAIN) {
+			msgb_free(msg);
+			return 0;
+		}
+		goto close;
+	}
+
+	if (rc < sizeof(struct hnb_prim_hdr)) {
+		LOGP(DLLSK, LOGL_ERROR, "Received %d bytes on PCU Socket, but primitive hdr size "
+		     "is %zu, discarding\n", rc, sizeof(struct hnb_prim_hdr));
+		msgb_free(msg);
+		return 0;
+	}
+
+	rc = llsk_rx(state->hnb, hdr, rc);
+
+	/* as we always synchronously process the message in llsk_rx() and
+	 * its callbacks, we can free the message here. */
+	msgb_free(msg);
+
+	return rc;
+
+close:
+	msgb_free(msg);
+	llsk_close(state);
+	return -1;
+}
+
+static int llsk_write(struct osmo_fd *bfd)
+{
+	struct llsk_state *state = bfd->data;
+	int rc;
+
+	while (!llist_empty(&state->upqueue)) {
+		struct msgb *msg, *msg2;
+
+		/* peek at the beginning of the queue */
+		msg = llist_entry(state->upqueue.next, struct msgb, list);
+
+		osmo_fd_write_disable(bfd);
+
+		if (!msgb_length(msg)) {
+			LOGP(DLLSK, LOGL_ERROR, "message with ZERO "
+				"bytes!\n");
+			goto dontsend;
+		}
+
+		/* try to send it over the socket */
+		rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
+		if (rc == 0)
+			goto close;
+		if (rc < 0) {
+			if (errno == EAGAIN) {
+				osmo_fd_write_enable(bfd);
+				break;
+			}
+			goto close;
+		}
+
+dontsend:
+		/* _after_ we send it, we can deueue */
+		msg2 = msgb_dequeue(&state->upqueue);
+		assert(msg == msg2);
+		msgb_free(msg);
+	}
+	return 0;
+
+close:
+	llsk_close(state);
+
+	return -1;
+}
+
+static int llsk_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
+{
+	int rc = 0;
+
+	if (flags & OSMO_FD_READ)
+		rc = llsk_read(bfd);
+	if (rc < 0)
+		return rc;
+
+	if (flags & OSMO_FD_WRITE)
+		rc = llsk_write(bfd);
+
+	return rc;
+}
+
+/* accept connection coming from PCU */
+static int llsk_accept(struct osmo_fd *bfd, unsigned int flags)
+{
+	struct llsk_state *state = (struct llsk_state *)bfd->data;
+	struct osmo_fd *conn_bfd = &state->conn_bfd;
+	struct sockaddr_un un_addr;
+	socklen_t len;
+	int rc;
+
+	len = sizeof(un_addr);
+	rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
+	if (rc < 0) {
+		LOGP(DLLSK, LOGL_ERROR, "Failed to accept a new connection\n");
+		return -1;
+	}
+
+	if (conn_bfd->fd >= 0) {
+		LOGP(DLLSK, LOGL_NOTICE, "LL UD Socket connects but we already have "
+		     "another active connection ?!?\n");
+		/* We already have one LLSK connected, this is all we support */
+		state->listen_bfd.when &= ~OSMO_FD_READ;
+		close(rc);
+		return 0;
+	}
+
+	osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, llsk_bfd_cb, state, 0);
+
+	if (osmo_fd_register(conn_bfd) != 0) {
+		LOGP(DLLSK, LOGL_ERROR, "Failed to register new connection fd\n");
+		close(conn_bfd->fd);
+		conn_bfd->fd = -1;
+		return -1;
+	}
+
+	LOGP(DLLSK, LOGL_NOTICE, "LL UD socket connected\n");
+
+	return 0;
+}
+
+int llsk_open(struct llsk_state *state, const char *path)
+{
+	struct osmo_fd *bfd = &state->listen_bfd;
+	int rc;
+
+	rc = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND);
+	if (rc < 0) {
+		LOGP(DLLSK, LOGL_ERROR, "Could not create %s unix socket: %s\n",
+		     path, strerror(errno));
+		return -1;
+	}
+
+	osmo_fd_setup(bfd, rc, OSMO_FD_READ, llsk_accept, state, 0);
+
+	rc = osmo_fd_register(bfd);
+	if (rc < 0) {
+		LOGP(DLLSK, LOGL_ERROR, "Could not register listen fd: %d\n",
+			rc);
+		close(bfd->fd);
+		bfd->fd = -1;
+		return rc;
+	}
+
+	LOGP(DLLSK, LOGL_INFO, "Started listening on Lower Layer Unix Domain Socket: %s\n", path);
+
+	return 0;
+}
+
+static void llsk_close(struct llsk_state *state)
+{
+	struct osmo_fd *bfd = &state->conn_bfd;
+
+	close(bfd->fd);
+	bfd->fd = -1;
+	osmo_fd_unregister(bfd);
+
+	/* re-enable the generation of ACCEPT for new connections */
+	osmo_fd_read_enable(&state->listen_bfd);
+
+	/* flush the queue */
+	while (!llist_empty(&state->upqueue)) {
+		struct msgb *msg = msgb_dequeue(&state->upqueue);
+		msgb_free(msg);
+	}
+}
+
+int llsk_send(struct llsk_state *state, struct msgb *msg)
+{
+	struct osmo_fd *conn_bfd;
+
+	if (!state) {
+		LOGP(DLLSK, LOGL_INFO, "LL UD socket not created, dropping message\n");
+		msgb_free(msg);
+		return -EINVAL;
+	}
+	conn_bfd = &state->conn_bfd;
+	if (conn_bfd->fd <= 0) {
+		LOGP(DLLSK, LOGL_NOTICE, "LL UD socket not connected, dropping message\n");
+		msgb_free(msg);
+		return -EIO;
+	}
+	msgb_enqueue(&state->upqueue, msg);
+	osmo_fd_write_enable(conn_bfd);
+
+	return 0;
+}
+
+bool llsk_connected(const struct llsk_state *state)
+{
+	if (!state)
+		return false;
+	if (state->conn_bfd.fd <= 0)
+		return false;
+	return true;
+}
diff --git a/src/osmo-hnodeb/llsk_ctl.c b/src/osmo-hnodeb/llsk_ctl.c
new file mode 100644
index 0000000..c6b0f55
--- /dev/null
+++ b/src/osmo-hnodeb/llsk_ctl.c
@@ -0,0 +1,144 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/lienses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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/hnodeb/hnodeb.h>
+#include <osmocom/hnodeb/llsk.h>
+#include <osmocom/hnodeb/hnb_prim.h>
+
+static size_t llsk_ctl_prim_size_tbl[4][_HNB_CTL_PRIM_MAX] = {
+	[PRIM_OP_REQUEST] = {},
+	[PRIM_OP_RESPONSE] = {},
+	[PRIM_OP_INDICATION] = {
+		[HNB_CTL_PRIM_HELLO] = sizeof(struct hnb_ctl_hello_ind_param),
+	},
+	[PRIM_OP_CONFIRM] = {
+		[HNB_CTL_PRIM_HELLO] = sizeof(struct hnb_ctl_hello_cnf_param),
+	},
+};
+static inline size_t llsk_ctl_prim_size(enum hnb_ctl_prim_type ptype, enum osmo_prim_operation op)
+{
+	return llsk_ctl_prim_size_tbl[op][ptype];
+}
+
+const struct value_string hnb_ctl_prim_type_names[] = {
+	OSMO_VALUE_STRING(HNB_CTL_PRIM_HELLO),
+	{ 0, NULL }
+};
+
+struct msgb *llsk_ctl_msgb_alloc(enum hnb_ctl_prim_type ptype, enum osmo_prim_operation op)
+{
+	struct msgb *msg;
+	struct hnb_ctl_prim *ctl_prim;
+	size_t len = sizeof(ctl_prim->hdr) + llsk_ctl_prim_size(ptype, op);
+
+	msg = msgb_alloc(len, "llsk_ctl_tx");
+	if (!msg)
+		return NULL;
+	msgb_put(msg, len);
+	ctl_prim = (struct hnb_ctl_prim *) msg->data;
+	ctl_prim->hdr.sap = HNB_PRIM_SAPI_CTL;
+	ctl_prim->hdr.primitive = ptype;
+	ctl_prim->hdr.operation = op;
+
+	return msg;
+}
+
+static struct msgb *hnb_ctl_makeprim_hello_cnf(uint16_t api_version)
+{
+	struct msgb *msg;
+	struct hnb_ctl_prim *ctl_prim;
+
+	msg = llsk_ctl_msgb_alloc(HNB_CTL_PRIM_HELLO, PRIM_OP_CONFIRM);
+	ctl_prim = (struct hnb_ctl_prim *)msgb_data(msg);
+	ctl_prim->u.hello_cnf.api_version = api_version;
+
+	return msg;
+}
+
+static int llsk_rx_ctl_hello_ind(struct hnb *hnb, struct hnb_ctl_hello_ind_param *hello_ind)
+{
+	struct msgb *llsk_msg;
+	int rc;
+
+	LOGP(DLLSK, LOGL_NOTICE, "Rx CTL::HELLO.ind API_VERSION=%u\n", hello_ind->api_version);
+
+	LOGP(DLLSK, LOGL_NOTICE, "Tx CTL::HELLO.cnf API_VERSION=%u\n", hello_ind->api_version);
+	llsk_msg = hnb_ctl_makeprim_hello_cnf(HNB_CTL_PRIM_HELLO);
+	if ((rc = llsk_send(hnb->llsk.state, llsk_msg)) < 0)
+		LOGP(DHNBAP, LOGL_ERROR, "Failed sending CTL::HELLO.cnf\n");
+
+	if (hnb->registered) {
+		LOGP(DLLSK, LOGL_INFO, "Tx CTL::CONFIGURE.req API_VERSION=%u\n", hello_ind->api_version);
+		/* We are already registered, so configure the lower layers right now */
+		llsk_msg = hnb_iuh_makeprim_configure_req(hnb->plmn.mcc, hnb->plmn.mnc,
+							  hnb->cell_identity, hnb->lac,
+							  hnb->rac, hnb->sac, hnb->rnc_id);
+		if ((rc = llsk_send(hnb->llsk.state, llsk_msg)) < 0)
+			LOGP(DHNBAP, LOGL_ERROR, "Failed sending IUH::CONFIGURE.req\n");
+	}
+	return rc;
+}
+
+int llsk_rx_ctl(struct hnb *hnb, struct hnb_ctl_prim *ctl, unsigned len)
+{
+	size_t prim_size = sizeof(ctl->hdr) + llsk_ctl_prim_size(ctl->hdr.primitive, ctl->hdr.operation);
+
+	if (len < prim_size) {
+		LOGP(DLLSK, LOGL_ERROR, "Rx llsk-ctl %s::%s with length %u < %zu\n",
+		     get_value_string(hnb_iuh_prim_type_names, ctl->hdr.primitive),
+		     get_value_string(osmo_prim_op_names, ctl->hdr.operation),
+			len, prim_size);
+		return -EINVAL;
+	}
+
+	switch (ctl->hdr.operation) {
+	case PRIM_OP_INDICATION:
+		switch (ctl->hdr.primitive) {
+		case HNB_CTL_PRIM_HELLO:
+			return llsk_rx_ctl_hello_ind(hnb, &ctl->u.hello_ind);
+		default:
+			LOGP(DLLSK, LOGL_ERROR, "Rx llsk-ctl unknown primitive %u (len=%u)\n", ctl->hdr.primitive, len);
+			return -EINVAL;
+		}
+		break;
+
+	case PRIM_OP_RESPONSE:
+	case PRIM_OP_REQUEST:
+	case PRIM_OP_CONFIRM:
+	default:
+		LOGP(DLLSK, LOGL_ERROR, "Rx llsk-ctl unexpected primitive operation %s::%s (len=%u)\n",
+		     get_value_string(hnb_iuh_prim_type_names, ctl->hdr.primitive),
+		     get_value_string(osmo_prim_op_names, ctl->hdr.operation), len);
+		return -EINVAL;
+	}
+}
diff --git a/src/osmo-hnodeb/llsk_iuh.c b/src/osmo-hnodeb/llsk_iuh.c
new file mode 100644
index 0000000..b5c13d7
--- /dev/null
+++ b/src/osmo-hnodeb/llsk_iuh.c
@@ -0,0 +1,103 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/lienses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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/hnodeb/hnodeb.h>
+#include <osmocom/hnodeb/llsk.h>
+#include <osmocom/hnodeb/hnb_prim.h>
+
+static size_t llsk_iuh_prim_size_tbl[4][_HNB_IUH_PRIM_MAX] = {
+	[PRIM_OP_REQUEST] = {
+		[HNB_IUH_PRIM_CONFIGURE] = sizeof(struct hnb_iuh_configure_req_param),
+	},
+	[PRIM_OP_RESPONSE] = {},
+	[PRIM_OP_INDICATION] = {},
+	[PRIM_OP_CONFIRM] = {},
+};
+static inline size_t llsk_iuh_prim_size(enum hnb_iuh_prim_type ptype, enum osmo_prim_operation op)
+{
+	return llsk_iuh_prim_size_tbl[op][ptype];
+}
+
+const struct value_string hnb_iuh_prim_type_names[] = {
+	OSMO_VALUE_STRING(HNB_IUH_PRIM_CONFIGURE),
+	{ 0, NULL }
+};
+
+
+struct msgb *llsk_iuh_msgb_alloc(enum hnb_iuh_prim_type ptype, enum osmo_prim_operation op)
+{
+	struct msgb *msg;
+	struct hnb_iuh_prim *iuh_prim;
+	size_t len = sizeof(iuh_prim->hdr) + llsk_iuh_prim_size(ptype, op);
+
+	msg = msgb_alloc(len, "llsk_iuh_tx");
+	if (!msg)
+		return NULL;
+	msgb_put(msg, len);
+	iuh_prim = (struct hnb_iuh_prim *) msg->data;
+	iuh_prim->hdr.sap = HNB_PRIM_SAPI_IUH;
+	iuh_prim->hdr.primitive = ptype;
+	iuh_prim->hdr.operation = op;
+
+	return msg;
+}
+
+struct msgb *hnb_iuh_makeprim_configure_req(uint16_t mcc, uint16_t mnc,
+						   uint16_t cell_identity,
+						   uint16_t lac, uint8_t rac,
+						   uint16_t sac, uint16_t rnc_id)
+{
+	struct msgb *msg;
+	struct hnb_iuh_prim *iuh_prim;
+
+	msg = llsk_iuh_msgb_alloc(HNB_IUH_PRIM_CONFIGURE, PRIM_OP_REQUEST);
+	iuh_prim = (struct hnb_iuh_prim *)msgb_data(msg);
+	iuh_prim->u.configure_req.mcc = mcc;
+	iuh_prim->u.configure_req.mnc = mnc;
+	iuh_prim->u.configure_req.cell_identity = cell_identity;
+	iuh_prim->u.configure_req.lac = lac;
+	iuh_prim->u.configure_req.rac = rac;
+	iuh_prim->u.configure_req.sac = sac;
+	iuh_prim->u.configure_req.rnc_id = rnc_id;
+
+	return msg;
+}
+
+int llsk_rx_iuh(struct hnb *hnb, struct hnb_iuh_prim *iuh, unsigned len)
+{
+	switch (iuh->hdr.primitive) {
+	default:
+		LOGP(DLLSK, LOGL_ERROR, "Rx llsk-iuh unknown primitive %u (len=%u)\n", iuh->hdr.primitive, len);
+		return -EINVAL;
+	}
+}
diff --git a/src/osmo-hnodeb/main.c b/src/osmo-hnodeb/main.c
index 12d98ff..b8a883b 100644
--- a/src/osmo-hnodeb/main.c
+++ b/src/osmo-hnodeb/main.c
@@ -265,6 +265,13 @@
 		exit(1);
 	}
 
+	/* Start listening on lower layer unix domain socket: */
+	rc = llsk_open(g_hnb->llsk.state, g_hnb->llsk.sock_path);
+	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/vty.c b/src/osmo-hnodeb/vty.c
index e11fc2d..1331e59 100644
--- a/src/osmo-hnodeb/vty.c
+++ b/src/osmo-hnodeb/vty.c
@@ -47,6 +47,10 @@
 		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 @@
 	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_talloc_replace_string(g_hnb, &g_hnb->llsk.sock_path, argv[0]);
+
+	/* FIXME: re-open the interface? */
+	return CMD_SUCCESS;
+}
+
 
 static int config_write_hnodeb(struct vty *vty)
 {
@@ -266,6 +300,8 @@
 		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", g_hnb->llsk.sock_path, VTY_NEWLINE);
 	return CMD_SUCCESS;
 }
 
@@ -387,6 +423,9 @@
 	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(&hnb_register_cmd);

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-hnodeb/+/26357
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-hnodeb
Gerrit-Branch: master
Gerrit-Change-Id: Icaabb2206d6f141d4fba47dedf71f8ec37e6257d
Gerrit-Change-Number: 26357
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20211124/536b8c0b/attachment.htm>


More information about the gerrit-log mailing list