diff --git a/include/osmocom/iuh/hnbgw.h b/include/osmocom/iuh/hnbgw.h index 094f31f9..b79bcc16 100644 --- a/include/osmocom/iuh/hnbgw.h +++ b/include/osmocom/iuh/hnbgw.h @@ -97,6 +97,10 @@ struct hnb_context { /*! SCTP stream ID for RUA */ uint16_t rua_stream; + /*! True if a HNB-REGISTER-REQ from this HNB has been accepted. Note that + * this entire data structure is freed if the HNB sends HNB-DE-REGISTER-REQ. */ + bool hnb_registered; + /* linked list of hnbgw_context_map */ struct llist_head map_list; }; @@ -157,7 +161,7 @@ struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi, void ue_context_free(struct ue_context *ue); struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_link *link, int new_fd); -void hnb_context_release(struct hnb_context *ctx); +void hnb_context_release(struct hnb_context *ctx, bool destroy_conn); void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx); int hnbgw_vty_go_parent(struct vty *vty); diff --git a/src/hnbgw.c b/src/hnbgw.c index cd492bb2..94d8fb94 100644 --- a/src/hnbgw.c +++ b/src/hnbgw.c @@ -204,6 +204,12 @@ void ue_context_free(struct ue_context *ue) } static int hnb_close_cb(struct osmo_stream_srv *conn) { + struct hnb_context *hnb = osmo_stream_srv_get_data(conn); + + /* This connection is about to be closed. Destroy the HNB context now. */ + if (hnb) + hnb_context_release(hnb, false); + return 0; } @@ -228,10 +234,10 @@ static int hnb_read_cb(struct osmo_stream_srv *conn) } else if (rc < 0) { LOGP(DMAIN, LOGL_ERROR, "Error during sctp_recvmsg()\n"); /* FIXME: clean up after disappeared HNB */ - hnb_context_release(hnb); + hnb_context_release(hnb, true); goto out; } else if (rc == 0) { - hnb_context_release(hnb); + hnb_context_release(hnb, true); rc = -1; goto out; @@ -288,7 +294,7 @@ struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_ return ctx; } -void hnb_context_release(struct hnb_context *ctx) +void hnb_context_release(struct hnb_context *ctx, bool destroy_conn) { struct hnbgw_context_map *map, *map2; @@ -305,7 +311,9 @@ void hnb_context_release(struct hnb_context *ctx) context_map_deactivate(map); } ue_context_free_by_hnb(ctx->gw, ctx); - osmo_stream_srv_destroy(ctx->conn); + + if (destroy_conn) + osmo_stream_srv_destroy(ctx->conn); talloc_free(ctx); } diff --git a/src/hnbgw_hnbap.c b/src/hnbgw_hnbap.c index 8fba13c7..acc5affc 100644 --- a/src/hnbgw_hnbap.c +++ b/src/hnbgw_hnbap.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -48,6 +49,45 @@ static int hnbgw_hnbap_tx(struct hnb_context *ctx, struct msgb *msg) return 0; } +static int hnbgw_tx_hnb_register_rej(struct hnb_context *ctx) +{ + HNBRegisterReject_t reject_out; + HNBRegisterRejectIEs_t reject; + struct msgb *msg; + int rc; + + reject.presenceMask = 0, + reject.cause.present = Cause_PR_radioNetwork; + reject.cause.choice.radioNetwork = CauseRadioNetwork_unspecified; + + /* encode the Information Elements */ + memset(&reject_out, 0, sizeof(reject_out)); + rc = hnbap_encode_hnbregisterrejecties(&reject_out, &reject); + if (rc < 0) { + LOGP(DHNBAP, LOGL_ERROR, "Failure to encode HNB-REGISTER-REJECT to %s: rc=%d\n", + ctx->identity_info, rc); + return rc; + } + + /* generate a successfull outcome PDU */ + msg = hnbap_generate_unsuccessful_outcome(ProcedureCode_id_HNBRegister, + Criticality_reject, + &asn_DEF_HNBRegisterReject, + &reject_out); + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBRegisterReject, &reject_out); + + rc = hnbgw_hnbap_tx(ctx, msg); + if (rc == 0) { + /* Tell libosmo-netif to destroy this connection when it is done + * sending our HNB-REGISTER-REJECT response. */ + osmo_stream_srv_set_flush_and_destroy(ctx->conn); + } else { + /* The message was not queued. Destroy the connection right away. */ + hnb_context_release(ctx, true); + } +} + static int hnbgw_tx_hnb_register_acc(struct hnb_context *ctx) { HNBRegisterAccept_t accept_out; @@ -361,13 +401,14 @@ static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in) hnbap_cause_str(&ies.cause)); hnbap_free_hnbde_registeries(&ies); - hnb_context_release(ctx); + hnb_context_release(ctx, true); return 0; } static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in) { + struct hnb_context *hnb; HNBRegisterRequestIEs_t ies; int rc; @@ -387,6 +428,20 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in) ctx->id.cid = asn1bitstr_to_u28(&ies.cellIdentity); gsm48_mcc_mnc_from_bcd(ies.plmNidentity.buf, &ctx->id.mcc, &ctx->id.mnc); + llist_for_each_entry(hnb, &ctx->gw->hnb_list, list) { + if (hnb->hnb_registered && ctx != hnb && memcmp(&ctx->id, &hnb->id, sizeof(ctx->id)) == 0) { + struct osmo_fd *ofd = osmo_stream_srv_get_ofd(ctx->conn); + char *name = osmo_sock_get_name(ctx, ofd->fd); + LOGP(DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity " + "MCC=%u,MNC=%u,LAC=%u,RAC=%u,SAC=%u,CID=%u from %s\n", + ctx->id.mcc, ctx->id.mnc, ctx->id.lac, ctx->id.rac, ctx->id.sac, ctx->id.cid, name); + talloc_free(name); + return hnbgw_tx_hnb_register_rej(ctx); + } + } + + ctx->hnb_registered = true; + DEBUGP(DHNBAP, "HNB-REGISTER-REQ from %s\n", ctx->identity_info); /* Send HNBRegisterAccept */