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/+/24123 ) Change subject: gprs_ns2: implement outbound ADD/DEL SNS procedures ...................................................................... gprs_ns2: implement outbound ADD/DEL SNS procedures When adding or removing a bind, the remote side needs to be informed via the SNS-ADD/SNS-DELETE procedure. Related: OS#5036 Change-Id: I71c33200bd1f0307ceb943ee958db5ebe3623d36 --- M src/gb/gprs_ns2_sns.c 1 file changed, 246 insertions(+), 43 deletions(-) git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/23/24123/1 diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c index 7131974..accadac 100644 --- a/src/gb/gprs_ns2_sns.c +++ b/src/gb/gprs_ns2_sns.c @@ -124,6 +124,8 @@ enum sns_bind_flag { SNS_BIND_CHANGE_REQ, /*!< request to change weights */ + SNS_BIND_ADD_REQ, /*!< request to add */ + SNS_BIND_DEL_REQ, /*!< request to delete */ }; struct sns_endpoint { @@ -143,7 +145,7 @@ struct ns2_sns_bind { struct llist_head list; struct gprs_ns2_vc_bind *bind; - uint8_t change_weight_state; + uint8_t procedure_state; }; struct ns2_sns_state { @@ -213,7 +215,7 @@ 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) + ip4[i].udp_port == saddr->u.sin.sin_port) return &ip4[i]; } @@ -469,6 +471,10 @@ if (bind->ll != GPRS_NS2_LL_UDP) continue; + /* ignore binds which are about to be added or deleted */ + if (sbind->procedure_state & (S(SNS_BIND_ADD_REQ) | S(SNS_BIND_DEL_REQ))) + continue; + nsvc = nsvc_for_bind_and_remote(nse, bind, &remote); if (!nsvc) { nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0); @@ -500,6 +506,10 @@ if (bind->ll != GPRS_NS2_LL_UDP) continue; + /* ignore binds which are about to be added or deleted */ + if (sbind->procedure_state & (S(SNS_BIND_ADD_REQ) | S(SNS_BIND_DEL_REQ))) + continue; + /* we only care about UDP binds */ nsvc = nsvc_for_bind_and_remote(nse, bind, &remote); if (!nsvc) { @@ -521,6 +531,49 @@ return 0; } +/* Add a given local IPv4 element to gprs_sns_state */ +static int add_local_ip4_elem(struct ns2_sns_state *gss, uint32_t ip_addr, uint16_t port, uint16_t sig_weight, uint16_t data_weight) +{ + unsigned int i; + + if (gss->num_ip4_local + gss->num_ip4_remote >= gss->num_max_nsvcs) + return -NS_CAUSE_INVAL_NR_NS_VC; + + /* check for duplicates */ + for (i = 0; i < gss->num_ip4_local; i++) { + if (gss->ip4_local[i].ip_addr != ip_addr || gss->ip4_local[i].udp_port != port) + continue; + /* TODO: log message duplicate */ + /* TODO: check if this is the correct cause code */ + return -NS_CAUSE_PROTO_ERR_UNSPEC; + } + + gss->ip4_local = talloc_realloc(gss, gss->ip4_local, struct gprs_ns_ie_ip4_elem, + gss->num_ip4_local+1); + gss->ip4_local[gss->num_ip4_local].ip_addr = ip_addr; + gss->ip4_local[gss->num_ip4_local].udp_port = port; + gss->ip4_local[gss->num_ip4_local].sig_weight = sig_weight; + gss->ip4_local[gss->num_ip4_local].data_weight = data_weight; + gss->num_ip4_local += 1; + return 0; +} + +/* Remove a given local IPv4 element from gprs_sns_state */ +static int remove_local_ip4_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip4_elem *ip4) +{ + unsigned int i; + + for (i = 0; i < gss->num_ip4_local; i++) { + if (memcmp(&gss->ip4_local[i], ip4, sizeof(*ip4))) + continue; + /* all array elements < i remain as they are; all > i are shifted left by one */ + memmove(&gss->ip4_local[i], &gss->ip4_local[i+1], gss->num_ip4_local-i-1); + gss->num_ip4_local -= 1; + return 0; + } + return -1; +} + /* Add a given remote IPv4 element to gprs_sns_state */ static int add_remote_ip4_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip4_elem *ip4) { @@ -577,6 +630,49 @@ return -1; } +/* Add a given local IPv6 element to gprs_sns_state */ +static int add_local_ip6_elem(struct ns2_sns_state *gss, const struct in6_addr *ip_addr, uint16_t port, uint16_t sig_weight, uint16_t data_weight) +{ + unsigned int i; + + if (gss->num_ip6_local + gss->num_ip6_remote >= gss->num_max_nsvcs) + return -NS_CAUSE_INVAL_NR_NS_VC; + + /* check for duplicates */ + for (i = 0; i < gss->num_ip6_local; i++) { + if (memcmp(&gss->ip6_local[i].ip_addr, ip_addr, sizeof(struct in6_addr)) || gss->ip6_local[i].udp_port != port) + continue; + /* TODO: log message duplicate */ + /* TODO: check if this is the correct cause code */ + return -NS_CAUSE_PROTO_ERR_UNSPEC; + } + + gss->ip6_local = talloc_realloc(gss, gss->ip6_local, struct gprs_ns_ie_ip6_elem, + gss->num_ip6_local+1); + memcpy(&gss->ip6_local[gss->num_ip6_local].ip_addr, ip_addr, sizeof(struct in6_addr)); + gss->ip6_local[gss->num_ip6_local].udp_port = port; + gss->ip6_local[gss->num_ip6_local].sig_weight = sig_weight; + gss->ip6_local[gss->num_ip6_local].data_weight = data_weight; + gss->num_ip6_local += 1; + return 0; +} + +/* Remove a given local IPv4 element from gprs_sns_state */ +static int remove_local_ip6_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip6_elem *ip6) +{ + unsigned int i; + + for (i = 0; i < gss->num_ip6_local; i++) { + if (memcmp(&gss->ip6_local[i], ip6, sizeof(*ip6))) + continue; + /* all array elements < i remain as they are; all > i are shifted left by one */ + memmove(&gss->ip6_local[i], &gss->ip6_local[i+1], gss->num_ip6_local-i-1); + gss->num_ip6_local -= 1; + return 0; + } + return -1; +} + /* Add a given remote IPv6 element to gprs_sns_state */ static int add_remote_ip6_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip6_elem *ip6) { @@ -826,6 +922,9 @@ const struct osmo_sockaddr *sa = gprs_ns2_ip_bind_sockaddr(sbind->bind); if (!sa) continue; + /* ignore binds which will be added in the future via a procedure */ + if (sbind->procedure_state & S(SNS_BIND_ADD_REQ)) + continue; switch (stype) { case IPv4: @@ -864,7 +963,7 @@ remote = gprs_ns2_ip_vc_remote(gss->sns_nsvc); /* count how many bindings are available (only UDP binds) */ - count = llist_count(&gss->binds); + count = ns2_sns_count_num_local_ep(fi, gss->ip); if (count == 0) { LOGPFSML(fi, LOGL_ERROR, "No local binds for this NSE -> cannot determine IP endpoints\n"); return; @@ -879,6 +978,9 @@ gss->ip4_local = ip4_elems; llist_for_each_entry(sbind, &gss->binds, list) { bind = sbind->bind; + if (sbind->procedure_state & S(GPRS_SNS_EV_REQ_ADD_BIND)) + continue; + sa = gprs_ns2_ip_bind_sockaddr(bind); if (!sa) continue; @@ -915,6 +1017,9 @@ llist_for_each_entry(sbind, &gss->binds, list) { bind = sbind->bind; + if (sbind->procedure_state & S(GPRS_SNS_EV_REQ_ADD_BIND)) + continue; + sa = gprs_ns2_ip_bind_sockaddr(bind); if (!sa) continue; @@ -944,16 +1049,29 @@ } } +static void ns2_sns_clean_procedures(struct ns2_sns_state *gss) +{ + struct ns2_sns_bind *sbind, *tmp; + llist_for_each_entry_safe(sbind, tmp, &gss->binds, list) { + if (sbind->procedure_state & S(SNS_BIND_DEL_REQ)) { + llist_del(&sbind->list); + talloc_free(sbind); + } else { + sbind->procedure_state = 0; + } + } +} + static void ns2_sns_choose_next_bind(struct ns2_sns_state *gss) { /* take the first bind or take the next bind */ if (!gss->initial_bind) { - gss->initial_bind = llist_first_entry(&gss->binds, struct ns2_sns_bind, list); + gss->initial_bind = llist_first_entry_or_null(&gss->binds, struct ns2_sns_bind, list); } else { if (gss->initial_bind->list.next != &gss->binds) { gss->initial_bind = llist_entry(gss->initial_bind->list.next, struct ns2_sns_bind, list); } else { - gss->initial_bind = llist_first_entry(&gss->binds, struct ns2_sns_bind, list); + gss->initial_bind = llist_first_entry_or_null(&gss->binds, struct ns2_sns_bind, list); } } } @@ -963,6 +1081,7 @@ { struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv; + // TODO: check if bss is even valid ! */ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS); /* on a generic failure, the timer callback will recover */ @@ -975,6 +1094,7 @@ gss->alive = false; ns2_sns_compute_local_ep_from_binds(fi); + ns2_sns_clean_procedures(gss); ns2_sns_choose_next_bind(gss); /* setup the NSVC */ @@ -1420,9 +1540,11 @@ static void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state) { - struct gprs_ns2_vc *nsvc; 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 gprs_ns2_vc *nsvc; + struct ns2_sns_bind *sbind; /* NS-VC status updates are only parsed in ST_CONFIGURED. * Do an initial check if there are any nsvc alive atm */ llist_for_each_entry(nsvc, &nse->nsvc, list) { @@ -1439,31 +1561,54 @@ if (old_state != GPRS_SNS_ST_LOCAL_PROCEDURE) ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED); + + /* check if there are procedure waiting */ + llist_for_each_entry(sbind, &gss->binds, list) { + if (sbind->procedure_state) { + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, + nsi->timeout[NS_TOUT_TSNS_PROV], GPRS_SNS_ST_LOCAL_PROCEDURE); + break; + } + } } 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 gprs_ns2_nse *nse = nse_inst_from_fi(fi); struct ns2_sns_bind *sns_bind; if (gss->current_procedure.procedure == SNS_NONE) { gss->N = 0; /* select the next procedure */ + // TODO: first check for BIND_ADD_REQ, then UPDATE_WEIGHT, then DELETE 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; + if (sns_bind->procedure_state & S(SNS_BIND_ADD_REQ)) { + sns_bind->procedure_state = 0; + gss->current_procedure.procedure = SNS_ADD; gss->current_procedure.bind = sns_bind; + break; + } else if (sns_bind->procedure_state & S(SNS_BIND_CHANGE_REQ)) { + sns_bind->procedure_state = 0; gss->current_procedure.procedure = SNS_CHANGE_WEIGHT; - ns2_procedure_set_endpoint(gss); + gss->current_procedure.bind = sns_bind; + break; + } else if (sns_bind->procedure_state & S(SNS_BIND_DEL_REQ)) { + sns_bind->procedure_state = 0; + gss->current_procedure.procedure = SNS_DEL; + gss->current_procedure.bind = sns_bind; break; } } + // TODO: when add still set, take care of change weights at the same time (e.g. add a bind and change the weight at the same time) if (gss->current_procedure.procedure == SNS_NONE) { /* nothing to do */ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0); return; } + + ns2_procedure_set_endpoint(gss); gss->current_procedure.trans_id++; if (gss->current_procedure.trans_id == 0) gss->current_procedure.trans_id = 1; @@ -1476,6 +1621,8 @@ 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); + create_missing_nsvcs(fi); + gprs_ns2_start_alive_all_nsvcs(nse); break; case SNS_CHANGE_WEIGHT: if (gss->ip == IPv4) @@ -1500,7 +1647,7 @@ 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; + uint8_t trans_id, cause; switch (event) { case GPRS_SNS_EV_RX_ADD: @@ -1521,16 +1668,26 @@ 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 { + if (TLVP_PRESENT(tp, NS_IE_CAUSE)) { + cause = tlvp_val8(tp, NS_IE_CAUSE, 0); /* what happend on error cause? return to size? */ + LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx SNS ACK with cause code %d. Resetting SNS\n", + nse->nsei, cause); + osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL); + break; } + + // TODO: cleanup the bind also on SIZE or other failures + if (gss->current_procedure.procedure == SNS_DEL) { + talloc_free(gss->current_procedure.bind); + gss->current_procedure.bind = NULL; + } + + 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); break; } } @@ -1653,6 +1810,7 @@ /* common allstate-action for both roles */ static void ns2_sns_st_all_action(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 ns2_sns_bind *sbind; struct gprs_ns2_vc *nsvc, *nsvc2; @@ -1662,45 +1820,57 @@ sbind = data; switch (fi->state) { case GPRS_SNS_ST_UNCONFIGURED: - osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL); + osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL); break; case GPRS_SNS_ST_BSS_SIZE: - /* TODO: add the ip4 element to the list */ + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_SIZE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1); break; case GPRS_SNS_ST_BSS_CONFIG_BSS: case GPRS_SNS_ST_BSS_CONFIG_SGSN: + sbind->procedure_state |= S(SNS_BIND_ADD_REQ); + break; case GPRS_SNS_ST_CONFIGURED: - /* TODO: add to SNS-IP procedure queue & add nsvc() */ + sbind->procedure_state |= S(SNS_BIND_ADD_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; + case GPRS_SNS_ST_LOCAL_PROCEDURE: break; } break; case GPRS_SNS_EV_REQ_DELETE_BIND: sbind = data; + sbind->procedure_state |= S(SNS_BIND_DEL_REQ); switch (fi->state) { case GPRS_SNS_ST_UNCONFIGURED: + llist_del(&sbind->list); + talloc_free(sbind); break; case GPRS_SNS_ST_BSS_SIZE: - /* TODO: remove the ip4 element from the list */ - llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) { - if (nsvc->bind == sbind->bind) { - gprs_ns2_free_nsvc(nsvc); + if (sbind == gss->initial_bind) { + /* keep the order of binds */ + ns2_sns_choose_next_bind(gss); + if (sbind == gss->initial_bind) + gss->initial_bind = NULL; + llist_del(&sbind->list); + llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) { + if (nsvc->bind == sbind->bind) { + gprs_ns2_free_nsvc(nsvc); + } } + } else { + llist_del(&sbind->list); } + talloc_free(sbind); break; case GPRS_SNS_ST_BSS_CONFIG_BSS: case GPRS_SNS_ST_BSS_CONFIG_SGSN: + break; case GPRS_SNS_ST_CONFIGURED: - /* TODO: do an delete SNS-IP procedure */ - /* TODO: remove the ip4 element to the list */ - llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) { - if (nsvc->bind == sbind->bind) { - gprs_ns2_free_nsvc(nsvc); - } - } + osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], GPRS_SNS_ST_LOCAL_PROCEDURE); + break; + case GPRS_SNS_ST_LOCAL_PROCEDURE: break; } - /* 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; @@ -1710,11 +1880,21 @@ /* 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); + case GPRS_SNS_ST_BSS_CONFIG_BSS: + case GPRS_SNS_ST_BSS_CONFIG_SGSN: + case GPRS_SNS_ST_CONFIGURED: + /* ignore update weights when the bind hasn't added */ + if (sbind->procedure_state & S(SNS_BIND_ADD_REQ)) + return; + sbind->procedure_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; + case GPRS_SNS_ST_LOCAL_PROCEDURE: + /* ignore update weights when the bind hasn't added */ + if (sbind->procedure_state & S(SNS_BIND_ADD_REQ)) + return; + sbind->procedure_state |= S(SNS_BIND_DEL_REQ); + break; } break; } @@ -1745,7 +1925,9 @@ ns2_clear_ipv46_entries_remote(gss); /* Choose the next sns endpoint. */ - if (llist_empty(&gss->sns_endpoints) || llist_empty(&gss->binds)) { + ns2_sns_clean_procedures(gss); + ns2_sns_choose_next_bind(gss); + if (llist_empty(&gss->sns_endpoints) || !gss->initial_bind) { gss->initial = NULL; ns2_prim_status_ind(gss->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS); osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 3); @@ -2167,7 +2349,6 @@ return -ENOMEM; tmp->bind = bind; llist_add_tail(&tmp->list, &gss->binds); - osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_ADD_BIND, tmp); return 0; } @@ -2193,7 +2374,6 @@ llist_for_each_entry_safe(tmp, tmp2, &gss->binds, list) { if (tmp->bind == bind) { - llist_del(&tmp->list); found = true; break; } @@ -2212,6 +2392,7 @@ const struct osmo_sockaddr *sa; struct gprs_ns_ie_ip4_elem *ip4; struct gprs_ns_ie_ip6_elem *ip6; + int rc; /* TODO: ensure this bind is already added! */ OSMO_ASSERT(gss->current_procedure.procedure != SNS_NONE); @@ -2229,8 +2410,18 @@ 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); + if (gss->current_procedure.procedure == SNS_CHANGE_WEIGHT || gss->current_procedure.procedure == SNS_DEL) { + /* for SNS-UPDATE-PROCEDURE we need to find an endpoint */ + OSMO_ASSERT(ip4); + } else if (gss->current_procedure.procedure == SNS_ADD) { + OSMO_ASSERT(!ip4); + rc = add_local_ip4_elem(gss, sa->u.sin.sin_addr.s_addr, sa->u.sin.sin_port, + gss->current_procedure.bind->bind->sns_sig_weight, + gss->current_procedure.bind->bind->sns_data_weight); + OSMO_ASSERT(!rc); + ip4 = &gss->ip4_local[gss->num_ip4_local - 1]; + } + gss->current_procedure.ip4 = ip4; break; case AF_INET6: @@ -2242,7 +2433,19 @@ } else { ip6 = ip6_elem_by_saddr(gss->ip6_local, gss->num_ip6_local, sa); } - OSMO_ASSERT(ip6); + + /* for SNS-UPDATE-PROCEDURE we need to find an endpoint */ + if (gss->current_procedure.procedure == SNS_CHANGE_WEIGHT || gss->current_procedure.procedure == SNS_DEL) { + OSMO_ASSERT(ip6); + } else if (gss->current_procedure.procedure == SNS_ADD) { + OSMO_ASSERT(!ip6); + rc = add_local_ip6_elem(gss, &sa->u.sin6.sin6_addr, sa->u.sin6.sin6_port, + gss->current_procedure.bind->bind->sns_sig_weight, + gss->current_procedure.bind->bind->sns_data_weight); + OSMO_ASSERT(!rc); + ip6 = &gss->ip6_local[gss->num_ip6_local - 1]; + } + gss->current_procedure.ip6 = ip6; break; default: -- To view, visit https://gerrit.osmocom.org/c/libosmocore/+/24123 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmocore Gerrit-Branch: master Gerrit-Change-Id: I71c33200bd1f0307ceb943ee958db5ebe3623d36 Gerrit-Change-Number: 24123 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/20210504/53b82c99/attachment.htm>