<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>