diff options
author | Harald Welte <laforge@gnumonks.org> | 2019-09-03 13:54:37 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2019-09-05 14:30:17 +0200 |
commit | f53fde91a36eff2601df9811fddee97b8f89d6ee (patch) | |
tree | 4fab4e91ba2be64871a04406d2d257f4e6f221b8 /src/common/paging.c | |
parent | cb57028d52b711dfbc27dacf0b82c5a12f1f71c2 (diff) |
ETWS Primary Notification via P1 Rest Octets
The ETWS (Earthquake and Tsunami Warning System) uses a so-called
ETWS Primary Notification which is sent
* to phones in dedicated mode (via DCCH from the BSC)
* to phones in idle mode (via P1 Rest Octets on PCH/CCCH)
This patch implements the second part of the functionality, i.e.
transmitting the related ETWS Primary Notification via PCH. As
3GPP doesn't specify how this is communicated over Abis, we use
a new, vendor-specific RSL message type.
Closes: OS#4047
Depends: libosmocore I89c24a81ada6627694a9632e87485a61cbd3e680
Depends: libosmocore I36fc2ffc22728887d1cb8768c7fcd9739a8ec0fc
Change-Id: I18c60cdb86b9c19e09f5ec06d66e9b91608880e6
Diffstat (limited to 'src/common/paging.c')
-rw-r--r-- | src/common/paging.c | 140 |
1 files changed, 129 insertions, 11 deletions
diff --git a/src/common/paging.c b/src/common/paging.c index 111f9475..fca58b5f 100644 --- a/src/common/paging.c +++ b/src/common/paging.c @@ -21,7 +21,7 @@ /* TODO: * eMLPP priprity - * add P1/P2/P3 rest octets + * add P2/P3 rest octets */ #include <stdlib.h> @@ -274,11 +274,86 @@ int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data, #define L2_PLEN(len) (((len - 1) << 2) | 0x01) +/* abstract representation of P1 rest octets; we only implement those parts we need for now */ +struct p1_rest_octets { + bool packet_page_ind[2]; + bool r8_present; + struct { + bool prio_ul_access; + bool etws_present; + struct { + bool is_first; + uint8_t page_nr; + const uint8_t *page; + size_t page_bytes; + } etws; + } r8; +}; + +/* 3GPP TS 44.018 10.5.2.23 append a segment/page of an ETWS primary notification to given bitvec */ +static void append_etws_prim_notif(struct bitvec *bv, bool is_first, uint8_t page_nr, + const uint8_t *etws, ssize_t etws_len) +{ + OSMO_ASSERT(etws_len < 128/8); + + /* ETWS primary Notification struct + * 0 NNNN / 1 NNNN + * PNI n + * LEN nnnnnnn (at least 13 bits before paylod) + * number of bits (LEN; up to 128) */ + + if (is_first) + bitvec_set_bit(bv, 0); + else + bitvec_set_bit(bv, 1); + bitvec_set_uint(bv, page_nr, 4); /* Segment Number / Total Number */ + bitvec_set_bit(bv, 0); /* PNI to distinguish different ETWS */ + bitvec_set_uint(bv, etws_len*8, 7); /* length of payload in number of bits */ + bitvec_set_bytes(bv, etws, etws_len); + + /* 17 bytes = 136bit - (11+13) = 112 bits = 14 bytes per PT1 + * => at least 4x PT1 RO for complete primary notification (56 bytes) */ +} + +/* 3GPP TS 44.018 10.5.2.23 append P1 Rest Octets to given bit-vector */ +static void append_p1_rest_octets(struct bitvec *bv, const struct p1_rest_octets *p1ro) +{ + /* Paging 1 RO (at least 10 bits before ETWS struct) */ + bitvec_set_bit(bv, L); /* no NLN */ + bitvec_set_bit(bv, L); /* no Priority1 */ + bitvec_set_bit(bv, L); /* no Priority2 */ + bitvec_set_bit(bv, L); /* no Group Call Info */ + if (p1ro->packet_page_ind[0]) + bitvec_set_bit(bv, H); /* Packet Page Indication 1 */ + else + bitvec_set_bit(bv, L); /* Packet Page Indication 1 */ + if (p1ro->packet_page_ind[1]) + bitvec_set_bit(bv, H); /* Packet Page Indication 2 */ + else + bitvec_set_bit(bv, L); /* Packet Page Indication 2 */ + + bitvec_set_bit(bv, L); /* No Release 6 additions */ + bitvec_set_bit(bv, L); /* No Release 7 additions */ + + if (p1ro->r8_present) { + bitvec_set_bit(bv, H); /* Release 8 */ + bitvec_set_bit(bv, p1ro->r8.prio_ul_access); /* Priority Uplink Access */ + if (p1ro->r8.etws_present) { + bitvec_set_bit(bv, 1); /* ETWS present */ + append_etws_prim_notif(bv, p1ro->r8.etws.is_first, p1ro->r8.etws.page_nr, + p1ro->r8.etws.page, p1ro->r8.etws.page_bytes); + } else + bitvec_set_bit(bv, 0); + } +} + static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv, uint8_t chan1, const uint8_t *identity2_lv, - uint8_t chan2) + uint8_t chan2, const struct p1_rest_octets *p1ro) { struct gsm48_paging1 *pt1 = (struct gsm48_paging1 *) out_buf; + struct bitvec bv; + unsigned int paging_len; uint8_t *cur; memset(out_buf, 0, sizeof(*pt1)); @@ -294,7 +369,19 @@ static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv, pt1->l2_plen = L2_PLEN(cur - out_buf); - return cur - out_buf; + paging_len = cur - out_buf; + + memset(&bv, 0, sizeof(bv)); + bv.data = cur; + bv.data_len = GSM_MACBLOCK_LEN - paging_len; + + if (p1ro) + append_p1_rest_octets(&bv, p1ro); + + /* pad to the end of the MAC block */ + bitvec_spare_padding(&bv, bv.data_len *8); + + return GSM_MACBLOCK_LEN; } static int fill_paging_type_2(uint8_t *out_buf, const uint8_t *tmsi1_lv, @@ -406,16 +493,43 @@ static void sort_pr_tmsi_imsi(struct paging_record *pr[], unsigned int n) } } +static void build_p1_rest_octets(struct p1_rest_octets *p1ro, struct gsm_bts *bts) +{ + memset(p1ro, 0, sizeof(*p1ro)); + p1ro->packet_page_ind[0] = false; + p1ro->packet_page_ind[1] = false; + p1ro->r8_present = true; + p1ro->r8.prio_ul_access = false; + p1ro->r8.etws_present = true; + unsigned int offset = bts->etws.page_size * bts->etws.next_page; + + if (bts->etws.next_page == 0) { + p1ro->r8.etws.is_first = true; + p1ro->r8.etws.page_nr = bts->etws.num_pages; + } else { + p1ro->r8.etws.is_first = false; + p1ro->r8.etws.page_nr = bts->etws.next_page + 1; + } + p1ro->r8.etws.page = bts->etws.prim_notif + offset; + /* last page may be smaller than first pages */ + if (bts->etws.next_page < bts->etws.num_pages-1) + p1ro->r8.etws.page_bytes = bts->etws.page_size; + else + p1ro->r8.etws.page_bytes = bts->etws.prim_notif_len - offset; + bts->etws.next_page = (bts->etws.next_page + 1) % bts->etws.num_pages; +} + /* generate paging message for given gsm time */ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt, int *is_empty) { struct llist_head *group_q; + struct gsm_bts *bts = ps->bts; int group; int len; *is_empty = 0; - ps->bts->load.ccch.pch_total += 1; + bts->load.ccch.pch_total += 1; group = get_pag_subch_nr(ps, gt); if (group < 0) { @@ -427,11 +541,15 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g group_q = &ps->paging_queue[group]; - /* There is nobody to be paged, send Type1 with two empty ID */ - if (llist_empty(group_q)) { + if (ps->bts->etws.prim_notif) { + struct p1_rest_octets p1ro; + build_p1_rest_octets(&p1ro, bts); + len = fill_paging_type_1(out_buf, empty_id_lv, 0, NULL, 0, &p1ro); + } else if (llist_empty(group_q)) { + /* There is nobody to be paged, send Type1 with two empty ID */ //DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n"); len = fill_paging_type_1(out_buf, empty_id_lv, 0, - NULL, 0); + NULL, 0, NULL); *is_empty = 1; } else { struct paging_record *pr[4]; @@ -439,7 +557,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g time_t now = time(NULL); unsigned int i, num_imsi = 0; - ps->bts->load.ccch.pch_used += 1; + bts->load.ccch.pch_used += 1; /* get (if we have) up to four paging records */ for (i = 0; i < ARRAY_SIZE(pr); i++) { @@ -509,7 +627,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g len = fill_paging_type_1(out_buf, pr[0]->u.paging.identity_lv, pr[0]->u.paging.chan_needed, - NULL, 0); + NULL, 0, NULL); } else { /* 2 (any type) or * 3 or 4, of which only 2 will be sent */ @@ -518,7 +636,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g pr[0]->u.paging.identity_lv, pr[0]->u.paging.chan_needed, pr[1]->u.paging.identity_lv, - pr[1]->u.paging.chan_needed); + pr[1]->u.paging.chan_needed, NULL); if (num_pr >= 3) { /* re-add #4 for next time */ llist_add(&pr[2]->list, group_q); @@ -535,7 +653,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g /* skip those that we might have re-added above */ if (pr[i] == NULL) continue; - rate_ctr_inc2(ps->bts->ctrs, BTS_CTR_PAGING_SENT); + rate_ctr_inc2(bts->ctrs, BTS_CTR_PAGING_SENT); /* check if we can expire the paging record, * or if we need to re-queue it */ if (pr[i]->u.paging.expiration_time <= now) { |