<p>pespin has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-ggsn/+/17828">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">sgsnemu: Fix assumption ipv6 Interface-Identifier of public addr == announced Prefix<br><br>Until now, sgsnemu was able to identify pdp contexts of incoming packets<br>in the tun based on the assumption that the Interface-Identifier part of<br>public IPv6 addresses in incoming packets was equal to the announced<br>prefix path during Create Pdp Context Response (see changes in cb_tun_ind()).<br>This assumption works fine with osmo-ggsn due to implementation details but<br>breaks on other spec-conformant GGSNs.<br><br>In order to fix it, a new placeholder struct pdp_peer_sgsnemu_ctx is<br>introduced which will be assigned to each pdp_t "peer[0]" user-defined<br>pointer. This way, each pdp_t ctx upgrades from having only 1 iphash_t<br>item to 3 (hence being able to match against 3 different ip addresses).<br>This way, in IPv6 we can match against 2 different IP addresses set on<br>the tun iface:<br>* link-local: "fe80::IfId", where IfId is the Interface-Identifier<br> received during Pdp Context Resp and which can be used to communicate<br> with the nearest router (the GGSN).<br>* global: The global IPv6 addr set after SLAAC procedure, containing a<br> the prefix announced by CreatePdpContextResp/RouterAdvertisement and<br> an Interface-Identifier chosen by sgsnemu itself (currently ::ff).<br><br>This change is also a step forward towards supporting IPv4v6 APNs in sgsnemu.<br><br>Related: OS#4434<br>Change-Id: I0d36145250185e4cce699fdaedfe96bd969f5fa1<br>---<br>M sgsnemu/sgsnemu.c<br>1 file changed, 53 insertions(+), 41 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-ggsn refs/changes/28/17828/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c</span><br><span>index 779c887..71af735 100644</span><br><span>--- a/sgsnemu/sgsnemu.c</span><br><span>+++ b/sgsnemu/sgsnemu.c</span><br><span>@@ -64,15 +64,23 @@</span><br><span> #define MAXCONTEXTS 1024 /* Max number of allowed contexts */</span><br><span> </span><br><span> /* HASH tables for IP address allocation */</span><br><span style="color: hsl(120, 100%, 40%);">+struct pdp_peer_sgsnemu_ctx;</span><br><span> struct iphash_t {</span><br><span> uint8_t inuse; /* 0=free. 1=used by somebody */</span><br><span> struct iphash_t *ipnext;</span><br><span style="color: hsl(0, 100%, 40%);">- struct pdp_t *pdp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pdp_peer_sgsnemu_ctx *ctx;</span><br><span> struct in46_addr addr;</span><br><span> };</span><br><span style="color: hsl(0, 100%, 40%);">-struct iphash_t iparr[MAXCONTEXTS];</span><br><span> struct iphash_t *iphash[MAXCONTEXTS];</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct pdp_peer_sgsnemu_ctx {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct iphash_t hash_v4;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct iphash_t hash_v6_ll;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct iphash_t hash_v6_global;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pdp_t *pdp;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+struct pdp_peer_sgsnemu_ctx ctx_arr[MAXCONTEXTS];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* State variable used for ping */</span><br><span> /* 0: Idle */</span><br><span> /* 1: Wait_connect */</span><br><span>@@ -189,6 +197,9 @@</span><br><span> int hash = ippool_hash(addr) % MAXCONTEXTS;</span><br><span> struct iphash_t *h;</span><br><span> struct iphash_t *prev = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("Adding IP to local pool: %s\n", in46a_ntoa(addr));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ipaddr->ipnext = NULL;</span><br><span> ipaddr->addr = *addr;</span><br><span> for (h = iphash[hash]; h; h = h->ipnext)</span><br><span>@@ -1552,19 +1563,8 @@</span><br><span> src.len = 4;</span><br><span> src.v4.s_addr = iph->saddr;</span><br><span> } else if (iph->version == 6) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* We only have a single entry in the hash table, and it consists of the link-local</span><br><span style="color: hsl(0, 100%, 40%);">- * address "fe80::prefix". So we need to make sure to convert non-link-local source</span><br><span style="color: hsl(0, 100%, 40%);">- * addresses to that format before looking up the hash table via ippool_getip() */</span><br><span> src.len = 16;</span><br><span style="color: hsl(0, 100%, 40%);">- if (!memcmp(ip6h->ip6_src.s6_addr, ll_prefix, sizeof(ll_prefix))) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* is a link-local address, we can do the hash lookup 1:1 */</span><br><span style="color: hsl(0, 100%, 40%);">- src.v6 = ip6h->ip6_src;</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- /* it is not a link-local address, so we must convert from the /64 prefix</span><br><span style="color: hsl(0, 100%, 40%);">- * to the link-local format that's used in the hash table */</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(&src.v6.s6_addr[0], ll_prefix, sizeof(ll_prefix));</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(&src.v6.s6_addr[sizeof(ll_prefix)], ip6h->ip6_src.s6_addr, 16-sizeof(ll_prefix));</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+ src.v6 = ip6h->ip6_src;</span><br><span> } else {</span><br><span> printf("Dropping packet with invalid IP version %u\n", iph->version);</span><br><span> return 0;</span><br><span>@@ -1576,8 +1576,8 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (ipm->pdp) /* Check if a peer protocol is defined */</span><br><span style="color: hsl(0, 100%, 40%);">- gtp_data_req(gsn, ipm->pdp, pack, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ipm->ctx->pdp) /* Check if a peer protocol is defined */</span><br><span style="color: hsl(120, 100%, 40%);">+ gtp_data_req(gsn, ipm->ctx->pdp, pack, len);</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -1589,19 +1589,19 @@</span><br><span> sigset_t oldmask;</span><br><span> #endif</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- struct iphash_t *iph = (struct iphash_t *)cbp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pdp_peer_sgsnemu_ctx *ctx = (struct pdp_peer_sgsnemu_ctx *) cbp;</span><br><span> </span><br><span> if (cause < 0) {</span><br><span> printf("Create PDP Context Request timed out\n");</span><br><span style="color: hsl(0, 100%, 40%);">- if (iph->pdp->version == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ctx->pdp->version == 1) {</span><br><span> printf("Retrying with version 0\n");</span><br><span style="color: hsl(0, 100%, 40%);">- iph->pdp->version = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- gtp_create_context_req(gsn, iph->pdp, iph);</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->pdp->version = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ gtp_create_context_req(gsn, ctx->pdp, ctx);</span><br><span> return 0;</span><br><span> } else {</span><br><span> state = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- pdp_freepdp(iph->pdp);</span><br><span style="color: hsl(0, 100%, 40%);">- iph->pdp = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ pdp_freepdp(ctx->pdp);</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->pdp = NULL;</span><br><span> return EOF;</span><br><span> }</span><br><span> }</span><br><span>@@ -1611,8 +1611,8 @@</span><br><span> ("Received create PDP context response. Cause value: %d\n",</span><br><span> cause);</span><br><span> state = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- pdp_freepdp(iph->pdp);</span><br><span style="color: hsl(0, 100%, 40%);">- iph->pdp = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ pdp_freepdp(ctx->pdp);</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->pdp = NULL;</span><br><span> return EOF; /* Not what we expected */</span><br><span> }</span><br><span> </span><br><span>@@ -1620,8 +1620,8 @@</span><br><span> printf</span><br><span> ("Received create PDP context response. Cause value: %d\n",</span><br><span> cause);</span><br><span style="color: hsl(0, 100%, 40%);">- pdp_freepdp(iph->pdp);</span><br><span style="color: hsl(0, 100%, 40%);">- iph->pdp = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ pdp_freepdp(ctx->pdp);</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->pdp = NULL;</span><br><span> state = 0;</span><br><span> return EOF; /* Not a valid IP address */</span><br><span> }</span><br><span>@@ -1643,13 +1643,18 @@</span><br><span> </span><br><span> switch (addr[i].len) {</span><br><span> case 16: /* IPv6 */</span><br><span style="color: hsl(0, 100%, 40%);">- /* we have to enable the kernel to perform stateless autoconfiguration,</span><br><span style="color: hsl(0, 100%, 40%);">- * i.e. send a router solicitation using the lover 64bits of the allocated</span><br><span style="color: hsl(0, 100%, 40%);">- * EUA as interface identifier, as per 3GPP TS 29.061 Section 11.2.1.3.2 */</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Convert address to link local using the lower 64bits</span><br><span style="color: hsl(120, 100%, 40%);">+ of the allocated EUA as Interface-Identifier to</span><br><span style="color: hsl(120, 100%, 40%);">+ send router solicitation, as per 3GPP TS 29.061</span><br><span style="color: hsl(120, 100%, 40%);">+ Section 11.2.1.3.2 */</span><br><span> memcpy(addr[i].v6.s6_addr, ll_prefix, sizeof(ll_prefix));</span><br><span> printf("Derived IPv6 link-local address: %s\n", in46a_ntoa(&addr[i]));</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->hash_v6_ll.inuse = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ipset(&ctx->hash_v6_ll, &addr[i]);</span><br><span> break;</span><br><span> case 4: /* IPv4 */</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->hash_v4.inuse = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ipset(&ctx->hash_v4, &addr[i]);</span><br><span> break;</span><br><span> }</span><br><span> </span><br><span>@@ -1671,8 +1676,6 @@</span><br><span> if (options.ipup)</span><br><span> tun_runscript(tun, options.ipup);</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ipset(iph, &addr[i]);</span><br><span> }</span><br><span> </span><br><span> if (options.createif && options.pdp_type == PDP_EUA_TYPE_v6) {</span><br><span>@@ -1688,7 +1691,7 @@</span><br><span> }</span><br><span> SYS_ERR(DSGSN, LOGL_INFO, 0, "Sending ICMPv6 Router Soliciation to GGSN...");</span><br><span> msg = icmpv6_construct_rs(saddr6);</span><br><span style="color: hsl(0, 100%, 40%);">- gtp_data_req(gsn, iph->pdp, msgb_data(msg), msgb_length(msg));</span><br><span style="color: hsl(120, 100%, 40%);">+ gtp_data_req(gsn, ctx->pdp, msgb_data(msg), msgb_length(msg));</span><br><span> msgb_free(msg);</span><br><span> }</span><br><span> </span><br><span>@@ -1753,8 +1756,9 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void handle_router_adv(struct ip6_hdr *ip6h, struct icmpv6_radv_hdr *ra, size_t ra_len)</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_router_adv(struct pdp_t *pdp, struct ip6_hdr *ip6h, struct icmpv6_radv_hdr *ra, size_t ra_len)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pdp_peer_sgsnemu_ctx* ctx = (struct pdp_peer_sgsnemu_ctx*)pdp->peer[0];</span><br><span> struct icmpv6_opt_hdr *opt_hdr;</span><br><span> struct icmpv6_opt_prefix *opt_prefix;</span><br><span> int rc;</span><br><span>@@ -1780,6 +1784,12 @@</span><br><span> addr.v6.s6_addr[15] = 0x02;</span><br><span> SYS_ERR(DSGSN, LOGL_INFO, 0, "Adding addr %s to tun %s",</span><br><span> in46a_ntoa(&addr), tun->devname);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ctx->hash_v6_global.inuse) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->hash_v6_global.inuse = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ipset(&ctx->hash_v6_global, &addr);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ SYS_ERR(DSGSN, LOGL_ERROR, 0, "First v6 global address in hash already in use!");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span> #if defined(__linux__)</span><br><span> if ((options.netns)) {</span><br><span>@@ -1824,7 +1834,7 @@</span><br><span> case 6:</span><br><span> if ((ra = icmpv6_validate_router_adv(pack, len))) {</span><br><span> size_t ra_len = (uint8_t*)ra - (uint8_t*)pack;</span><br><span style="color: hsl(0, 100%, 40%);">- handle_router_adv((struct ip6_hdr *)pack, ra, ra_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ handle_router_adv(pdp, (struct ip6_hdr *)pack, ra, ra_len);</span><br><span> return 0;</span><br><span> }</span><br><span> break;</span><br><span>@@ -1965,7 +1975,7 @@</span><br><span> </span><br><span> /* Initialise hash tables */</span><br><span> memset(&iphash, 0, sizeof(iphash));</span><br><span style="color: hsl(0, 100%, 40%);">- memset(&iparr, 0, sizeof(iparr));</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(&ctx_arr, 0, sizeof(ctx_arr));</span><br><span> </span><br><span> printf("Done initialising GTP library\n\n");</span><br><span> </span><br><span>@@ -1977,7 +1987,6 @@</span><br><span> for (n = 0; n < options.contexts; n++) {</span><br><span> uint64_t myimsi;</span><br><span> printf("Setting up PDP context #%d\n", n);</span><br><span style="color: hsl(0, 100%, 40%);">- iparr[n].inuse = 1; /* TODO */</span><br><span> </span><br><span> imsi_add(options.imsi, &myimsi, n);</span><br><span> </span><br><span>@@ -1986,9 +1995,12 @@</span><br><span> /* Otherwise it is deallocated by gtplib */</span><br><span> gtp_pdp_newpdp(gsn, &pdp, myimsi, options.nsapi, NULL);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- pdp->peer[0] = &iparr[n]; /* FIXME: support v4v6, have 2 peers */</span><br><span style="color: hsl(120, 100%, 40%);">+ pdp->peer[0] = &ctx_arr[n];</span><br><span> pdp->ipif = tun; /* TODO */</span><br><span style="color: hsl(0, 100%, 40%);">- iparr[n].pdp = pdp;</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx_arr[n].pdp = pdp;</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx_arr[n].hash_v4.ctx = &ctx_arr[n];</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx_arr[n].hash_v6_ll.ctx = &ctx_arr[n];</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx_arr[n].hash_v6_global.ctx = &ctx_arr[n];</span><br><span> </span><br><span> if (options.gtpversion == 0) {</span><br><span> if (options.qos.l - 1 > sizeof(pdp->qos_req0)) {</span><br><span>@@ -2076,7 +2088,7 @@</span><br><span> </span><br><span> /* Create context */</span><br><span> /* We send this of once. Retransmissions are handled by gtplib */</span><br><span style="color: hsl(0, 100%, 40%);">- gtp_create_context_req(gsn, pdp, &iparr[n]);</span><br><span style="color: hsl(120, 100%, 40%);">+ gtp_create_context_req(gsn, pdp, &ctx_arr[n]);</span><br><span> }</span><br><span> </span><br><span> state = 1; /* Enter wait_connection state */</span><br><span>@@ -2126,7 +2138,7 @@</span><br><span> for (n = 0; n < options.contexts; n++) {</span><br><span> /* Delete context */</span><br><span> printf("Disconnecting PDP context #%d\n", n);</span><br><span style="color: hsl(0, 100%, 40%);">- gtp_delete_context_req2(gsn, iparr[n].pdp, NULL, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ gtp_delete_context_req2(gsn, ctx_arr[n].pdp, NULL, 1);</span><br><span> if ((options.pinghost.len)</span><br><span> && ntransmitted)</span><br><span> ping_finish();</span><br><span>@@ -2148,7 +2160,7 @@</span><br><span> if (options.debug)</span><br><span> printf("Create_ping %d\n", diff);</span><br><span> create_ping(gsn,</span><br><span style="color: hsl(0, 100%, 40%);">- iparr[pingseq %</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx_arr[pingseq %</span><br><span> options.contexts].pdp,</span><br><span> &options.pinghost, pingseq,</span><br><span> options.pingsize);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-ggsn/+/17828">change 17828</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-ggsn/+/17828"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: osmo-ggsn </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I0d36145250185e4cce699fdaedfe96bd969f5fa1 </div>
<div style="display:none"> Gerrit-Change-Number: 17828 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>