diff options
Diffstat (limited to 'src/gsm/gsup.c')
-rw-r--r-- | src/gsm/gsup.c | 125 |
1 files changed, 115 insertions, 10 deletions
diff --git a/src/gsm/gsup.c b/src/gsm/gsup.c index ef33ed08..4f0a1b5f 100644 --- a/src/gsm/gsup.c +++ b/src/gsm/gsup.c @@ -103,6 +103,10 @@ const struct value_string osmo_gsup_message_type_names[] = { OSMO_VALUE_STRING(OSMO_GSUP_MSGT_ROUTING_ERROR), + OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_REQUEST), + OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_RESULT), + OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_ERROR), + { 0, NULL } }; @@ -122,6 +126,62 @@ int osmo_gsup_get_err_msg_type(enum osmo_gsup_message_type type_in) return OSMO_GSUP_TO_MSGT_ERROR(type_in); } +static int decode_pdp_address(const uint8_t *data, size_t data_len, struct osmo_gsup_pdp_info *pdp_info) +{ + const struct gsm48_pdp_address *pdp_addr = (const struct gsm48_pdp_address *)data; + /* Explicitly pre-nitialize them to AF_UNSPEC to signal they are empty: */ + pdp_info->pdp_address[0].u.sa.sa_family = AF_UNSPEC; + pdp_info->pdp_address[1].u.sa.sa_family = AF_UNSPEC; + + if (data_len < 2) + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + + pdp_info->pdp_type_org = pdp_addr->organization; + pdp_info->pdp_type_nr = pdp_addr->type; + + if (pdp_info->pdp_type_org != PDP_TYPE_ORG_IETF) + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + + /* Skip type-org + type-nr for easy calculations below: */ + data_len -= 2; + + switch (pdp_info->pdp_type_nr) { + case PDP_TYPE_N_IETF_IPv4: + if (data_len == 0) + return 0; /* empty, marked as AF_UNSET. */ + if (data_len != sizeof(pdp_addr->ietf.v4)) + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + pdp_info->pdp_address[0].u.sa.sa_family = AF_INET; + pdp_info->pdp_address[0].u.sin.sin_addr.s_addr = pdp_addr->ietf.v4; + return 0; + case PDP_TYPE_N_IETF_IPv6: + if (data_len == 0) + return 0; /* empty, marked as AF_UNSET. */ + if (data_len != sizeof(pdp_addr->ietf.v6)) + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + pdp_info->pdp_address[0].u.sa.sa_family = AF_INET6; + memcpy(&pdp_info->pdp_address[0].u.sin6.sin6_addr, + pdp_addr->ietf.v6, + sizeof(pdp_addr->ietf.v6)); + return 0; + case PDP_TYPE_N_IETF_IPv4v6: + if (data_len == 0) + return 0; /* empty, marked as AF_UNSET. */ + if (data_len != sizeof(pdp_addr->ietf.v4v6)) + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + pdp_info->pdp_address[0].u.sa.sa_family = AF_INET; + pdp_info->pdp_address[0].u.sin.sin_addr.s_addr = pdp_addr->ietf.v4v6.v4; + pdp_info->pdp_address[1].u.sa.sa_family = AF_INET6; + memcpy(&pdp_info->pdp_address[1].u.sin6.sin6_addr, + pdp_addr->ietf.v4v6.v6, + sizeof(pdp_addr->ietf.v4v6.v6)); + return 0; + default: + /* reserved, both pdp_info->pdp_address are preinitialied to AF_UNSET. */ + return 0; + } +} + static int decode_pdp_info(uint8_t *data, size_t data_len, struct osmo_gsup_pdp_info *pdp_info) { @@ -145,9 +205,9 @@ static int decode_pdp_info(uint8_t *data, size_t data_len, pdp_info->context_id = osmo_decode_big_endian(value, value_len); break; - case OSMO_GSUP_PDP_TYPE_IE: - pdp_info->pdp_type = - osmo_decode_big_endian(value, value_len) & 0x0fff; + case OSMO_GSUP_PDP_ADDRESS_IE: + if ((rc = decode_pdp_address(value, value_len, pdp_info)) < 0) + return rc; break; case OSMO_GSUP_ACCESS_POINT_NAME_IE: @@ -262,7 +322,7 @@ static int decode_auth_info(uint8_t *data, size_t data_len, parse_error: LOGP(DLGSUP, LOGL_ERROR, - "GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len); + "GSUP IE type %d, length %zu invalid in auth info\n", iei, value_len); return -1; } @@ -353,7 +413,7 @@ int osmo_gsup_decode(const uint8_t *const_data, size_t data_len, switch (iei) { case OSMO_GSUP_IMSI_IE: - case OSMO_GSUP_PDP_TYPE_IE: + case OSMO_GSUP_PDP_ADDRESS_IE: case OSMO_GSUP_ACCESS_POINT_NAME_IE: case OSMO_GSUP_SRES_IE: case OSMO_GSUP_KC_IE: @@ -446,6 +506,11 @@ int osmo_gsup_decode(const uint8_t *const_data, size_t data_len, gsup_msg->rand = value; break; + case OSMO_GSUP_PCO_IE: + gsup_msg->pco = value; + gsup_msg->pco_len = value_len; + break; + case OSMO_GSUP_MSISDN_IE: gsup_msg->msisdn_enc = value; gsup_msg->msisdn_enc_len = value_len; @@ -597,11 +662,45 @@ static void encode_pdp_info(struct msgb *msg, enum osmo_gsup_iei iei, u8 = pdp_info->context_id; msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8); - if (pdp_info->pdp_type) { - msgb_tlv_put(msg, OSMO_GSUP_PDP_TYPE_IE, - OSMO_GSUP_PDP_TYPE_SIZE, - osmo_encode_big_endian(pdp_info->pdp_type | 0xf000, - OSMO_GSUP_PDP_TYPE_SIZE)); + if (pdp_info->pdp_type_org == PDP_TYPE_ORG_IETF) { + struct gsm48_pdp_address pdp_addr; + uint8_t pdp_addr_len = 2; + pdp_addr.spare = 0x0f; + pdp_addr.organization = pdp_info->pdp_type_org; + pdp_addr.type = pdp_info->pdp_type_nr; + + switch (pdp_info->pdp_type_nr) { + case PDP_TYPE_N_IETF_IPv4: + if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET) { + pdp_addr.ietf.v4 = pdp_info->pdp_address[0].u.sin.sin_addr.s_addr; + pdp_addr_len += sizeof(pdp_addr.ietf.v4); + } + break; + case PDP_TYPE_N_IETF_IPv6: + if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET6) { + memcpy(pdp_addr.ietf.v6, + &pdp_info->pdp_address[0].u.sin6.sin6_addr, + sizeof(pdp_addr.ietf.v6)); + pdp_addr_len += sizeof(pdp_addr.ietf.v6); + } + break; + case PDP_TYPE_N_IETF_IPv4v6: + if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET) { + pdp_addr.ietf.v4v6.v4 = pdp_info->pdp_address[0].u.sin.sin_addr.s_addr; + pdp_addr_len += sizeof(pdp_addr.ietf.v4v6.v4); + } + if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET6) { + memcpy(pdp_addr.ietf.v4v6.v6, + &pdp_info->pdp_address[1].u.sin6.sin6_addr, + sizeof(pdp_addr.ietf.v4v6.v6)); + pdp_addr_len += sizeof(pdp_addr.ietf.v4v6.v6); + } + break; + } + + msgb_tlv_put(msg, OSMO_GSUP_PDP_ADDRESS_IE, + pdp_addr_len, + (const uint8_t *)&pdp_addr); } if (pdp_info->apn_enc) { @@ -778,6 +877,11 @@ int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg) if (gsup_msg->rand) msgb_tlv_put(msg, OSMO_GSUP_RAND_IE, 16, gsup_msg->rand); + if (gsup_msg->pco && gsup_msg->pco_len > 0) { + if (gsup_msg->pco_len > OSMO_GSUP_MAX_PCO_LEN) + return -EINVAL; + msgb_tlv_put(msg, OSMO_GSUP_PCO_IE, gsup_msg->pco_len, gsup_msg->pco); + } if (gsup_msg->cn_domain) { uint8_t dn = gsup_msg->cn_domain; msgb_tlv_put(msg, OSMO_GSUP_CN_DOMAIN_IE, 1, &dn); @@ -911,6 +1015,7 @@ const struct value_string osmo_gsup_message_class_names[] = { { OSMO_GSUP_MESSAGE_CLASS_SMS, "SMS" }, { OSMO_GSUP_MESSAGE_CLASS_USSD, "USSD" }, { OSMO_GSUP_MESSAGE_CLASS_INTER_MSC, "Inter-MSC" }, + { OSMO_GSUP_MESSAGE_CLASS_IPSEC_EPDG, "IPsec-ePDG" }, {} }; |