aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Sperling <ssperling@sysmocom.de>2018-07-19 19:27:59 +0200
committerStefan Sperling <ssperling@sysmocom.de>2018-07-19 19:45:01 +0200
commit57238889ebf046cdf9381036d9bc1394a574ed57 (patch)
treee1fe15951f3d44c422f173f573f7d8f0fb4e3778
parentd70ab97fa4be4c59f9ebbae19ec8b8f64990a538 (diff)
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
-rw-r--r--ggsn/ggsn.c77
1 files 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)