diff options
| author | Neels Hofmeyr <neels@hofmeyr.de> | 2019-01-16 16:53:26 +0100 | 
|---|---|---|
| committer | Neels Hofmeyr <neels@hofmeyr.de> | 2019-01-29 00:59:32 +0100 | 
| commit | 7740d26d7e6a3ee963cafadae8fc6d0ef7e7d21b (patch) | |
| tree | 80853135e78da11fe0318a719b564e94c33d62b5 | |
| parent | 0423b61aa8dd75b3141e13cf9276b8fa14ceb473 (diff) | |
add osmo_classmark_* API
osmo-bsc and osmo-msc implement identical Classmark structures. It makes sense
to define once near the gsm48 protocol definitions.
Also move along some generic Classmark API from osmo-msc.
Change-Id: Ifd27bab0380f7ad0c44c719aa6c8bd62cf7b034c
| -rw-r--r-- | include/osmocom/gsm/protocol/gsm_04_08.h | 16 | ||||
| -rw-r--r-- | src/gsm/gsm48.c | 126 | ||||
| -rw-r--r-- | src/gsm/libosmogsm.map | 7 | 
3 files changed, 149 insertions, 0 deletions
diff --git a/include/osmocom/gsm/protocol/gsm_04_08.h b/include/osmocom/gsm/protocol/gsm_04_08.h index 234fa793..fbd4fe6a 100644 --- a/include/osmocom/gsm/protocol/gsm_04_08.h +++ b/include/osmocom/gsm/protocol/gsm_04_08.h @@ -56,6 +56,22 @@ struct gsm48_classmark2 {  #endif  } __attribute__ ((packed)); +struct osmo_gsm48_classmark { +	bool classmark1_set; +	struct gsm48_classmark1 classmark1; +	uint8_t classmark2_len; +	struct gsm48_classmark2 classmark2; +	uint8_t classmark3_len; +	uint8_t classmark3[14]; /* if cm3 gets extended by spec, it will be truncated */ +}; + +bool osmo_gsm48_classmark_is_r99(const struct osmo_gsm48_classmark *cm); +bool osmo_gsm48_classmark1_is_r99(const struct gsm48_classmark1 *cm1); +bool osmo_gsm48_classmark2_is_r99(const struct gsm48_classmark2 *cm2, uint8_t cm2_len); +int osmo_gsm48_classmark_supports_a5(const struct osmo_gsm48_classmark *cm, uint8_t a5); +const char *osmo_gsm48_classmark_a5_name(const struct osmo_gsm48_classmark *cm); +void osmo_gsm48_classmark_update(struct osmo_gsm48_classmark *dst, const struct osmo_gsm48_classmark *src); +  /* Chapter 10.5.2.1b.3 */  #if OSMO_IS_LITTLE_ENDIAN == 1  struct gsm48_range_1024 { diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c index 86d40d4c..4166c153 100644 --- a/src/gsm/gsm48.c +++ b/src/gsm/gsm48.c @@ -1160,4 +1160,130 @@ const struct value_string osmo_cm_service_type_names[] = {  	{}  }; +bool osmo_gsm48_classmark1_is_r99(const struct gsm48_classmark1 *cm1) +{ +	return cm1->rev_lev >= 2; +} + +bool osmo_gsm48_classmark2_is_r99(const struct gsm48_classmark2 *cm2, uint8_t cm2_len) +{ +	if (!cm2_len) +		return false; +	return cm2->rev_lev >= 2; +} + +/*! Return true if any of Classmark 1 or Classmark 2 are present and indicate R99 capability. + * \param[in] cm  Classmarks. + * \returns True if R99 or later, false if pre-R99 or no Classmarks are present. + */ +bool osmo_gsm48_classmark_is_r99(const struct osmo_gsm48_classmark *cm) +{ +	if (cm->classmark1_set) +		return osmo_gsm48_classmark1_is_r99(&cm->classmark1); +	return osmo_gsm48_classmark2_is_r99(&cm->classmark2, cm->classmark2_len); +} + +/*! Return a string representation of A5 cipher algorithms indicated by Classmark 1, 2 and 3. + * \param[in] cm  Classmarks. + * \returns A statically allocated string like "cm1{a5/1=supported} cm2{0x23= A5/2 A5/3} no-cm3" + */ +const char *osmo_gsm48_classmark_a5_name(const struct osmo_gsm48_classmark *cm) +{ +	static char buf[128]; +	char cm1[42] = "no-cm1"; +	char cm2[42] = " no-cm2"; +	char cm3[42] = " no-cm2"; + +	if (cm->classmark1_set) +		snprintf(cm1, sizeof(cm1), "cm1{a5/1=%s}", +		     cm->classmark1.a5_1 ? "not-supported":"supported" /* inverted logic */); + +	if (cm->classmark2_len >= 3) +		snprintf(cm2, sizeof(cm2), " cm2{0x%x=%s%s}", +			 cm->classmark2.a5_2 + (cm->classmark2.a5_3 << 1), +			 cm->classmark2.a5_2 ? " A5/2" : "", +			 cm->classmark2.a5_3 ? " A5/3" : ""); + +	if (cm->classmark3_len >= 1) +		snprintf(cm3, sizeof(cm3), " cm3{0x%x=%s%s%s%s}", +			 cm->classmark3[0], +			 cm->classmark3[0] & (1 << 0) ? " A5/4" : "", +			 cm->classmark3[0] & (1 << 1) ? " A5/5" : "", +			 cm->classmark3[0] & (1 << 2) ? " A5/6" : "", +			 cm->classmark3[0] & (1 << 3) ? " A5/7" : ""); + +	snprintf(buf, sizeof(buf), "%s%s%s", cm1, cm2, cm3); +	return buf; +} + +/*! Overwrite dst with the Classmark information present in src. + * Add an new Classmark and overwrite in dst what src has to offer, but where src has no Classmark information, leave + * dst unchanged. (For Classmark 2 and 3, dst will exactly match any non-zero Classmark length from src, hence may end + * up with a shorter Classmark after this call.) + * \param[out] dst  The target Classmark storage to be updated. + * \param[in] src  The new Classmark information to read from. + */ +void osmo_gsm48_classmark_update(struct osmo_gsm48_classmark *dst, const struct osmo_gsm48_classmark *src) +{ +	if (src->classmark1_set) { +		dst->classmark1 = src->classmark1; +		dst->classmark1_set = true; +	} +	if (src->classmark2_len) { +		dst->classmark2_len = src->classmark2_len; +		dst->classmark2 = src->classmark2; +	} +	if (src->classmark3_len) { +		dst->classmark3_len = src->classmark3_len; +		memcpy(dst->classmark3, src->classmark3, OSMO_MIN(sizeof(dst->classmark3), src->classmark3_len)); +	} +} + + +/*! Determine if the given Classmark (1/2/3) value permits a given A5/n cipher. + * \param[in] cm  Classmarks. + * \param[in] a5  The N in A5/N for which to query whether support is indicated. + * \return 1 when the given A5/n is permitted, 0 when not (or a5 > 7), and negative if the respective MS Classmark is + *         not known, where the negative number indicates the classmark type: -2 means Classmark 2 is not available. The + *         idea is that when e.g. A5/3 is requested and the corresponding Classmark 3 is not available, that the caller + *         can react by obtaining Classmark 3 and calling again once it is available. + */ +int osmo_gsm48_classmark_supports_a5(const struct osmo_gsm48_classmark *cm, uint8_t a5) +{ +	switch (a5) { +	case 0: +		/* all phones must implement A5/0, see 3GPP TS 43.020 4.9 */ +		return 1; +	case 1: +		/* 3GPP TS 43.020 4.9 requires A5/1 to be suppored by all phones and actually states: +		 * "The network shall not provide service to an MS which indicates that it does not +		 *  support the ciphering algorithm A5/1.".  However, let's be more tolerant based +		 * on policy here */ +		/* See 3GPP TS 24.008 10.5.1.7 */ +		if (!cm->classmark1_set) +			return -1; +		/* Inverted logic for this bit! */ +		return cm->classmark1.a5_1 ? 0 : 1; +	case 2: +		/* See 3GPP TS 24.008 10.5.1.6 */ +		if (cm->classmark2_len < 3) +			return -2; +		return cm->classmark2.a5_2 ? 1 : 0; +	case 3: +		if (cm->classmark2_len < 3) +			return -2; +		return cm->classmark2.a5_3 ? 1 : 0; +	case 4: +	case 5: +	case 6: +	case 7: +		/* See 3GPP TS 24.008 10.5.1.7 */ +		if (!cm->classmark3_len) +			return -3; +		return (cm->classmark3[0] & (1 << (a5-4))) ? 1 : 0; +	default: +		return 0; +	} +} +  /*! @} */ diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index ea8c9be9..3cfe6a7c 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -580,5 +580,12 @@ osmo_rat_type_names;  osmo_lu_type_names;  osmo_cm_service_type_names; +osmo_classmark_is_r99; +osmo_classmark1_is_r99; +osmo_classmark2_is_r99; +osmo_classmark_supports_a5; +osmo_classmark_a5_name; +osmo_classmark_update; +  local: *;  };  | 
