Change in ...osmo-ggsn[master]: ggsn: Move PCO handling code into its own file

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Tue Aug 20 10:31:46 UTC 2019


pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ggsn/+/15246


Change subject: ggsn: Move PCO handling code into its own file
......................................................................

ggsn: Move PCO handling code into its own file

This way ggsn.c is shrinked in size and get rid of a lot of code there,
which is of no interest unless the reader is interesting in that really
specific part.

Change-Id: Ieaa7e71f17c7fd9377c76ef53362eab596d669a6
---
M ggsn/Makefile.am
M ggsn/ggsn.c
M ggsn/ggsn.h
A ggsn/pco.c
A ggsn/pco.h
5 files changed, 341 insertions(+), 312 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ggsn refs/changes/46/15246/1

diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am
index eca385f..022cdef 100644
--- a/ggsn/Makefile.am
+++ b/ggsn/Makefile.am
@@ -12,4 +12,4 @@
 endif
 
 osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
-osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h
+osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h pco.c pco.h
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 593c319..7832338 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -68,6 +68,7 @@
 #include "../gtp/pdp.h"
 #include "../gtp/gtp.h"
 #include "icmpv6.h"
+#include "pco.h"
 #include "ggsn.h"
 
 void *tall_ggsn_ctx;
@@ -79,14 +80,6 @@
 struct ul255_t qos;
 struct ul255_t apn;
 
-#define LOGPAPN(level, apn, fmt, args...)			\
-	LOGP(DGGSN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)
-
-#define LOGPGGSN(level, ggsn, fmt, args...)			\
-	LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
-
-#define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## args)
-
 static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
 static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);
 
@@ -396,92 +389,12 @@
 	return 0;
 }
 
-#include <osmocom/gsm/tlv.h>
-
-/* RFC 1332 */
-enum ipcp_options {
-	IPCP_OPT_IPADDR = 3,
-	IPCP_OPT_PRIMARY_DNS = 129,
-	IPCP_OPT_SECONDARY_DNS = 131,
-};
-
-struct ipcp_option_hdr {
-	uint8_t type;
-	uint8_t len;
-	uint8_t data[0];
-} __attribute__ ((packed));
-
-struct ipcp_hdr {
-	uint8_t code;
-	uint8_t id;
-	uint16_t len;
-	uint8_t options[0];
-} __attribute__ ((packed));
-
-/* determine if IPCP contains given option */
-static const uint8_t *ipcp_contains_option(const struct ipcp_hdr *ipcp, size_t ipcp_len,
-					   enum ipcp_options opt, size_t opt_minlen)
-{
-	const uint8_t *cur_opt = ipcp->options;
-
-	/* iterate over Options and check if protocol contained */
-	while (cur_opt + sizeof(struct ipcp_option_hdr) <= (uint8_t*)ipcp + ipcp_len) {
-		const struct ipcp_option_hdr *cur_opt_hdr = (const struct ipcp_option_hdr *)cur_opt;
-		/* length value includes 2 bytes type/length */
-		if (cur_opt_hdr->len < sizeof(struct ipcp_option_hdr))
-			return NULL;
-		if (cur_opt_hdr->type == opt &&
-		    cur_opt_hdr->len >= sizeof(struct ipcp_option_hdr) + opt_minlen)
-			return cur_opt;
-		cur_opt += cur_opt_hdr->len;
-	}
-	return NULL;
-}
-
-/* 3GPP TS 24.008 10.6.5.3 */
-enum pco_protocols {
-	PCO_P_LCP		= 0xC021,
-	PCO_P_PAP		= 0xC023,
-	PCO_P_CHAP		= 0xC223,
-	PCO_P_IPCP		= 0x8021,
-	PCO_P_PCSCF_ADDR	= 0x0001,
-	PCO_P_IM_CN_SS_F	= 0x0002,
-	PCO_P_DNS_IPv6_ADDR	= 0x0003,
-	PCO_P_POLICY_CTRL_REJ	= 0x0004,	/* only in Network->MS */
-	PCO_P_MS_SUP_NETREQ_BCI	= 0x0005,
-	/* reserved */
-	PCO_P_DSMIPv6_HA_ADDR	= 0x0007,
-	PCO_P_DSMIPv6_HN_PREF	= 0x0008,
-	PCO_P_DSMIPv6_v4_HA_ADDR= 0x0009,
-	PCO_P_IP_ADDR_VIA_NAS	= 0x000a,	/* only MS->Network */
-	PCO_P_IPv4_ADDR_VIA_DHCP= 0x000b,	/* only MS->Netowrk */
-	PCO_P_PCSCF_IPv4_ADDR	= 0x000c,
-	PCO_P_DNS_IPv4_ADDR	= 0x000d,
-	PCO_P_MSISDN		= 0x000e,
-	PCO_P_IFOM_SUPPORT	= 0x000f,
-	PCO_P_IPv4_LINK_MTU	= 0x0010,
-	PCO_P_MS_SUPP_LOC_A_TFT	= 0x0011,
-	PCO_P_PCSCF_RESEL_SUP	= 0x0012,	/* only MS->Network */
-	PCO_P_NBIFOM_REQ	= 0x0013,
-	PCO_P_NBIFOM_MODE	= 0x0014,
-	PCO_P_NONIP_LINK_MTU	= 0x0015,
-	PCO_P_APN_RATE_CTRL_SUP	= 0x0016,
-	PCO_P_PS_DATA_OFF_UE	= 0x0017,
-	PCO_P_REL_DATA_SVC	= 0x0018,
-};
-
-struct pco_element {
-	uint16_t protocol_id;	/* network byte order */
-	uint8_t length;		/* length of data below */
-	uint8_t data[0];
-} __attribute__((packed));
-
 /*! Get the peer of pdp based on IP version used.
  *  \param[in] pdp PDP context to select the peer from.
  *  \param[in] v4v6 IP version to select. Valid values are 4 and 6.
  *  \returns The selected peer matching the given IP version. NULL if not present.
  */
