aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)