From 57238889ebf046cdf9381036d9bc1394a574ed57 Mon Sep 17 00:00:00 2001 From: Stefan Sperling Date: Thu, 19 Jul 2018 19:27:59 +0200 Subject: fix support for multiple IPCP in PDP protocol configuration options Parse multiple IPCP IEs embedded in Protocol Configuration Options, and return IPCP responses for all of them. Makes the associated TTCN3 GGSN test pass. Depends: Ia1410abb216831864042f95679330f4508e1af3d Change-Id: I51ecab4e35f3ee638e68ca773b0da90cc0294ab0 Related: OS#3319 --- ggsn/ggsn.c | 77 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 30584ef..39695b9 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -463,9 +463,9 @@ enum pco_protocols { }; /* determine if PCO contains given protocol */ -static uint8_t *pco_contains_proto(struct ul255_t *pco, uint16_t prot, size_t prot_minlen) +static uint8_t *pco_contains_proto(struct ul255_t *pco, size_t offset, uint16_t prot, size_t prot_minlen) { - uint8_t *cur = pco->v + 1; + uint8_t *cur = pco->v + 1 + offset; /* iterate over PCO and check if protocol contained */ while (cur + 3 <= pco->v + pco->l) { @@ -510,47 +510,52 @@ static void build_ipcp_pco(struct apn_ctx *apn, struct pdp_t *pdp, struct msgb * uint8_t *ipcp; uint16_t ipcp_len; uint8_t *len1, *len2, *pco_ipcp; - uint8_t *start = msg->tail; unsigned int len_appended; ptrdiff_t consumed; - size_t remain; + size_t remain, offset = 0; /* pco_contains_proto() returns a potentially unaligned pointer into pco_req->v (see OS#3194) */ - if (!(pco_ipcp = pco_contains_proto(&pdp->pco_req, PCO_P_IPCP, sizeof(struct ipcp_hdr)))) - return; - - ipcp = (pco_ipcp + 3); /* 2=type + 1=len */ - consumed = (ipcp - &pdp->pco_req.v[0]); - remain = sizeof(pdp->pco_req.v) - consumed; - ipcp_len = osmo_load16be(ipcp + 2); /* 1=code + 1=id */ - if (remain < 0 || remain < ipcp_len) - return; - - /* Three byte T16L header */ - msgb_put_u16(msg, 0x8021); /* IPCP */ - len1 = msgb_put(msg, 1); /* Length of contents: delay */ + pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr)); + while (pco_ipcp) { + uint8_t *start = msg->tail; + + ipcp = (pco_ipcp + 3); /* 2=type + 1=len */ + consumed = (ipcp - &pdp->pco_req.v[0]); + remain = sizeof(pdp->pco_req.v) - consumed; + ipcp_len = osmo_load16be(ipcp + 2); /* 1=code + 1=id */ + if (remain < 0 || remain < ipcp_len) + return; + + /* Three byte T16L header */ + msgb_put_u16(msg, 0x8021); /* IPCP */ + len1 = msgb_put(msg, 1); /* Length of contents: delay */ + + msgb_put_u8(msg, 0x02); /* ACK */ + msgb_put_u8(msg, ipcp[1]); /* ID: Needs to match request */ + msgb_put_u8(msg, 0x00); /* Length MSB */ + len2 = msgb_put(msg, 1); /* Length LSB: delay */ + + if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) { + msgb_put_u8(msg, 0x81); /* DNS1 Tag */ + msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */ + msgb_put_u32(msg, ntohl(dns1->v4.s_addr)); + } - msgb_put_u8(msg, 0x02); /* ACK */ - msgb_put_u8(msg, ipcp[1]); /* ID: Needs to match request */ - msgb_put_u8(msg, 0x00); /* Length MSB */ - len2 = msgb_put(msg, 1); /* Length LSB: delay */ + if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) { + msgb_put_u8(msg, 0x83); /* DNS2 Tag */ + msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */ + msgb_put_u32(msg, ntohl(dns2->v4.s_addr)); + } - if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) { - msgb_put_u8(msg, 0x81); /* DNS1 Tag */ - msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */ - msgb_put_u32(msg, ntohl(dns1->v4.s_addr)); - } + /* patch in length values */ + len_appended = msg->tail - start; + *len1 = len_appended - 3; + *len2 = len_appended - 3; - if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) { - msgb_put_u8(msg, 0x83); /* DNS2 Tag */ - msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */ - msgb_put_u32(msg, ntohl(dns2->v4.s_addr)); + offset += 3 + ipcp_len; + pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr)); } - /* patch in length values */ - len_appended = msg->tail - start; - *len1 = len_appended - 3; - *len2 = len_appended - 3; } /* process one PCO request from a MS/UE, putting together the proper responses */ @@ -566,7 +571,7 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp) if (peer_v4) build_ipcp_pco(apn, pdp, msg); - if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv6_ADDR, 0)) { + if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv6_ADDR, 0)) { for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) { struct in46_addr *i46a = &apn->v6.cfg.dns[i]; if (i46a->len != 16) @@ -575,7 +580,7 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp) } } - if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv4_ADDR, 0)) { + if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv4_ADDR, 0)) { for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) { struct in46_addr *i46a = &apn->v4.cfg.dns[i]; if (i46a->len != 4) -- cgit v1.2.3