[SMF] fix the PCO bug (#1357, #1358)

- SMF can handle PCO ID PAP (0xc023)
- SMF adds DNS servers to PCO IPCP only if UE requested
This commit is contained in:
Sukchan Lee 2022-02-19 16:05:07 +09:00
parent b4f382d360
commit 420c1d5ad3
3 changed files with 126 additions and 20 deletions

View File

@ -487,6 +487,7 @@ int ogs_fqdn_parse(char *dst, char *src, int len);
#define OGS_PCO_PPP_FOR_USE_WITH_IP_PDP_TYPE_OR_IP_PDN_TYPE 0
#define OGS_PCO_ID_INTERNET_PROTOCOL_CONTROL_PROTOCOL 0x8021
#define OGS_PCO_ID_PASSWORD_AUTHENTICATION_PROTOCOL 0xc023
#define OGS_PCO_ID_CHALLENGE_HANDSHAKE_AUTHENTICATION_PROTOCOL 0xc223
#define OGS_PCO_ID_P_CSCF_IPV6_ADDRESS_REQUEST 0x0001
#define OGS_PCO_ID_DNS_SERVER_IPV6_ADDRESS_REQUEST 0x0003
@ -497,6 +498,13 @@ int ogs_fqdn_parse(char *dst, char *src, int len);
#define OGS_PCO_ID_IPV4_LINK_MTU_REQUEST 0x0010
#define OGS_PCO_ID_MS_SUPPORT_LOCAL_ADDR_TFT_INDICATOR 0x0011
#define OGS_PCO_ID_P_CSCF_RE_SELECTION_SUPPORT 0x0012
enum ogs_pco_ipcp_options {
OGS_IPCP_OPT_IPADDR = 3,
OGS_IPCP_OPT_PRIMARY_DNS = 129,
OGS_IPCP_OPT_SECONDARY_DNS = 131,
};
typedef struct ogs_pco_ipcp_options_s {
uint8_t type;
uint8_t len;
@ -511,6 +519,20 @@ typedef struct ogs_pco_ipcp_s {
ogs_pco_ipcp_options_t options[OGS_PCO_MAX_NUM_OF_IPCO_OPTIONS];
} __attribute__ ((packed)) ogs_pco_ipcp_t;
typedef struct ogs_pco_pap_s {
uint8_t code;
uint8_t identifier;
uint16_t len;
uint8_t welcome_len;
char welcome[255];
} __attribute__ ((packed)) ogs_pco_pap_t;
typedef struct ogs_pco_chap_s {
uint8_t code;
uint8_t identifier;
uint16_t len;
} __attribute__ ((packed)) ogs_pco_chap_t;
typedef struct ogs_pco_id_s {
uint16_t id;
uint8_t len;

View File

@ -2442,11 +2442,45 @@ smf_pf_t *smf_pf_next(smf_pf_t *pf)
return ogs_list_next(pf);
}
/*
* The following code is stolen from osmo-ggsn.
* https://github.com/osmocom/osmo-ggsn/blob/master/ggsn/pco.c#L26-L43
*/
/* determine if IPCP contains given option */
static const uint8_t *ipcp_contains_option(
const ogs_pco_ipcp_t *ipcp, size_t ipcp_len,
enum ogs_pco_ipcp_options opt, size_t opt_minlen)
{
const uint8_t *cur_opt = (const uint8_t *)ipcp->options;
/* iterate over Options and check if protocol contained */
while (cur_opt + sizeof(struct ogs_pco_ipcp_options_s) <=
(uint8_t*)ipcp + ipcp_len) {
const struct ogs_pco_ipcp_options_s *cur_opt_hdr =
(const struct ogs_pco_ipcp_options_s *)cur_opt;
/* length value includes 2 bytes type/length */
if (cur_opt_hdr->len < 2)
return NULL;
if (cur_opt_hdr->type == opt &&
cur_opt_hdr->len >= 2 + opt_minlen)
return cur_opt;
cur_opt += cur_opt_hdr->len;
}
return NULL;
}
#include "../version.h"
static const char *pap_welcome = "Welcome to open5gs-smfd " OPEN5GS_VERSION;
int smf_pco_build(uint8_t *pco_buf, uint8_t *buffer, int length)
{
int rv;
ogs_pco_t ue, smf;
ogs_pco_pap_t pco_pap;
ogs_pco_chap_t pco_chap;
ogs_pco_ipcp_t pco_ipcp;
int pco_size = 0;
ogs_ipsubnet_t dns_primary, dns_secondary, dns6_primary, dns6_secondary;
ogs_ipsubnet_t p_cscf, p_cscf6;
int size = 0;
@ -2467,52 +2501,95 @@ int smf_pco_build(uint8_t *pco_buf, uint8_t *buffer, int length)
for (i = 0; i < ue.num_of_id; i++) {
uint8_t *data = ue.ids[i].data;
switch(ue.ids[i].id) {
case OGS_PCO_ID_PASSWORD_AUTHENTICATION_PROTOCOL:
if (data[0] == 1) { /* Code : Authenticate-Request */
memset(&pco_pap, 0, sizeof(ogs_pco_pap_t));
pco_pap.welcome_len = strlen(pap_welcome);
memcpy(pco_pap.welcome, pap_welcome, pco_pap.welcome_len);
pco_size =
4 + /* sizeof(code+identifier+len) */
1 + /* sizeof(welcome_len) */
pco_pap.welcome_len;
pco_pap.code = 2; /* Code : Authenticate-Ack */
pco_pap.identifier = data[1]; /* Identifier */
pco_pap.len = htobe16(pco_size);
smf.ids[smf.num_of_id].id = ue.ids[i].id;
smf.ids[smf.num_of_id].len = pco_size;
smf.ids[smf.num_of_id].data = (uint8_t *)&pco_pap;
smf.num_of_id++;
}
break;
case OGS_PCO_ID_CHALLENGE_HANDSHAKE_AUTHENTICATION_PROTOCOL:
if (data[0] == 2) { /* Code : Response */
memset(&pco_chap, 0, sizeof(ogs_pco_chap_t));
pco_size = 4; /* sizeof(code+identifier+len) */
pco_chap.code = 3; /* Code : Success */
pco_chap.identifier = data[1]; /* Identifier */
pco_chap.len = htobe16(pco_size);
smf.ids[smf.num_of_id].id = ue.ids[i].id;
smf.ids[smf.num_of_id].len = 4;
smf.ids[smf.num_of_id].data =
(uint8_t *)"\x03\x00\x00\x04"; /* Code : Success */
smf.ids[smf.num_of_id].len = pco_size;
smf.ids[smf.num_of_id].data = (uint8_t *)&pco_chap;
smf.num_of_id++;
}
break;
case OGS_PCO_ID_INTERNET_PROTOCOL_CONTROL_PROTOCOL:
if (data[0] == 1) { /* Code : Configuration Request */
uint16_t len = 0;
ogs_pco_ipcp_t *ipcp = (ogs_pco_ipcp_t *)data;
uint16_t in_len = 0;
uint16_t out_len = 0;
int num_of_option = 0;
ogs_assert(smf_self()->dns[0] || smf_self()->dns[1]);
ogs_assert(ipcp);
in_len = be16toh(ipcp->len);
memset(&pco_ipcp, 0, sizeof(ogs_pco_ipcp_t));
pco_ipcp.code = 2; /* Code : Configuration Ack */
pco_ipcp.len = htobe16(len);
len = 4;
out_len = 4;
/* Primary DNS Server IP Address */
if (smf_self()->dns[0]) {
if (smf_self()->dns[0] &&
ipcp_contains_option(ipcp, in_len,
OGS_IPCP_OPT_PRIMARY_DNS, 4)) {
rv = ogs_ipsubnet(
&dns_primary, smf_self()->dns[0], NULL);
ogs_assert(rv == OGS_OK);
pco_ipcp.options[0].type = 129;
pco_ipcp.options[0].len = 6;
pco_ipcp.options[0].addr = dns_primary.sub[0];
len += 6;
pco_ipcp.options[num_of_option].type =
OGS_IPCP_OPT_PRIMARY_DNS;
pco_ipcp.options[num_of_option].len = 6;
pco_ipcp.options[num_of_option].addr = dns_primary.sub[0];
num_of_option++;
out_len += 6;
}
/* Secondary DNS Server IP Address */
if (smf_self()->dns[1]) {
if (smf_self()->dns[1] &&
ipcp_contains_option(ipcp, in_len,
OGS_IPCP_OPT_SECONDARY_DNS, 4)) {
rv = ogs_ipsubnet(
&dns_secondary, smf_self()->dns[1], NULL);
ogs_assert(rv == OGS_OK);
pco_ipcp.options[1].type = 131;
pco_ipcp.options[1].len = 6;
pco_ipcp.options[1].addr = dns_secondary.sub[0];
len += 6;
pco_ipcp.options[num_of_option].type =
OGS_IPCP_OPT_SECONDARY_DNS;
pco_ipcp.options[num_of_option].len = 6;
pco_ipcp.options[num_of_option].addr = dns_secondary.sub[0];
num_of_option++;
out_len += 6;
}
pco_ipcp.len = htobe16(len);
pco_ipcp.len = htobe16(out_len);
smf.ids[smf.num_of_id].id = ue.ids[i].id;
smf.ids[smf.num_of_id].len = len;
smf.ids[smf.num_of_id].len = out_len;
smf.ids[smf.num_of_id].data = (uint8_t *)&pco_ipcp;
smf.num_of_id++;

View File

@ -144,12 +144,19 @@ ogs_pkbuf_t *testesm_build_esm_information_response(test_sess_t *sess)
uint8_t ue_pco[29] =
"\x80\x80\x21\x10\x01\x01\x00\x10\x81\x06\x00\x00\x00\x00"
"\x83\x06\x00\x00\x00\x00\x00\x03\x00\x00\x0a\x00\x00\x0d\x00";
#else
uint8_t ue_pco[94] =
"\x80\xc2\x23"
"\x23\x01\x01\x00\x23\x10\xec\xa3\x90\x00\x3e\xdb\xf9\x17\xbe\xcf"
"\xa8\x14\x8a\xcd\xde\x56\x55\x4d\x54\x53\x5f\x43\x48\x41\x50\x5f"
"\x53\x52\x56\x52\xc2\x23\x15\x02\x01\x00\x15\x10\xb6\xfa\xad\xc5"
"\x6a\x43\x6b\x2f\x0f\x9f\x82\x35\x6e\x07\xd9\xd9\x80\x21\x1c\x01"
"\x00\x00\x1c\x81\x06\x00\x00\x00\x00\x82\x06\x00\x00\x00\x00\x83"
"\x06\x00\x00\x00\x00\x84\x06\x00\x00\x00\x00";
#endif
uint8_t ue_pco[35] =
"\x80\x80\x21\x10\x01\x00\x00\x10\x81\x06\x00\x00\x00\x00"
"\x83\x06\x00\x00\x00\x00\x00\x0c\x00\x00\x0d\x00\x00\x02\x00\x00"
"\x0a\x00\x00\x10\x00";
#endif
test_ue_t *test_ue = NULL;
ogs_pkbuf_t *pkbuf = NULL;