diff options
-rw-r--r-- | src/hnbgw.c | 97 | ||||
-rw-r--r-- | src/hnbgw.h | 42 | ||||
-rw-r--r-- | src/hnbgw_hnbap.c | 47 |
3 files changed, 176 insertions, 10 deletions
diff --git a/src/hnbgw.c b/src/hnbgw.c new file mode 100644 index 0000000..fc26d0d --- /dev/null +++ b/src/hnbgw.c @@ -0,0 +1,97 @@ + +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/linuxlist.h> + +#include "hnbgw.h" + +struct hnb_gw g_hnb_gw = { + .config = { + .iuh_listen_port = IUH_DEFAULT_SCTP_PORT, + }, +}; + +static int hnb_socket_cb(struct osmo_fd *fd, unsigned int what) +{ + struct hnb_context *hnb = fd->data; + struct sctp_sndrcvinfo sinfo; + struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx"); + int flags; + int rc; + + if (!msg) + return -ENOMEM; + + rc = sctp_recvmsg(fd->fd, msgb_data(msg), msgb_tailroom(msg), + NULL, NULL, &sinfo, &flags); + if (rc < 0) { + LOGP(DMAIN, LOGL_ERROR, "Error during sctp_recvmsg()\n"); + return rc; + } else + msgb_put(msg, rc); + + switch (sinfo.sinfo_ppid) { + case IUH_PPI_HNBAP: + rc = hnbgw_hnbap_rx(hnb, msg); + break; + case IUH_PPI_RUA: + rc = hnbgw_rua_rx(hnb, msg); + break; + case IUH_PPI_SABP: + case IUH_PPI_RNA: + case IUH_PPI_PUA: + LOGP(DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%u received\n", + sinfo.sinfo_ppid); + rc = 0; + break; + default: + LOGP(DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%u received\n", + sinfo.sinfo_ppid); + rc = 0; + break; + } + + return rc; +} + +/*! call-back when the listen FD has something to read */ +static int listen_fd_cb(struct osmo_fd *fd, unsigned int what) +{ + struct hnb_gw *gw = fd->data; + struct hmb_context *ctx; + struct sokaddr_storage sockaddr; + socklen_t len = sizeof(sockaddr); + + int new_fd = accept(fd->fd, (struct sockaddr *)&sockaddr, &len); + if (new_fd < 0) { + LOGP(DMAIN, LOGL_ERROR, "Iuh accept() failed\n"); + return new_fd; + } + + LOGP(DMAIN, LOGL_INFO, "SCTP Connection accept()ed\n"); + + ctx = talloc_zero(tall_hnb_ctx, struct hnb_context); + if (!ctx) + return -ENOMEM; + + ctx->gw = gw; + ctx->socket.data = ctx; + ctx->socket.fd = new_fd; + ctx->socket.when = BSC_FD_READ; + ctx->socket.cb = hnb_socket_cb; + osmo_fd_register(&cttx->socket); + + return 0; +} + + + +{ + g_hnb_gw.listen_fd.cb = listen_fd_cb; + g_hnb_gw.listen_fd.when = BSC_FD_READ; + g_hnb_gw.listen_fd.data = &g_hnb_gw; + + osmo_sock_init_ofd(&g_hnb_gw.listen_fd, AF_INET, SOCK_STREAM, + IPPROTO_SCTP, "127.0.0.1", + g_hnb_gw.config.iuh_listen_port, OSMO_SOCK_F_BIND); +} diff --git a/src/hnbgw.h b/src/hnbgw.h index 87a5a2f..7b69a10 100644 --- a/src/hnbgw.h +++ b/src/hnbgw.h @@ -3,27 +3,63 @@ #include <osmocom/core/select.h> #include <osmocom/core/linuxlist.h> +/* 25.467 Section 7.1 */ +#define IUH_DEFAULT_SCTP_PORT 29169 +#define RNA_DEFAULT_SCTP_PORT 25471 + +#define IUH_PPI_RUA 19 +#define IUH_PPI_HNBAP 20 +#define IUH_PPI_SABP 31 +#define IUH_PPI_RNA 42 +#define IUH_PPI_PUA 55 + +#define IHU_MSGB_SIZE 2048 + +struct umts_cell_id { + uint16_t mcc; /*!< Mobile Country Code */ + uint16_t mnc; /*!< Mobile Network Code */ + uint16_t lac; /*!< Locaton Area Code */ + uint16_t rac; /*!< Routing Area Code */ + uint16_t sac; /*!< Service Area Code */ + uint32_t cid; /*!< Cell ID */ +}; + +struct hnb_gw; + struct hnb_context { /*! Entry in HNB-global list of HNB */ struct llist_head list; + /*! HNB-GW we are part of */ + struct hnb_gw *gw; /*! SCTP socket for Iuh to this specific HNB */ struct osmo_fd socket; - - /*! copied from HNB-Identity-Info */ - char identity[256]; + /*! copied from HNB-Identity-Info IE */ + char identity_info[256]; + /*! copied from Cell Identity IE */ + struct umts_cell_id id; }; struct ue_context { + /*! Entry in the HNB-global list of UE */ struct llist_head list; + /*! Unique Context ID for this UE */ uint32_t context_id; + /*! UE is serviced via this HNB */ + struct hnb_context *hnb; }; struct hnb_gw { struct { /*! SCTP port for Iuh listening */ uint16_t iuh_listen_port; + /*! The UDP port where we receive multiplexed CS user + * plane traffic from HNBs */ + uint16_t iuh_cs_mux_port; + uint16_t rnc_id; } config; /*! SCTP listen socket for incoming connections */ struct osmo_fd listen_fd; struct llist_head hnb_list; }; + +extern struct hnb_gw g_hnb_gw; diff --git a/src/hnbgw_hnbap.c b/src/hnbgw_hnbap.c index 9b674ad..ce41222 100644 --- a/src/hnbgw_hnbap.c +++ b/src/hnbgw_hnbap.c @@ -29,7 +29,31 @@ struct ProtocolIE_Field_1 *find_ie(const struct ProtocolIE_Container_1 *cont, Pr return NULL; } -static int hnbgw_rx_hnb_register_req(struct HNBRegisterRequest *req) +static inline uint16_t asn1str_to_u16(ASN1String *as) +{ + if (as->len < 2) + return 0; + else + return *(uint16_t *)as->buf; +} + +static inline uint8_t asn1str_to_u8(ASN1String *as) +{ + if (as->len < 1) + return 0; + else + return *(uint8_t *)as->buf; +} + +static inline uint8_t asn1bitstr_to_u32(ASN1BitString *as) +{ + if (as->len < 25) + return 0; + else + return *(uint32_t *)as->buf; +} + +static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, struct HNBRegisterRequest *req) { HNB_Identity *identity = FIND_IE(req->protocolIEs, HNBAP_IEI_HNB_Identity); @@ -47,10 +71,19 @@ static int hnbgw_rx_hnb_register_req(struct HNBRegisterRequest *req) if(!identity || !loc || !plmn_id || !cell_id || !lac || !rac || !sac) return -1; + /* copy all identity parameters from the message to ctx */ + strncpy(ctx->identity_info, sizeof(ctx->identity_info, identity_info->buf); + ctx->id.lac = asn1str_to_u16(lac); + ctx->id.sac = asn1str_to_u16(sac); + ctx->id.rac = asn1str_to_u8(rac); + ctx->id.cid = asn1bitstr_to_u32(cell_id); + ctx->id.mcc FIXME + ctx->id.mnc FIXME + /* FIXME: Send HNBRegisterAccept */ } -static int hnbgw_rx_ue_register_req(struct UERegisterRequest *req) +static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, struct UERegisterRequest *req) { UE_Identity *id = FIND_IE(req->protocolIEs, HNBAP_IEI_UE_Identity); @@ -65,7 +98,7 @@ static int hnbgw_rx_ue_register_req(struct UERegisterRequest *req) /* FIXME: Send UERegisterAccept */ } -static int hnbgw_rx_initiating_msg(struct InitiatingMessage *msg) +static int hnbgw_rx_initiating_msg(struct hnb_context *hnb, struct InitiatingMessage *msg) { int rc; @@ -73,14 +106,14 @@ static int hnbgw_rx_initiating_msg(struct InitiatingMessage *msg) case HNBAP_PC_HNBRegister: /* 8.2 */ if (msg->value.type != asn1_type_HNBRegisterRequest) return -1; - rc = hnbgw_rx_hnb_register_req(); + rc = hnbgw_rx_hnb_register_req(hnb, FIXME); break; case HNBAP_PC_HNBDe_Register: /* 8.3 */ break; case HNBAP_PC_UERegister: /* 8.4 */ if (msg->value.type != asn1_type_UERegisterRequest) return -1; - rc = hnbgw_rx_ue_register_req(); + rc = hnbgw_rx_ue_register_req(hnb, FIXME); break; case HNBAP_PC_UEDe_Register: /* 8.5 */ break; @@ -107,7 +140,7 @@ static int hnbgw_rx_unsuccessful_outcome_msg(struct UnsuccessfulOutcome *msg) } -static int _hnbgw_hnbap_rx(struct HNBAP_PDU *pdu) +static int _hnbgw_hnbap_rx(struct hnb_context *hnb, struct HNBAP_PDU *pdu) { /* it's a bit odd that we can't dispatch on procedure code, but * that's not possible */ @@ -126,7 +159,7 @@ static int _hnbgw_hnbap_rx(struct HNBAP_PDU *pdu) } } -int hnbgw_hnbap_rx(struct msgb *msg) +int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg) { /* FIXME: decode and handle to _hnbgw_hnbap_rx() */ } |