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/.
lynxis lazus gerrit-no-reply at lists.osmocom.orglynxis lazus has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/23758 ) Change subject: gprs_ns2: implement local update weight procedure ...................................................................... gprs_ns2: implement local update weight procedure When changing the bind ip-sns weight, initiate a SNS UPDATE WEIGHT procedure to inform the other side. Related: OS#5036 Change-Id: If034ac371a604bab5e58beadb784382c8b97cca3 --- M src/gb/gprs_ns2.c M src/gb/gprs_ns2_internal.h M src/gb/gprs_ns2_sns.c M src/gb/gprs_ns2_vty.c M tests/gb/gprs_ns2_vty.vty 5 files changed, 298 insertions(+), 10 deletions(-) git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/58/23758/1 diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c index 1148d6f..2f8396e 100644 --- a/src/gb/gprs_ns2.c +++ b/src/gb/gprs_ns2.c @@ -1400,6 +1400,7 @@ nsi->timeout[NS_TOUT_TSNS_PROV] = 3; /* 1..10 */ nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES] = 3; nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES] = 3; + nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES] = 3; return nsi; } diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h index 70e212a..8b02a88 100644 --- a/src/gb/gprs_ns2_internal.h +++ b/src/gb/gprs_ns2_internal.h @@ -46,8 +46,8 @@ struct gprs_ns2_vc_driver; struct gprs_ns2_vc_bind; -#define NS_TIMERS_COUNT 10 -#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries)" +#define NS_TIMERS_COUNT 11 +#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries)" #define NS_TIMERS_HELP \ "(un)blocking Timer (Tns-block) timeout\n" \ "(un)blocking Timer (Tns-block) number of retries\n" \ @@ -59,6 +59,7 @@ "SNS Provision Timer (Tsns-prov) timeout\n" \ "SNS Size number of retries\n" \ "SNS Config number of retries\n" \ + "SNS Procedures number of retries\n" \ /* Educated guess - LLC user payload is 1500 bytes plus possible headers */ #define NS_ALLOC_SIZE 3072 @@ -75,6 +76,7 @@ NS_TOUT_TSNS_PROV, NS_TOUT_TSNS_SIZE_RETRIES, NS_TOUT_TSNS_CONFIG_RETRIES, + NS_TOUT_TSNS_PROCEDURES_RETRIES, }; enum nsvc_timer_mode { diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c index c6e80af..a19171f 100644 --- a/src/gb/gprs_ns2_sns.c +++ b/src/gb/gprs_ns2_sns.c @@ -75,6 +75,7 @@ GPRS_SNS_ST_CONFIGURED, GPRS_SNS_ST_SGSN_WAIT_CONFIG, /* !< SGSN role: Wait for CONFIG from BSS */ GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, /* !< SGSN role: Wait for CONFIG-ACK from BSS */ + GPRS_SNS_ST_LOCAL_PROCEDURE, /*!< in process of a ADD/DEL/UPDATE procedure towards SGSN (BSS->SGSN) */ }; enum gprs_sns_event { @@ -92,6 +93,7 @@ GPRS_SNS_EV_REQ_NSVC_ALIVE, /*!< a NS-VC became alive */ GPRS_SNS_EV_REQ_ADD_BIND, /*!< add a new local bind to this NSE */ GPRS_SNS_EV_REQ_DELETE_BIND, /*!< remove a local bind from this NSE */ + GPRS_SNS_EV_REQ_CHANGE_WEIGHT, /*!< a bind changed its weight */ }; static const struct value_string gprs_sns_event_names[] = { @@ -109,17 +111,40 @@ { GPRS_SNS_EV_REQ_NSVC_ALIVE, "REQ_NSVC_ALIVE"}, { GPRS_SNS_EV_REQ_ADD_BIND, "REQ_ADD_BIND"}, { GPRS_SNS_EV_REQ_DELETE_BIND, "REQ_DELETE_BIND"}, + { GPRS_SNS_EV_REQ_CHANGE_WEIGHT, "REQ_UPDATE_WEIGHT"}, { 0, NULL } }; +enum sns_procedure { + SNS_NONE, /*!< used as invalid/idle value */ + SNS_ADD, + SNS_DEL, + SNS_CHANGE_WEIGHT, +}; + +enum sns_bind_flag { + SNS_BIND_CHANGE_REQ, /*!< need to change */ + SNS_BIND_CHANGE_IN_PROGRESS, /*!< change is going on */ +}; + struct sns_endpoint { struct llist_head list; struct osmo_sockaddr saddr; }; +struct ns2_sns_procedure { + struct llist_head list; + struct ns2_sns_bind *bind; + struct gprs_ns_ie_ip4_elem *ip4; + struct gprs_ns_ie_ip6_elem *ip6; + enum sns_procedure procedure; + uint8_t trans_id; +}; + struct ns2_sns_bind { struct llist_head list; struct gprs_ns2_vc_bind *bind; + uint8_t change_weight_state; }; struct ns2_sns_state { @@ -170,14 +195,46 @@ /* remote configuration as received */ struct gprs_ns_ie_ip6_elem *ip6_remote; unsigned int num_ip6_remote; + + struct ns2_sns_procedure current_procedure; }; +static void ns2_procedure_set_endpoint(struct ns2_sns_state *gss); + static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi) { struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv; return gss->nse; } +static struct gprs_ns_ie_ip4_elem *ip4_elem_by_saddr(struct gprs_ns_ie_ip4_elem *ip4, size_t num, const struct osmo_sockaddr *saddr) +{ + if (saddr->u.sa.sa_family != AF_INET) + return NULL; + + for (size_t i = 0; i < num; i++) { + if (ip4[i].ip_addr == saddr->u.sin.sin_addr.s_addr && + ip4->udp_port == saddr->u.sin.sin_port) + return &ip4[i]; + } + + return NULL; +} + +static struct gprs_ns_ie_ip6_elem *ip6_elem_by_saddr(struct gprs_ns_ie_ip6_elem *ip6, size_t num, const struct osmo_sockaddr *saddr) +{ + if (saddr->u.sa.sa_family != AF_INET6) + return NULL; + + for (size_t i = 0; i < num; i++) { + if (memcmp(&ip6[i].ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr)) == 0 && + ip6->udp_port == saddr->u.sin6.sin6_port) + return &ip6[i]; + } + + return NULL; +} + /* helper function to compute the sum of all (data or signaling) weights */ static int ip4_weight_sum(const struct gprs_ns_ie_ip4_elem *ip4, unsigned int num, bool data_weight) @@ -298,6 +355,11 @@ osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_NO_NSVC, NULL); } +static void ns2_clear_procedures(struct ns2_sns_state *gss) +{ + memset(&gss->current_procedure, 0, sizeof(struct ns2_sns_state)); +} + static void ns2_clear_ipv46_entries_local(struct ns2_sns_state *gss) { TALLOC_FREE(gss->ip4_local); @@ -896,6 +958,7 @@ if (old_state != GPRS_SNS_ST_BSS_SIZE) gss->N = 0; + ns2_clear_procedures(gss); gss->alive = false; ns2_sns_compute_local_ep_from_binds(fi); @@ -1372,7 +1435,102 @@ if (gss->sns_nsvc->sns_only) gprs_ns2_free_nsvc(gss->sns_nsvc); - ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED); + if (old_state != GPRS_SNS_ST_LOCAL_PROCEDURE) + ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED); +} + +static void ns2_sns_st_local_procedure_onenter(struct osmo_fsm_inst *fi, uint32_t old_state) +{ + struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv; + struct ns2_sns_bind *sns_bind; + + if (gss->current_procedure.procedure == SNS_NONE) { + gss->N = 0; + /* select the next procedure */ + llist_for_each_entry(sns_bind, &gss->binds, list) { + if (sns_bind->change_weight_state & S(SNS_BIND_CHANGE_REQ)) { + sns_bind->change_weight_state = 0; + gss->current_procedure.bind = sns_bind; + gss->current_procedure.procedure = SNS_CHANGE_WEIGHT; + ns2_procedure_set_endpoint(gss); + break; + } + } + + if (gss->current_procedure.procedure == SNS_NONE) { + /* nothing to do */ + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0); + return; + } + gss->current_procedure.trans_id++; + if (gss->current_procedure.trans_id == 0) + gss->current_procedure.trans_id = 1; + } + + /* also takes care of retransmitting */ + switch (gss->current_procedure.procedure) { + case SNS_ADD: + if (gss->ip == IPv4) + ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure.trans_id, gss->current_procedure.ip4, 1, NULL, 0); + else + ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure.trans_id, NULL, 0, gss->current_procedure.ip6, 1); + break; + case SNS_CHANGE_WEIGHT: + if (gss->ip == IPv4) + ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure.trans_id, gss->current_procedure.ip4, 1, NULL, 0); + else + ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure.trans_id, NULL, 0, gss->current_procedure.ip6, 1); + break; + case SNS_DEL: + if (gss->ip == IPv4) + ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure.trans_id, gss->current_procedure.ip4, 1, NULL, 0); + else + ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure.trans_id, NULL, 0, gss->current_procedure.ip6, 1); + break; + default: + break; + } +} + +static void ns2_sns_st_local_procedure(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv; + struct gprs_ns2_nse *nse = nse_inst_from_fi(fi); + struct gprs_ns2_inst *nsi = nse->nsi; + struct tlv_parsed *tp = data; + uint8_t trans_id; + + switch (event) { + case GPRS_SNS_EV_RX_ADD: + ns2_sns_st_configured_add(fi, gss, tp); + break; + case GPRS_SNS_EV_RX_DELETE: + ns2_sns_st_configured_delete(fi, gss, tp); + break; + case GPRS_SNS_EV_RX_CHANGE_WEIGHT: + ns2_sns_st_configured_change(fi, gss, tp); + break; + case GPRS_SNS_EV_RX_ACK: + /* presence of trans_id is already checked here */ + trans_id = tlvp_val8(tp, NS_IE_TRANS_ID, 0); + if (trans_id != gss->current_procedure.trans_id) { + LOGPFSML(fi, LOGL_DEBUG, "NSEI=%u Rx SNS ACK with invalid transaction id %d. Valid %d\n", + nse->nsei, trans_id, gss->current_procedure.trans_id); + break; + } + + if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) { + gss->current_procedure.procedure = SNS_NONE; + /* everything ok, local_procedure_onenter() will check if there are more procedures required */ + + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, + nsi->timeout[NS_TOUT_TSNS_PROV], GPRS_SNS_ST_LOCAL_PROCEDURE); + return; + } else { + /* what happend on error cause? return to size? */ + } + break; + } } static const struct osmo_fsm_state ns2_sns_bss_states[] = { @@ -1419,11 +1577,27 @@ S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) | S(GPRS_SNS_EV_REQ_NSVC_ALIVE), .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) | - S(GPRS_SNS_ST_BSS_SIZE), + S(GPRS_SNS_ST_BSS_SIZE) | + S(GPRS_SNS_ST_LOCAL_PROCEDURE), .name = "CONFIGURED", .action = ns2_sns_st_configured, .onenter = ns2_sns_st_configured_onenter, }, + [GPRS_SNS_ST_LOCAL_PROCEDURE] = { + .in_event_mask = S(GPRS_SNS_EV_RX_ADD) | + S(GPRS_SNS_EV_RX_DELETE) | + S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) | + S(GPRS_SNS_EV_RX_ACK) | + S(GPRS_SNS_EV_REQ_NSVC_ALIVE), + .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) | + S(GPRS_SNS_ST_BSS_SIZE) | + S(GPRS_SNS_ST_CONFIGURED) | + S(GPRS_SNS_ST_LOCAL_PROCEDURE), + .name = "LOCAL_PROCEDURE", + .action = ns2_sns_st_local_procedure, + .onenter = ns2_sns_st_local_procedure_onenter, + }, + }; static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi) @@ -1462,6 +1636,14 @@ LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.\n", nse->nsei); osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL); break; + case GPRS_SNS_ST_LOCAL_PROCEDURE: + if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) { + LOGPFSML(fi, LOGL_ERROR, "NSE %d: Procedure retries failed. Restarting NSE.\n", nse->nsei); + osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL); + } else { + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], GPRS_SNS_ST_LOCAL_PROCEDURE); + } + break; } return 0; } @@ -1518,6 +1700,21 @@ /* if this is the last bind, the free_nsvc() will trigger a reselection */ talloc_free(sbind); break; + case GPRS_SNS_EV_REQ_CHANGE_WEIGHT: + sbind = data; + switch (fi->state) { + case GPRS_SNS_ST_UNCONFIGURED: + case GPRS_SNS_ST_BSS_SIZE: + /* unconfigured or size don't need a procedure */ + break; + /* all other states */ + default: + /* change the flag */ + sbind->change_weight_state |= S(SNS_BIND_CHANGE_REQ); + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], GPRS_SNS_ST_LOCAL_PROCEDURE); + break; + } + break; } } @@ -1577,6 +1774,7 @@ .allstate_event_mask = S(GPRS_SNS_EV_REQ_NO_NSVC) | S(GPRS_SNS_EV_REQ_SELECT_ENDPOINT) | S(GPRS_SNS_EV_REQ_ADD_BIND) | + S(GPRS_SNS_EV_REQ_CHANGE_WEIGHT) | S(GPRS_SNS_EV_REQ_DELETE_BIND), .allstate_action = ns2_sns_st_all_action_bss, .cleanup = NULL, @@ -1911,7 +2109,7 @@ return; gss = nse->bss_sns_fi->priv; - if(nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED) + if(nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED && nse->bss_sns_fi->state != GPRS_SNS_ST_LOCAL_PROCEDURE) return; if (alive == gss->alive) @@ -2005,12 +2203,71 @@ return 0; } -/* Update SNS weights - * \param[in] nsvc the NSVC which should be updated +/* Update SNS weights for a bind (local endpoint). */ +static void ns2_procedure_set_endpoint(struct ns2_sns_state *gss) { + struct osmo_sockaddr *local = NULL, *remote = NULL; + const struct osmo_sockaddr *sa; + struct gprs_ns_ie_ip4_elem *ip4; + struct gprs_ns_ie_ip6_elem *ip6; + + /* TODO: ensure this bind is already added! */ + OSMO_ASSERT(gss->current_procedure.procedure != SNS_NONE); + sa = gprs_ns2_ip_bind_sockaddr(gss->current_procedure.bind->bind); + if (!sa) + return; + + switch (sa->u.sa.sa_family) { + case AF_INET: + if (sa->u.sin.sin_addr.s_addr == 0) { + if (osmo_sockaddr_local_ip(local, &gss->initial->saddr)) + return; + ip4 = ip4_elem_by_saddr(gss->ip4_local, gss->num_ip4_local, local); + } else { + ip4 = ip4_elem_by_saddr(gss->ip4_local, gss->num_ip4_local, sa); + } + + /* TODO: check if this bind is about to be added */ + OSMO_ASSERT(ip4); + gss->current_procedure.ip4 = ip4; + break; + case AF_INET6: + // in6addr_any + if (memcmp(&sa->u.sin6.sin6_addr, &in6addr_any, sizeof(struct in6_addr))) { + if (osmo_sockaddr_local_ip(local, remote)) + return; + ip6 = ip6_elem_by_saddr(gss->ip6_local, gss->num_ip6_local, local); + } else { + ip6 = ip6_elem_by_saddr(gss->ip6_local, gss->num_ip6_local, sa); + } + OSMO_ASSERT(ip6); + gss->current_procedure.ip6 = ip6; + break; + default: + OSMO_ASSERT(false); + break; + } +} + +/* Update SNS weights for a bind (local endpoint). + * \param[in] bind the bind which has been updated */ void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind) { - /* TODO: implement weights after binds per sns implemented */ + struct ns2_sns_bind *sns_bind; + struct gprs_ns2_nse *nse; + struct ns2_sns_state *gss; + llist_for_each_entry(nse, &bind->nsi->nse, list) { + if (!nse->bss_sns_fi) + continue; + + gss = nse->bss_sns_fi->priv; + llist_for_each_entry(sns_bind, &gss->binds, list) { + if (sns_bind->bind == bind) { + osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, GPRS_SNS_EV_REQ_CHANGE_WEIGHT, sns_bind); + break; + } + } + } } @@ -2145,6 +2402,22 @@ .action = ns2_sns_st_configured, .onenter = ns2_sns_st_configured_onenter, }, + [GPRS_SNS_ST_LOCAL_PROCEDURE] = { + .in_event_mask = S(GPRS_SNS_EV_RX_ADD) | + S(GPRS_SNS_EV_RX_DELETE) | + S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) | + S(GPRS_SNS_EV_RX_ACK) | + S(GPRS_SNS_EV_REQ_CHANGE_WEIGHT) | + S(GPRS_SNS_EV_REQ_NSVC_ALIVE), + .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) | + S(GPRS_SNS_ST_BSS_SIZE) | + S(GPRS_SNS_ST_CONFIGURED) | + S(GPRS_SNS_ST_LOCAL_PROCEDURE), + .name = "LOCAL_PROCEDURE", + /* shared with BSS side; once configured there's no difference */ + .action = ns2_sns_st_local_procedure, + .onenter = ns2_sns_st_local_procedure_onenter, + }, }; static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi) @@ -2166,11 +2439,20 @@ case 4: LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online.\n", nse->nsei); break; + case GPRS_SNS_ST_LOCAL_PROCEDURE: + if (gss->N >= nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES]) { + LOGPFSML(fi, LOGL_ERROR, "NSE %d: SNS Procedure retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei); + osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL); + } else { + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], + fi->T); + } + break; } + return 0; } - /* allstate-action for SGSN role */ static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { @@ -2238,6 +2520,7 @@ /* clear all state */ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0); gss->N = 0; + ns2_clear_procedures(gss); ns2_clear_ipv46_entries_local(gss); ns2_clear_ipv46_entries_remote(gss); llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) { @@ -2272,6 +2555,7 @@ .allstate_event_mask = S(GPRS_SNS_EV_RX_SIZE) | S(GPRS_SNS_EV_REQ_NO_NSVC) | S(GPRS_SNS_EV_REQ_ADD_BIND) | + S(GPRS_SNS_EV_REQ_CHANGE_WEIGHT) | S(GPRS_SNS_EV_REQ_DELETE_BIND), .allstate_action = ns2_sns_st_all_action_sgsn, .cleanup = NULL, diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c index b678db4..169b891 100644 --- a/src/gb/gprs_ns2_vty.c +++ b/src/gb/gprs_ns2_vty.c @@ -100,6 +100,7 @@ { 7, "tsns-prov" }, { 8, "tsns-size-retries" }, { 9, "tsns-config-retries" }, + {10, "tsns-procedures-retries" }, { 0, NULL } }; diff --git a/tests/gb/gprs_ns2_vty.vty b/tests/gb/gprs_ns2_vty.vty index 78c7e78..1c78069 100644 --- a/tests/gb/gprs_ns2_vty.vty +++ b/tests/gb/gprs_ns2_vty.vty @@ -17,7 +17,7 @@ OsmoNSdummy(config)# ns OsmoNSdummy(config-ns)# list ... - timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries) <0-65535> + timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries) <0-65535> nse <0-65535> [ip-sns-role-sgsn] no nse <0-65535> bind (fr|udp) ID -- To view, visit https://gerrit.osmocom.org/c/libosmocore/+/23758 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmocore Gerrit-Branch: master Gerrit-Change-Id: If034ac371a604bab5e58beadb784382c8b97cca3 Gerrit-Change-Number: 23758 Gerrit-PatchSet: 1 Gerrit-Owner: lynxis lazus <lynxis at fe80.eu> Gerrit-MessageType: newchange -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210414/f0284142/attachment.htm>