-static struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) {
+struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) {
 	uint8_t i;
 
 	for (i = 0; i < 2; i++) {
@@ -496,228 +409,6 @@
 	return NULL;
 }
 
-/* RFC 1334, section 3.2. Packet Format */
-struct pap_element {
-	uint8_t code;
-	uint8_t id;
-	uint16_t len; /* length including header */
-	uint8_t data[0];
-} __attribute__((packed));
-
-enum pap_code {
-	PAP_CODE_AUTH_REQ = 1,
-	PAP_CODE_AUTH_ACK = 2,
-	PAP_CODE_AUTH_NAK = 3,
-};
-
-static const char *pap_welcome = "Welcome to OsmoGGSN " PACKAGE_VERSION;
-
-/* Handle PAP protocol according to RFC 1334 */
-static void process_pco_element_pap(const struct pco_element *pco_in, struct msgb *resp,
-				    const struct apn_ctx *apn, struct pdp_t *pdp)
-{
-	const struct pap_element *pap_in = (const struct pap_element *) pco_in->data;
-	uint16_t pap_in_len;
-	uint8_t peer_id_len;
-	const uint8_t *peer_id;
-	unsigned int pap_welcome_len;
-	uint8_t pap_out_size;
-	struct pap_element *pap_out;
-
-	if (pco_in->length < sizeof(struct pap_element))
-		goto ret_broken;
-
-	pap_in_len = osmo_load16be(&pap_in->len);
-	if (pco_in->length < pap_in_len)
-		goto ret_broken;
-	/* "pco_in->length > pap_in_len" is allowed: RFC1334 2.2 states:
-	   "Octets outside the range of the Length field should be treated as
-	   Data Link Layer padding and should be ignored on reception."
-	 */
-
-	switch (pap_in->code) {
-	case PAP_CODE_AUTH_REQ:
-		if (pap_in_len < sizeof(struct pap_element) + 1)
-			goto ret_broken_auth;
-		peer_id_len = pap_in->data[0];
-		if (pap_in_len < sizeof(struct pap_element) + 1 + peer_id_len)
-			goto ret_broken_auth;
-		peer_id = &pap_in->data[1];
-		LOGPPDP(LOGL_DEBUG, pdp, "PCO PAP PeerId = %s, ACKing\n",
-			osmo_quote_str((const char *)peer_id, peer_id_len));
-		/* Password-Length + Password following here, but we don't care */
-
-		/* Prepare response, we ACK all of them: */
-		pap_welcome_len = strlen(pap_welcome);
-		/* +1: Length field of pap_welcome Message */
-		pap_out_size = sizeof(struct pap_element) + 1 + pap_welcome_len;
-		pap_out = alloca(pap_out_size);
-		pap_out->code = PAP_CODE_AUTH_ACK;
-		pap_out->id = pap_in->id;
-		pap_out->len = htons(pap_out_size);
-		pap_out->data[0] = pap_welcome_len;
-		memcpy(pap_out->data+1, pap_welcome, pap_welcome_len);
-		msgb_t16lv_put(resp, PCO_P_PAP, pap_out_size, (uint8_t *) pap_out);
-		break;
-	case PAP_CODE_AUTH_ACK:
-	case PAP_CODE_AUTH_NAK:
-	default:
-		LOGPPDP(LOGL_NOTICE, pdp, "Unsupported PAP PCO Code %u, ignoring\n", pap_in->code);
-		break;
-	}
-	return;
-
-ret_broken_auth:
-	LOGPPDP(LOGL_NOTICE, pdp, "Invalid PAP AuthenticateReq: %s, ignoring\n",
-		osmo_hexdump_nospc((const uint8_t *)pco_in, pco_in->length));
-	return;
-
-ret_broken:
-	LOGPPDP(LOGL_NOTICE, pdp, "Invalid PAP PCO Length: %s, ignoring\n",
-		osmo_hexdump_nospc((const uint8_t *)pco_in, pco_in->length));
-}
-
-static void process_pco_element_ipcp(const struct pco_element *pco_elem, struct msgb *resp,
-				     const struct apn_ctx *apn, struct pdp_t *pdp)
-{
-	struct ippoolm_t *peer_v4 = pdp_get_peer_ipv(pdp, false);
-	const struct in46_addr *dns1 = &apn->v4.cfg.dns[0];
-	const struct in46_addr *dns2 = &apn->v4.cfg.dns[1];
-	uint8_t *start = resp->tail;
-	const struct ipcp_hdr *ipcp;
-	uint16_t ipcp_len;
-	uint8_t *len1, *len2;
-	unsigned int len_appended;
-	ptrdiff_t consumed;
-	size_t remain;
-
-	if (!peer_v4) {
-		LOGPPDP(LOGL_ERROR, pdp, "IPCP but no IPv4 type ?!?\n");
-		return;
-	}
-
-	ipcp = (const struct ipcp_hdr *)pco_elem->data;
-	consumed = (pco_elem->data - &pdp->pco_req.v[0]);
-	remain = sizeof(pdp->pco_req.v) - consumed;
-	ipcp_len = osmo_load16be(&ipcp->len);
-	if (remain < 0 || remain < ipcp_len) {
-		LOGPPDP(LOGL_ERROR, pdp, "Malformed IPCP, ignoring\n");
-		return;
-	}
-
-	/* Three byte T16L header */
-	msgb_put_u16(resp, 0x8021);	/* IPCP */
-	len1 = msgb_put(resp, 1);	/* Length of contents: delay */
-
-	msgb_put_u8(resp, 0x02);	/* ACK */
-	msgb_put_u8(resp, ipcp->id);	/* ID: Needs to match request */
-	msgb_put_u8(resp, 0x00);	/* Length MSB */
-	len2 = msgb_put(resp, 1);	/* Length LSB: delay */
-
-	if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) {
-		msgb_put_u8(resp, 0x81);		/* DNS1 Tag */
-		msgb_put_u8(resp, 2 + dns1->len);	/* DNS1 Length, incl. TL */
-		msgb_put_u32(resp, ntohl(dns1->v4.s_addr));
-	}
-
-	if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) {
-		msgb_put_u8(resp, 0x83);		/* DNS2 Tag */
-		msgb_put_u8(resp, 2 + dns2->len);	/* DNS2 Length, incl. TL */
-		msgb_put_u32(resp, ntohl(dns2->v4.s_addr));
-	}
-
-	/* patch in length values */
-	len_appended = resp->tail - start;
-	*len1 = len_appended - 3;
-	*len2 = len_appended - 3;
-}
-
-static void process_pco_element_dns_ipv6(const struct pco_element *pco_elem, struct msgb *resp,
-					 const struct apn_ctx *apn, struct pdp_t *pdp)
-{
-	unsigned int i;
-	const uint8_t *tail = resp->tail;
-
-	for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
-		const struct in46_addr *i46a = &apn->v6.cfg.dns[i];
-		if (i46a->len != 16)
-			continue;
-		msgb_t16lv_put(resp, PCO_P_DNS_IPv6_ADDR, i46a->len, i46a->v6.s6_addr);
-	}
-	if (resp->tail == tail)
-		LOGPPDP(LOGL_NOTICE, pdp, "MS requested IPv6 DNS, but APN has none configured\n");
-}
-
-static void process_pco_element_dns_ipv4(const struct pco_element *pco_elem, struct msgb *resp,
-					 const struct apn_ctx *apn, struct pdp_t *pdp)
-{
-	unsigned int i;
-	const uint8_t *tail = resp->tail;
-
-	for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
-		const struct in46_addr *i46a = &apn->v4.cfg.dns[i];
-		if (i46a->len != 4)
-			continue;
-		msgb_t16lv_put(resp, PCO_P_DNS_IPv4_ADDR, i46a->len, (uint8_t *)&i46a->v4);
-	}
-	if (resp->tail == tail)
-		LOGPPDP(LOGL_NOTICE, pdp, "MS requested IPv4 DNS, but APN has none configured\n");
-}
-
-static void process_pco_element(const struct pco_element *pco_elem, struct msgb *resp,
-				const struct apn_ctx *apn, struct pdp_t *pdp)
-{
-	uint16_t protocol_id = osmo_load16be(&pco_elem->protocol_id);
-
-	LOGPPDP(LOGL_DEBUG, pdp, "PCO Protocol 0x%04x\n", protocol_id);
-	switch (protocol_id) {
-	case PCO_P_PAP:
-		process_pco_element_pap(pco_elem, resp, apn, pdp);
-		break;
-	case PCO_P_IPCP:
-		process_pco_element_ipcp(pco_elem, resp, apn, pdp);
-		break;
-	case PCO_P_DNS_IPv6_ADDR:
-		process_pco_element_dns_ipv6(pco_elem, resp, apn, pdp);
-		break;
-	case PCO_P_DNS_IPv4_ADDR:
-		process_pco_element_dns_ipv4(pco_elem, resp, apn, pdp);
-		break;
-	default:
-		LOGPPDP(LOGL_INFO, pdp, "Unknown/Unimplemented PCO Protocol 0x%04x: %s\n",
-			protocol_id, osmo_hexdump_nospc(pco_elem->data, pco_elem->length));
-		break;
-	}
-}
-
-/* process one PCO request from a MS/UE, putting together the proper responses */
-static void process_pco(const struct apn_ctx *apn, struct pdp_t *pdp)
-{
-	struct msgb *resp = msgb_alloc(256, "PCO.resp");
-	const struct ul255_t *pco = &pdp->pco_req;
-	const struct pco_element *pco_elem;
-	const uint8_t *cur;
-
-	/* build the header of the PCO response */
-	OSMO_ASSERT(resp);
-	msgb_put_u8(resp, 0x80); /* ext-bit + configuration protocol byte */
-
-	/* iterate over the PCO elements in the request; call process_pco_element() for each */
-	for (cur = pco->v + 1, pco_elem = (const struct pco_element *) cur;
-	     cur + sizeof(struct pco_element) <= pco->v + pco->l;
-	     cur += pco_elem->length + sizeof(*pco_elem), pco_elem = (const struct pco_element *) cur) {
-		process_pco_element(pco_elem, resp, apn, pdp);
-	}
-
-	/* copy the PCO response msgb and copy its contents over to the PDP context */
-	if (msgb_length(resp) > 1) {
-		memcpy(pdp->pco_neg.v, msgb_data(resp), msgb_length(resp));
-		pdp->pco_neg.l = msgb_length(resp);
-	} else
-		pdp->pco_neg.l = 0;
-	msgb_free(resp);
-}
-
 static bool apn_supports_ipv4(const struct apn_ctx *apn)
 {
 	if (apn->v4.cfg.static_prefix.addr.len  || apn->v4.cfg.dynamic_prefix.addr.len)
diff --git a/ggsn/ggsn.h b/ggsn/ggsn.h
index 09cd0f6..a37381f 100644
--- a/ggsn/ggsn.h
+++ b/ggsn/ggsn.h
@@ -141,3 +141,12 @@
 extern int ggsn_stop(struct ggsn_ctx *ggsn);
 extern int apn_start(struct apn_ctx *apn);
 extern int apn_stop(struct apn_ctx *apn);
+extern struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6);
+
+#define LOGPAPN(level, apn, fmt, args...)			\
+	LOGP(DGGSN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)
+
+#define LOGPGGSN(level, ggsn, fmt, args...)			\
+	LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
+
+#define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## args)
diff --git a/ggsn/pco.c b/ggsn/pco.c
new file mode 100644
index 0000000..a55b768
--- /dev/null
+++ b/ggsn/pco.c
@@ -0,0 +1,248 @@
+/*
+ * PCO parsing related code
+ * Copyright 2019 sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ *
+ * The contents of this file may be used under the terms of the GNU
+ * General Public License Version 2, provided that the above copyright
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
+ *
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+
+#include "pco.h"
+#include "ggsn.h"
+
+/* determine if IPCP contains given option */
+static const uint8_t *ipcp_contains_option(const struct ipcp_hdr *ipcp, size_t ipcp_len,
+					   enum ipcp_options opt, size_t opt_minlen)
+{
+	const uint8_t *cur_opt = ipcp->options;
+
+	/* iterate over Options and check if protocol contained */
+	while (cur_opt + sizeof(struct ipcp_option_hdr) <= (uint8_t*)ipcp + ipcp_len) {
+		const struct ipcp_option_hdr *cur_opt_hdr = (const struct ipcp_option_hdr *)cur_opt;
+		/* length value includes 2 bytes type/length */
+		if (cur_opt_hdr->len < sizeof(struct ipcp_option_hdr))
+			return NULL;
+		if (cur_opt_hdr->type == opt &&
+		    cur_opt_hdr->len >= sizeof(struct ipcp_option_hdr) + opt_minlen)
+			return cur_opt;
+		cur_opt += cur_opt_hdr->len;
+	}
+	return NULL;
+}
+
+
+static const char *pap_welcome = "Welcome to OsmoGGSN " PACKAGE_VERSION;
+
+/* Handle PAP protocol according to RFC 1334 */
+static void process_pco_element_pap(const struct pco_element *pco_in, struct msgb *resp,
+				    const struct apn_ctx *apn, struct pdp_t *pdp)
+{
+	const struct pap_element *pap_in = (const struct pap_element *) pco_in->data;
+	uint16_t pap_in_len;
+	uint8_t peer_id_len;
+	const uint8_t *peer_id;
+	unsigned int pap_welcome_len;
+	uint8_t pap_out_size;
+	struct pap_element *pap_out;
+
+	if (pco_in->length < sizeof(struct pap_element))
+		goto ret_broken;
+
+	pap_in_len = osmo_load16be(&pap_in->len);
+	if (pco_in->length < pap_in_len)
+		goto ret_broken;
+	/* "pco_in->length > pap_in_len" is allowed: RFC1334 2.2 states:
+	   "Octets outside the range of the Length field should be treated as
+	   Data Link Layer padding and should be ignored on reception."
+	 */
+
+	switch (pap_in->code) {
+	case PAP_CODE_AUTH_REQ:
+		if (pap_in_len < sizeof(struct pap_element) + 1)
+			goto ret_broken_auth;
+		peer_id_len = pap_in->data[0];
+		if (pap_in_len < sizeof(struct pap_element) + 1 + peer_id_len)
+			goto ret_broken_auth;
+		peer_id = &pap_in->data[1];
+		LOGPPDP(LOGL_DEBUG, pdp, "PCO PAP PeerId = %s, ACKing\n",
+			osmo_quote_str((const char *)peer_id, peer_id_len));
+		/* Password-Length + Password following here, but we don't care */
+
+		/* Prepare response, we ACK all of them: */
+		pap_welcome_len = strlen(pap_welcome);
+		/* +1: Length field of pap_welcome Message */
+		pap_out_size = sizeof(struct pap_element) + 1 + pap_welcome_len;
+		pap_out = alloca(pap_out_size);
+		pap_out->code = PAP_CODE_AUTH_ACK;
+		pap_out->id = pap_in->id;
+		pap_out->len = htons(pap_out_size);
+		pap_out->data[0] = pap_welcome_len;
+		memcpy(pap_out->data+1, pap_welcome, pap_welcome_len);
+		msgb_t16lv_put(resp, PCO_P_PAP, pap_out_size, (uint8_t *) pap_out);
+		break;
+	case PAP_CODE_AUTH_ACK:
+	case PAP_CODE_AUTH_NAK:
+	default:
+		LOGPPDP(LOGL_NOTICE, pdp, "Unsupported PAP PCO Code %u, ignoring\n", pap_in->code);
+		break;
+	}
+	return;
+
+ret_broken_auth:
+	LOGPPDP(LOGL_NOTICE, pdp, "Invalid PAP AuthenticateReq: %s, ignoring\n",
+		osmo_hexdump_nospc((const uint8_t *)pco_in, pco_in->length));
+	return;
+
+ret_broken:
+	LOGPPDP(LOGL_NOTICE, pdp, "Invalid PAP PCO Length: %s, ignoring\n",
+		osmo_hexdump_nospc((const uint8_t *)pco_in, pco_in->length));
+}
+
+static void process_pco_element_ipcp(const struct pco_element *pco_elem, struct msgb *resp,
+				     const struct apn_ctx *apn, struct pdp_t *pdp)
+{
+	struct ippoolm_t *peer_v4 = pdp_get_peer_ipv(pdp, false);
+	const struct in46_addr *dns1 = &apn->v4.cfg.dns[0];
+	const struct in46_addr *dns2 = &apn->v4.cfg.dns[1];
+	uint8_t *start = resp->tail;
+	const struct ipcp_hdr *ipcp;
+	uint16_t ipcp_len;
+	uint8_t *len1, *len2;
+	unsigned int len_appended;
+	ptrdiff_t consumed;
+	size_t remain;
+
+	if (!peer_v4) {
+		LOGPPDP(LOGL_ERROR, pdp, "IPCP but no IPv4 type ?!?\n");
+		return;
+	}
+
+	ipcp = (const struct ipcp_hdr *)pco_elem->data;
+	consumed = (pco_elem->data - &pdp->pco_req.v[0]);
+	remain = sizeof(pdp->pco_req.v) - consumed;
+	ipcp_len = osmo_load16be(&ipcp->len);
+	if (remain < 0 || remain < ipcp_len) {
+		LOGPPDP(LOGL_ERROR, pdp, "Malformed IPCP, ignoring\n");
+		return;
+	}
+
+	/* Three byte T16L header */
+	msgb_put_u16(resp, 0x8021);	/* IPCP */
+	len1 = msgb_put(resp, 1);	/* Length of contents: delay */
+
+	msgb_put_u8(resp, 0x02);	/* ACK */
+	msgb_put_u8(resp, ipcp->id);	/* ID: Needs to match request */
+	msgb_put_u8(resp, 0x00);	/* Length MSB */
+	len2 = msgb_put(resp, 1);	/* Length LSB: delay */
+
+	if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) {
+		msgb_put_u8(resp, 0x81);		/* DNS1 Tag */
+		msgb_put_u8(resp, 2 + dns1->len);	/* DNS1 Length, incl. TL */
+		msgb_put_u32(resp, ntohl(dns1->v4.s_addr));
+	}
+
+	if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) {
+		msgb_put_u8(resp, 0x83);		/* DNS2 Tag */
+		msgb_put_u8(resp, 2 + dns2->len);	/* DNS2 Length, incl. TL */
+		msgb_put_u32(resp, ntohl(dns2->v4.s_addr));
+	}
+
+	/* patch in length values */
+	len_appended = resp->tail - start;
+	*len1 = len_appended - 3;
+	*len2 = len_appended - 3;
+}
+
+static void process_pco_element_dns_ipv6(const struct pco_element *pco_elem, struct msgb *resp,
+					 const struct apn_ctx *apn, struct pdp_t *pdp)
+{
+	unsigned int i;
+	const uint8_t *tail = resp->tail;
+
+	for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
+		const struct in46_addr *i46a = &apn->v6.cfg.dns[i];
+		if (i46a->len != 16)
+			continue;
+		msgb_t16lv_put(resp, PCO_P_DNS_IPv6_ADDR, i46a->len, i46a->v6.s6_addr);
+	}
+	if (resp->tail == tail)
+		LOGPPDP(LOGL_NOTICE, pdp, "MS requested IPv6 DNS, but APN has none configured\n");
+}
+
+static void process_pco_element_dns_ipv4(const struct pco_element *pco_elem, struct msgb *resp,
+					 const struct apn_ctx *apn, struct pdp_t *pdp)
+{
+	unsigned int i;
+	const uint8_t *tail = resp->tail;
+
+	for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
+		const struct in46_addr *i46a = &apn->v4.cfg.dns[i];
+		if (i46a->len != 4)
+			continue;
+		msgb_t16lv_put(resp, PCO_P_DNS_IPv4_ADDR, i46a->len, (uint8_t *)&i46a->v4);
+	}
+	if (resp->tail == tail)
+		LOGPPDP(LOGL_NOTICE, pdp, "MS requested IPv4 DNS, but APN has none configured\n");
+}
+
+static void process_pco_element(const struct pco_element *pco_elem, struct msgb *resp,
+				const struct apn_ctx *apn, struct pdp_t *pdp)
+{
+	uint16_t protocol_id = osmo_load16be(&pco_elem->protocol_id);
+
+	LOGPPDP(LOGL_DEBUG, pdp, "PCO Protocol 0x%04x\n", protocol_id);
+	switch (protocol_id) {
+	case PCO_P_PAP:
+		process_pco_element_pap(pco_elem, resp, apn, pdp);
+		break;
+	case PCO_P_IPCP:
+		process_pco_element_ipcp(pco_elem, resp, apn, pdp);
+		break;
+	case PCO_P_DNS_IPv6_ADDR:
+		process_pco_element_dns_ipv6(pco_elem, resp, apn, pdp);
+		break;
+	case PCO_P_DNS_IPv4_ADDR:
+		process_pco_element_dns_ipv4(pco_elem, resp, apn, pdp);
+		break;
+	default:
+		LOGPPDP(LOGL_INFO, pdp, "Unknown/Unimplemented PCO Protocol 0x%04x: %s\n",
+			protocol_id, osmo_hexdump_nospc(pco_elem->data, pco_elem->length));
+		break;
+	}
+}
+
+/* process one PCO request from a MS/UE, putting together the proper responses */
+void process_pco(const struct apn_ctx *apn, struct pdp_t *pdp)
+{
+	struct msgb *resp = msgb_alloc(256, "PCO.resp");
+	const struct ul255_t *pco = &pdp->pco_req;
+	const struct pco_element *pco_elem;
+	const uint8_t *cur;
+
+	/* build the header of the PCO response */
+	OSMO_ASSERT(resp);
+	msgb_put_u8(resp, 0x80); /* ext-bit + configuration protocol byte */
+
+	/* iterate over the PCO elements in the request; call process_pco_element() for each */
+	for (cur = pco->v + 1, pco_elem = (const struct pco_element *) cur;
+	     cur + sizeof(struct pco_element) <= pco->v + pco->l;
+	     cur += pco_elem->length + sizeof(*pco_elem), pco_elem = (const struct pco_element *) cur) {
+		process_pco_element(pco_elem, resp, apn, pdp);
+	}
+
+	/* copy the PCO response msgb and copy its contents over to the PDP context */
+	if (msgb_length(resp) > 1) {
+		memcpy(pdp->pco_neg.v, msgb_data(resp), msgb_length(resp));
+		pdp->pco_neg.l = msgb_length(resp);
+	} else
+		pdp->pco_neg.l = 0;
+	msgb_free(resp);
+}
diff --git a/ggsn/pco.h b/ggsn/pco.h
new file mode 100644
index 0000000..7ebe05a
--- /dev/null
+++ b/ggsn/pco.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <stdint.h>
+
+#include "../gtp/pdp.h"
+
+/* 3GPP TS 24.008 10.6.5.3 */
+enum pco_protocols {
+	PCO_P_LCP		= 0xC021,
+	PCO_P_PAP		= 0xC023,
+	PCO_P_CHAP		= 0xC223,
+	PCO_P_IPCP		= 0x8021,
+	PCO_P_PCSCF_ADDR	= 0x0001,
+	PCO_P_IM_CN_SS_F	= 0x0002,
+	PCO_P_DNS_IPv6_ADDR	= 0x0003,
+	PCO_P_POLICY_CTRL_REJ	= 0x0004,	/* only in Network->MS */
+	PCO_P_MS_SUP_NETREQ_BCI	= 0x0005,
+	/* reserved */
+	PCO_P_DSMIPv6_HA_ADDR	= 0x0007,
+	PCO_P_DSMIPv6_HN_PREF	= 0x0008,
+	PCO_P_DSMIPv6_v4_HA_ADDR= 0x0009,
+	PCO_P_IP_ADDR_VIA_NAS	= 0x000a,	/* only MS->Network */
+	PCO_P_IPv4_ADDR_VIA_DHCP= 0x000b,	/* only MS->Netowrk */
+	PCO_P_PCSCF_IPv4_ADDR	= 0x000c,
+	PCO_P_DNS_IPv4_ADDR	= 0x000d,
+	PCO_P_MSISDN		= 0x000e,
+	PCO_P_IFOM_SUPPORT	= 0x000f,
+	PCO_P_IPv4_LINK_MTU	= 0x0010,
+	PCO_P_MS_SUPP_LOC_A_TFT	= 0x0011,
+	PCO_P_PCSCF_RESEL_SUP	= 0x0012,	/* only MS->Network */
+	PCO_P_NBIFOM_REQ	= 0x0013,
+	PCO_P_NBIFOM_MODE	= 0x0014,
+	PCO_P_NONIP_LINK_MTU	= 0x0015,
+	PCO_P_APN_RATE_CTRL_SUP	= 0x0016,
+	PCO_P_PS_DATA_OFF_UE	= 0x0017,
+	PCO_P_REL_DATA_SVC	= 0x0018,
+};
+
+struct pco_element {
+	uint16_t protocol_id;	/* network byte order */
+	uint8_t length;		/* length of data below */
+	uint8_t data[0];
+} __attribute__((packed));
+
+
+/* RFC 1332 */
+enum ipcp_options {
+	IPCP_OPT_IPADDR = 3,
+	IPCP_OPT_PRIMARY_DNS = 129,
+	IPCP_OPT_SECONDARY_DNS = 131,
+};
+
+struct ipcp_option_hdr {
+	uint8_t type;
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct ipcp_hdr {
+	uint8_t code;
+	uint8_t id;
+	uint16_t len;
+	uint8_t options[0];
+} __attribute__ ((packed));
+
+/* RFC 1334, section 3.2. Packet Format */
+struct pap_element {
+	uint8_t code;
+	uint8_t id;
+	uint16_t len; /* length including header */
+	uint8_t data[0];
+} __attribute__((packed));
+
+enum pap_code {
+	PAP_CODE_AUTH_REQ = 1,
+	PAP_CODE_AUTH_ACK = 2,
+	PAP_CODE_AUTH_NAK = 3,
+};
+
+struct apn_ctx;
+void process_pco(const struct apn_ctx *apn, struct pdp_t *pdp);

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-ggsn/+/15246
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Change-Id: Ieaa7e71f17c7fd9377c76ef53362eab596d669a6
Gerrit-Change-Number: 15246
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190820/3f717196/attachment.htm>


More information about the gerrit-log mailing list