<p>Neels Hofmeyr <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12659">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Harald Welte: Looks good to me, approved
  Vadim Yanitskiy: Looks good to me, but someone else must approve

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">add osmo_classmark_* API<br><br>osmo-bsc and osmo-msc implement identical Classmark structures. It makes sense<br>to define once near the gsm48 protocol definitions.<br><br>Also move along some generic Classmark API from osmo-msc.<br><br>Change-Id: Ifd27bab0380f7ad0c44c719aa6c8bd62cf7b034c<br>---<br>M include/osmocom/gsm/protocol/gsm_04_08.h<br>M src/gsm/gsm48.c<br>M src/gsm/libosmogsm.map<br>3 files changed, 149 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/gsm/protocol/gsm_04_08.h b/include/osmocom/gsm/protocol/gsm_04_08.h</span><br><span>index 234fa79..fbd4fe6 100644</span><br><span>--- a/include/osmocom/gsm/protocol/gsm_04_08.h</span><br><span>+++ b/include/osmocom/gsm/protocol/gsm_04_08.h</span><br><span>@@ -56,6 +56,22 @@</span><br><span> #endif</span><br><span> } __attribute__ ((packed));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsm48_classmark {</span><br><span style="color: hsl(120, 100%, 40%);">+ bool classmark1_set;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm48_classmark1 classmark1;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t classmark2_len;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm48_classmark2 classmark2;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t classmark3_len;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t classmark3[14]; /* if cm3 gets extended by spec, it will be truncated */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_gsm48_classmark_is_r99(const struct osmo_gsm48_classmark *cm);</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_gsm48_classmark1_is_r99(const struct gsm48_classmark1 *cm1);</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_gsm48_classmark2_is_r99(const struct gsm48_classmark2 *cm2, uint8_t cm2_len);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsm48_classmark_supports_a5(const struct osmo_gsm48_classmark *cm, uint8_t a5);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_gsm48_classmark_a5_name(const struct osmo_gsm48_classmark *cm);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_gsm48_classmark_update(struct osmo_gsm48_classmark *dst, const struct osmo_gsm48_classmark *src);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Chapter 10.5.2.1b.3 */</span><br><span> #if OSMO_IS_LITTLE_ENDIAN == 1</span><br><span> struct gsm48_range_1024 {</span><br><span>diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c</span><br><span>index 86d40d4..4166c15 100644</span><br><span>--- a/src/gsm/gsm48.c</span><br><span>+++ b/src/gsm/gsm48.c</span><br><span>@@ -1160,4 +1160,130 @@</span><br><span>  {}</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_gsm48_classmark1_is_r99(const struct gsm48_classmark1 *cm1)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        return cm1->rev_lev >= 2;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_gsm48_classmark2_is_r99(const struct gsm48_classmark2 *cm2, uint8_t cm2_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!cm2_len)</span><br><span style="color: hsl(120, 100%, 40%);">+         return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ return cm2->rev_lev >= 2;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return true if any of Classmark 1 or Classmark 2 are present and indicate R99 capability.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] cm  Classmarks.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True if R99 or later, false if pre-R99 or no Classmarks are present.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_gsm48_classmark_is_r99(const struct osmo_gsm48_classmark *cm)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cm->classmark1_set)</span><br><span style="color: hsl(120, 100%, 40%);">+            return osmo_gsm48_classmark1_is_r99(&cm->classmark1);</span><br><span style="color: hsl(120, 100%, 40%);">+  return osmo_gsm48_classmark2_is_r99(&cm->classmark2, cm->classmark2_len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return a string representation of A5 cipher algorithms indicated by Classmark 1, 2 and 3.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] cm  Classmarks.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A statically allocated string like "cm1{a5/1=supported} cm2{0x23= A5/2 A5/3} no-cm3"</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_gsm48_classmark_a5_name(const struct osmo_gsm48_classmark *cm)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   static char buf[128];</span><br><span style="color: hsl(120, 100%, 40%);">+ char cm1[42] = "no-cm1";</span><br><span style="color: hsl(120, 100%, 40%);">+    char cm2[42] = " no-cm2";</span><br><span style="color: hsl(120, 100%, 40%);">+   char cm3[42] = " no-cm2";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cm->classmark1_set)</span><br><span style="color: hsl(120, 100%, 40%);">+            snprintf(cm1, sizeof(cm1), "cm1{a5/1=%s}",</span><br><span style="color: hsl(120, 100%, 40%);">+               cm->classmark1.a5_1 ? "not-supported":"supported" /* inverted logic */);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (cm->classmark2_len >= 3)</span><br><span style="color: hsl(120, 100%, 40%);">+            snprintf(cm2, sizeof(cm2), " cm2{0x%x=%s%s}",</span><br><span style="color: hsl(120, 100%, 40%);">+                        cm->classmark2.a5_2 + (cm->classmark2.a5_3 << 1),</span><br><span style="color: hsl(120, 100%, 40%);">+                         cm->classmark2.a5_2 ? " A5/2" : "",</span><br><span style="color: hsl(120, 100%, 40%);">+                    cm->classmark2.a5_3 ? " A5/3" : "");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (cm->classmark3_len >= 1)</span><br><span style="color: hsl(120, 100%, 40%);">+            snprintf(cm3, sizeof(cm3), " cm3{0x%x=%s%s%s%s}",</span><br><span style="color: hsl(120, 100%, 40%);">+                    cm->classmark3[0],</span><br><span style="color: hsl(120, 100%, 40%);">+                         cm->classmark3[0] & (1 << 0) ? " A5/4" : "",</span><br><span style="color: hsl(120, 100%, 40%);">+                         cm->classmark3[0] & (1 << 1) ? " A5/5" : "",</span><br><span style="color: hsl(120, 100%, 40%);">+                         cm->classmark3[0] & (1 << 2) ? " A5/6" : "",</span><br><span style="color: hsl(120, 100%, 40%);">+                         cm->classmark3[0] & (1 << 3) ? " A5/7" : "");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     snprintf(buf, sizeof(buf), "%s%s%s", cm1, cm2, cm3);</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Overwrite dst with the Classmark information present in src.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Add an new Classmark and overwrite in dst what src has to offer, but where src has no Classmark information, leave</span><br><span style="color: hsl(120, 100%, 40%);">+ * dst unchanged. (For Classmark 2 and 3, dst will exactly match any non-zero Classmark length from src, hence may end</span><br><span style="color: hsl(120, 100%, 40%);">+ * up with a shorter Classmark after this call.)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] dst  The target Classmark storage to be updated.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] src  The new Classmark information to read from.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_gsm48_classmark_update(struct osmo_gsm48_classmark *dst, const struct osmo_gsm48_classmark *src)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      if (src->classmark1_set) {</span><br><span style="color: hsl(120, 100%, 40%);">+         dst->classmark1 = src->classmark1;</span><br><span style="color: hsl(120, 100%, 40%);">+              dst->classmark1_set = true;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (src->classmark2_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+         dst->classmark2_len = src->classmark2_len;</span><br><span style="color: hsl(120, 100%, 40%);">+              dst->classmark2 = src->classmark2;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (src->classmark3_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+         dst->classmark3_len = src->classmark3_len;</span><br><span style="color: hsl(120, 100%, 40%);">+              memcpy(dst->classmark3, src->classmark3, OSMO_MIN(sizeof(dst->classmark3), src->classmark3_len));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Determine if the given Classmark (1/2/3) value permits a given A5/n cipher.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] cm  Classmarks.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] a5  The N in A5/N for which to query whether support is indicated.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 1 when the given A5/n is permitted, 0 when not (or a5 > 7), and negative if the respective MS Classmark is</span><br><span style="color: hsl(120, 100%, 40%);">+ *         not known, where the negative number indicates the classmark type: -2 means Classmark 2 is not available. The</span><br><span style="color: hsl(120, 100%, 40%);">+ *         idea is that when e.g. A5/3 is requested and the corresponding Classmark 3 is not available, that the caller</span><br><span style="color: hsl(120, 100%, 40%);">+ *         can react by obtaining Classmark 3 and calling again once it is available.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsm48_classmark_supports_a5(const struct osmo_gsm48_classmark *cm, uint8_t a5)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       switch (a5) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case 0:</span><br><span style="color: hsl(120, 100%, 40%);">+               /* all phones must implement A5/0, see 3GPP TS 43.020 4.9 */</span><br><span style="color: hsl(120, 100%, 40%);">+          return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     case 1:</span><br><span style="color: hsl(120, 100%, 40%);">+               /* 3GPP TS 43.020 4.9 requires A5/1 to be suppored by all phones and actually states:</span><br><span style="color: hsl(120, 100%, 40%);">+          * "The network shall not provide service to an MS which indicates that it does not</span><br><span style="color: hsl(120, 100%, 40%);">+               *  support the ciphering algorithm A5/1.".  However, let's be more tolerant based</span><br><span style="color: hsl(120, 100%, 40%);">+            * on policy here */</span><br><span style="color: hsl(120, 100%, 40%);">+          /* See 3GPP TS 24.008 10.5.1.7 */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!cm->classmark1_set)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Inverted logic for this bit! */</span><br><span style="color: hsl(120, 100%, 40%);">+            return cm->classmark1.a5_1 ? 0 : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 2:</span><br><span style="color: hsl(120, 100%, 40%);">+               /* See 3GPP TS 24.008 10.5.1.6 */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (cm->classmark2_len < 3)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return -2;</span><br><span style="color: hsl(120, 100%, 40%);">+            return cm->classmark2.a5_2 ? 1 : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 3:</span><br><span style="color: hsl(120, 100%, 40%);">+               if (cm->classmark2_len < 3)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return -2;</span><br><span style="color: hsl(120, 100%, 40%);">+            return cm->classmark2.a5_3 ? 1 : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 4:</span><br><span style="color: hsl(120, 100%, 40%);">+       case 5:</span><br><span style="color: hsl(120, 100%, 40%);">+       case 6:</span><br><span style="color: hsl(120, 100%, 40%);">+       case 7:</span><br><span style="color: hsl(120, 100%, 40%);">+               /* See 3GPP TS 24.008 10.5.1.7 */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!cm->classmark3_len)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -3;</span><br><span style="color: hsl(120, 100%, 40%);">+            return (cm->classmark3[0] & (1 << (a5-4))) ? 1 : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span>diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map</span><br><span>index ea8c9be..3cfe6a7 100644</span><br><span>--- a/src/gsm/libosmogsm.map</span><br><span>+++ b/src/gsm/libosmogsm.map</span><br><span>@@ -580,5 +580,12 @@</span><br><span> osmo_lu_type_names;</span><br><span> osmo_cm_service_type_names;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+osmo_classmark_is_r99;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_classmark1_is_r99;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_classmark2_is_r99;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_classmark_supports_a5;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_classmark_a5_name;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_classmark_update;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> local: *;</span><br><span> };</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12659">change 12659</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/12659"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ifd27bab0380f7ad0c44c719aa6c8bd62cf7b034c </div>
<div style="display:none"> Gerrit-Change-Number: 12659 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Vadim Yanitskiy <axilirator@gmail.com> </div>