aboutsummaryrefslogtreecommitdiffstats
path: root/ggsn/ggsn.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-08-08 23:27:22 +0200
committerHarald Welte <laforge@gnumonks.org>2017-08-11 10:45:56 +0200
commitd46bcd236e93432c894a939f4e5810dc5e9b4974 (patch)
treef25e4606b0e25db3230736984801ed8770c14e58 /ggsn/ggsn.c
parentd4d6e09fd29e23e28960959ca488e1481339571e (diff)
IPv6: Implement IPv6 prefix assignment via ICMPv6 router advertisement
The 3GPP specs are quite strange when it comes to how an IPv6 address or rather prefix is assigned to an IPv6 PDP context. The designated method for allocating the IPv6 address via the PDP EUA (End User Address) Information Element in the GTP signalling plane is *not* used to allocate the address/prefix. Instead, the EUA is used to allocate an "interface identifier" to the MS, which it the uses to derive its link-local source address to send a router solicitation. The GGSN subsequently answers witha router advertisement, advertising a single/64 prefix, whihcthe MS then uses to generate it's real IPv6 source address for subsequent communication. Change-Id: Icddf7d30e01d76a4784bcef5787b36f52f703a9f
Diffstat (limited to 'ggsn/ggsn.c')
-rw-r--r--ggsn/ggsn.c36
1 files changed, 35 insertions, 1 deletions
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 9b11884..c307177 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -64,6 +64,7 @@
#include "../gtp/gtp.h"
#include "cmdline.h"
#include "gtp-kernel.h"
+#include "icmpv6.h"
int end = 0;
int maxfd = 0; /* For select() */
@@ -206,11 +207,23 @@ int create_context_ind(struct pdp_t *pdp)
return 0; /* Allready in use, or no more available */
}
- in46a_to_eua(&member->addr, &pdp->eua);
+ if (addr.len == sizeof(struct in6_addr)) {
+ struct in46_addr tmp;
+ /* IPv6 doesn't really send the real/allocated address at this point, but just
+ * the link-identifier which the MS shall use for router solicitation */
+ tmp.len = addr.len;
+ /* initialize upper 64 bits to prefix, they are discarded by MS anyway */
+ memcpy(tmp.v6.s6_addr, &member->addr.v6, 8);
+ /* use allocated 64bit prefix as lower 64bit, used as link id by MS */
+ memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8);
+ in46a_to_eua(&tmp, &pdp->eua);
+ } else
+ in46a_to_eua(&member->addr, &pdp->eua);
pdp->peer = member;
pdp->ipif = tun; /* TODO */
member->peer = pdp;
+ /* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
if (gtp_kernel_tunnel_add(pdp) < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Cannot add tunnel to kernel: %s\n", strerror(errno));
@@ -264,9 +277,30 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
return 0;
}
+/* RFC3307 link-local scope multicast address */
+static const struct in6_addr all_router_mcast_addr = {
+ .s6_addr = { 0xff,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2 }
+};
+
int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
{
+ struct iphdr *iph = (struct iphdr *)pack;
+ struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
+
DEBUGP(DGGSN, "encaps_tun. Packet received: forwarding to tun\n");
+
+ switch (iph->version) {
+ case 6:
+ /* daddr: all-routers multicast addr */
+ if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
+ return handle_router_mcast(gsn, pdp, pack, len);
+ break;
+ case 4:
+ break;
+ default:
+ LOGP(DGGSN, LOGL_ERROR, "Packet from MS is neither IPv4 nor IPv6\n");
+ return -1;
+ }
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
}