aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-08-08 23:27:22 +0200
committerHarald Welte <laforge@gnumonks.org>2017-08-09 22:14:53 +0200
commit949e50e02bb1af911dca820f21a11232f615e106 (patch)
tree54df2271d331ba2b268d264fe690512e250ab319
parent714a41bf66932bb6b383cd4573d1eff51aaf50dd (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
-rw-r--r--ggsn/Makefile.am2
-rw-r--r--ggsn/checksum.c205
-rw-r--r--ggsn/checksum.h13
-rw-r--r--ggsn/ggsn.c36
-rw-r--r--ggsn/icmpv6.c234
-rw-r--r--ggsn/icmpv6.h6
-rw-r--r--lib/debug.c5
-rw-r--r--lib/in46_addr.c2
-rw-r--r--lib/ippool.c6
-rw-r--r--lib/syserr.h1
10 files changed, 505 insertions, 5 deletions
diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am
index 3ad3a6e..c945f0b 100644
--- a/ggsn/Makefile.am
+++ b/ggsn/Makefile.am
@@ -12,7 +12,7 @@ ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(
endif
ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
-ggsn_SOURCES = ggsn.c cmdline.c cmdline.h gtp-kernel.h
+ggsn_SOURCES = ggsn.c cmdline.c cmdline.h gtp-kernel.h icmpv6.c icmpv6.h checksum.c checksum.h
if ENABLE_GTP_KERNEL
ggsn_SOURCES += gtp-kernel.c
diff --git a/ggsn/checksum.c b/ggsn/checksum.c
new file mode 100644
index 0000000..1e9e58e
--- /dev/null
+++ b/ggsn/checksum.c
@@ -0,0 +1,205 @@
+/*
+ *
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IP/TCP/UDP checksumming routines
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Tom May, <ftom@netcom.com>
+ * Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
+ * Lots of code moved from tcp.c and ip.c; see those files
+ * for more names.
+ *
+ * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
+ * Fixed some nasty bugs, causing some horrible crashes.
+ * A: At some points, the sum (%0) was used as
+ * length-counter instead of the length counter
+ * (%1). Thanks to Roman Hodek for pointing this out.
+ * B: GCC seems to mess up if one uses too many
+ * data-registers to hold input values and one tries to
+ * specify d0 and d1 as scratch registers. Letting gcc
+ * choose these registers itself solves the problem.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
+ kills, so most of the assembly has to go. */
+
+#include "checksum.h"
+#include <arpa/inet.h>
+#include <asm/byteorder.h>
+
+static inline unsigned short from32to16(unsigned int x)
+{
+ /* add up 16-bit and 16-bit for 16+c bit */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up carry.. */
+ x = (x & 0xffff) + (x >> 16);
+ return x;
+}
+
+static unsigned int do_csum(const unsigned char *buff, int len)
+{
+ int odd;
+ unsigned int result = 0;
+
+ if (len <= 0)
+ goto out;
+ odd = 1 & (unsigned long) buff;
+ if (odd) {
+#ifdef __LITTLE_ENDIAN
+ result += (*buff << 8);
+#else
+ result = *buff;
+#endif
+ len--;
+ buff++;
+ }
+ if (len >= 2) {
+ if (2 & (unsigned long) buff) {
+ result += *(unsigned short *) buff;
+ len -= 2;
+ buff += 2;
+ }
+ if (len >= 4) {
+ const unsigned char *end = buff + ((unsigned)len & ~3);
+ unsigned int carry = 0;
+ do {
+ unsigned int w = *(unsigned int *) buff;
+ buff += 4;
+ result += carry;
+ result += w;
+ carry = (w > result);
+ } while (buff < end);
+ result += carry;
+ result = (result & 0xffff) + (result >> 16);
+ }
+ if (len & 2) {
+ result += *(unsigned short *) buff;
+ buff += 2;
+ }
+ }
+ if (len & 1)
+#ifdef __LITTLE_ENDIAN
+ result += *buff;
+#else
+ result += (*buff << 8);
+#endif
+ result = from32to16(result);
+ if (odd)
+ result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+ return result;
+}
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ */
+uint16_t ip_fast_csum(const void *iph, unsigned int ihl)
+{
+ return (uint16_t)~do_csum(iph, ihl*4);
+}
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+uint32_t csum_partial(const void *buff, int len, uint32_t wsum)
+{
+ unsigned int sum = (unsigned int)wsum;
+ unsigned int result = do_csum(buff, len);
+
+ /* add in old sum, and carry.. */
+ result += sum;
+ if (sum > result)
+ result += 1;
+ return (uint32_t)result;
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+uint16_t ip_compute_csum(const void *buff, int len)
+{
+ return (uint16_t)~do_csum(buff, len);
+}
+
+uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ uint32_t len, uint8_t proto, uint32_t csum)
+{
+ int carry;
+ uint32_t ulen;
+ uint32_t uproto;
+ uint32_t sum = (uint32_t)csum;
+
+ sum += (uint32_t)saddr->s6_addr32[0];
+ carry = (sum < (uint32_t)saddr->s6_addr32[0]);
+ sum += carry;
+
+ sum += (uint32_t)saddr->s6_addr32[1];
+ carry = (sum < (uint32_t)saddr->s6_addr32[1]);
+ sum += carry;
+
+ sum += (uint32_t)saddr->s6_addr32[2];
+ carry = (sum < (uint32_t)saddr->s6_addr32[2]);
+ sum += carry;
+
+ sum += (uint32_t)saddr->s6_addr32[3];
+ carry = (sum < (uint32_t)saddr->s6_addr32[3]);
+ sum += carry;
+
+ sum += (uint32_t)daddr->s6_addr32[0];
+ carry = (sum < (uint32_t)daddr->s6_addr32[0]);
+ sum += carry;
+
+ sum += (uint32_t)daddr->s6_addr32[1];
+ carry = (sum < (uint32_t)daddr->s6_addr32[1]);
+ sum += carry;
+
+ sum += (uint32_t)daddr->s6_addr32[2];
+ carry = (sum < (uint32_t)daddr->s6_addr32[2]);
+ sum += carry;
+
+ sum += (uint32_t)daddr->s6_addr32[3];
+ carry = (sum < (uint32_t)daddr->s6_addr32[3]);
+ sum += carry;
+
+ ulen = (uint32_t)htonl((uint32_t) len);
+ sum += ulen;
+ carry = (sum < ulen);
+ sum += carry;
+
+ uproto = (uint32_t)htonl(proto);
+ sum += uproto;
+ carry = (sum < uproto);
+ sum += carry;
+
+ return csum_fold((uint32_t)sum);
+}
+
+/* fold a partial checksum */
+uint16_t csum_fold(uint32_t csum)
+{
+ uint32_t sum = (uint32_t)csum;
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = (sum & 0xffff) + (sum >> 16);
+ return (uint16_t)~sum;
+}
diff --git a/ggsn/checksum.h b/ggsn/checksum.h
new file mode 100644
index 0000000..4b22431
--- /dev/null
+++ b/ggsn/checksum.h
@@ -0,0 +1,13 @@
+#pragma once
+#include <stdint.h>
+#include <netinet/in.h>
+
+uint16_t ip_fast_csum(const void *iph, unsigned int ihl);
+uint32_t csum_partial(const void *buff, int len, uint32_t wsum);
+uint16_t ip_compute_csum(const void *buff, int len);
+
+uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ uint32_t len, uint8_t proto, uint32_t csum);
+
+uint16_t csum_fold(uint32_t csum);
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);
}
diff --git a/ggsn/icmpv6.c b/ggsn/icmpv6.c
new file mode 100644
index 0000000..94c980b
--- /dev/null
+++ b/ggsn/icmpv6.c
@@ -0,0 +1,234 @@
+/* Minimal ICMPv6 code for generating router advertisements as required by
+ * relevant 3GPP specs for a GGSN with IPv6 PDP contexts */
+
+/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include "checksum.h"
+
+#include "../gtp/gtp.h"
+#include "../gtp/pdp.h"
+#include "../lib/ippool.h"
+#include "../lib/syserr.h"
+#include "config.h"
+
+/* 29.061 11.2.1.3.4 IPv6 Router Configuration Variables in GGSN */
+#define GGSN_MaxRtrAdvInterval 21600 /* 6 hours */
+#define GGSN_MinRtrAdvInterval 16200 /* 4.5 hours */
+#define GGSN_AdvValidLifetime 0xffffffff /* infinite */
+#define GGSN_AdvPreferredLifetime 0xffffffff /* infinite */
+
+struct icmpv6_hdr {
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+} __attribute__ ((packed));
+
+/* RFC4861 Section 4.2 */
+struct icmpv6_radv_hdr {
+ struct icmpv6_hdr hdr;
+ uint8_t cur_ho_limit;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t res:6,
+ m:1,
+ o:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t m:1,
+ o:1,
+ res:6;
+#else
+# error "Please fix <bits/endian.h>"
+#endif
+ uint16_t router_lifetime;
+ uint32_t reachable_time;
+ uint32_t retrans_timer;
+ uint8_t options[0];
+} __attribute__ ((packed));
+
+/* RFC4861 Section 4.6 */
+struct icmpv6_opt_hdr {
+ uint8_t type;
+ /* length in units of 8 octets, including type+len! */
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* RFC4861 Section 4.6.2 */
+struct icmpv6_opt_prefix {
+ struct icmpv6_opt_hdr hdr;
+ uint8_t prefix_len;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t res:6,
+ a:1,
+ l:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t l:1,
+ a:1,
+ res:6;
+#else
+# error "Please fix <bits/endian.h>"
+#endif
+ uint32_t valid_lifetime;
+ uint32_t preferred_lifetime;
+ uint32_t res2;
+ uint8_t prefix[16];
+} __attribute__ ((packed));
+
+
+/*! construct a 3GPP 29.061 compliant router advertisement for a given prefix
+ * \param[in] saddr Source IPv6 address for router advertisement
+ * \param[in] daddr Destination IPv6 address for router advertisement IPv6 header
+ * \param[in] prefix The single prefix to be advertised (/64 implied!)i
+ * \returns callee-allocated message buffer containing router advertisement */
+struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ const struct in6_addr *prefix)
+{
+ struct msgb *msg = msgb_alloc_headroom(512,128, "IPv6 RA");
+ struct icmpv6_radv_hdr *ra;
+ struct icmpv6_opt_prefix *ra_opt_pref;
+ struct ip6_hdr *i6h;
+ uint32_t len;
+ uint16_t skb_csum;
+
+ OSMO_ASSERT(msg);
+
+ ra = (struct icmpv6_radv_hdr *) msgb_put(msg, sizeof(*ra));
+ ra->hdr.type = 134; /* see RFC4861 4.2 */
+ ra->hdr.code = 0; /* see RFC4861 4.2 */
+ ra->hdr.csum = 0; /* updated below */
+ ra->cur_ho_limit = 64; /* seems reasonable? */
+ /* the GGSN shall leave the M-flag cleared in the Router
+ * Advertisement messages */
+ ra->m = 0;
+ /* The GGSN may set the O-flag if there are additional
+ * configuration parameters that need to be fetched by the MS */
+ ra->o = 0; /* no DHCPv6 */
+ ra->res = 0;
+ /* RFC4861 Default: 3 * MaxRtrAdvInterval */
+ ra->router_lifetime = htons(3*GGSN_MaxRtrAdvInterval);
+ ra->reachable_time = 0; /* Unspecified */
+
+ /* RFC4861 Section 4.6.2 */
+ ra_opt_pref = (struct icmpv6_opt_prefix *) msgb_put(msg, sizeof(*ra_opt_pref));
+ ra_opt_pref->hdr.type = 3; /* RFC4861 4.6.2 */
+ ra_opt_pref->hdr.len = 4; /* RFC4861 4.6.2 */
+ ra_opt_pref->prefix_len = 64; /* only prefix length as per 3GPP */
+ /* The Prefix is contained in the Prefix Information Option of
+ * the Router Advertisements and shall have the A-flag set
+ * and the L-flag cleared */
+ ra_opt_pref->a = 1;
+ ra_opt_pref->l = 0;
+ ra_opt_pref->res = 0;
+ /* The lifetime of the prefix shall be set to infinity */
+ ra_opt_pref->valid_lifetime = htonl(GGSN_AdvValidLifetime);
+ ra_opt_pref->preferred_lifetime = htonl(GGSN_AdvPreferredLifetime);
+ ra_opt_pref->res2 = 0;
+ memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix));
+
+ /* checksum */
+ skb_csum = csum_partial(msgb_data(msg), msgb_length(msg), 0);
+ len = msgb_length(msg);
+ ra->hdr.csum = csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, skb_csum);
+
+ /* Push IPv6 header in front of ICMPv6 packet */
+ i6h = (struct ip6_hdr *) msgb_push(msg, sizeof(*i6h));
+ /* 4 bits version, 8 bits TC, 20 bits flow-ID */
+ i6h->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000);
+ i6h->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len);
+ i6h->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6;
+ i6h->ip6_ctlun.ip6_un1.ip6_un1_hlim = 255;
+ i6h->ip6_src = *saddr;
+ i6h->ip6_dst = *daddr;
+
+ return msg;
+}
+
+/* Walidate an ICMPv6 router solicitation according to RFC4861 6.1.1 */
+static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
+{
+ const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
+ //const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
+
+ /* Hop limit field must have 255 */
+ if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_hlim != 255)
+ return false;
+ /* FIXME: ICMP checksum is valid */
+ /* ICMP length (derived from IP length) is 8 or more octets */
+ if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 8)
+ return false;
+ /* FIXME: All included options have a length > 0 */
+ /* FIXME: If IP source is unspecified, no source link-layer addr option */
+ return true;
+}
+
+/* RFC3307 link-local scope multicast address */
+static const struct in6_addr my_local_addr = {
+ .s6_addr = { 0x01,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0xff }
+};
+
+/* handle incoming packets to the all-routers multicast address */
+int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len)
+{
+ struct ippoolm_t *member = pdp->peer;
+ const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
+ const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
+ struct msgb *msg;
+
+ OSMO_ASSERT(pdp);
+ OSMO_ASSERT(member);
+
+ if (len < sizeof(*ip6h)) {
+ LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
+ return -1;
+ }
+
+ /* we only treat ICMPv6 here */
+ if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_ICMPV6) {
+ LOGP(DICMP6, LOGL_DEBUG, "Ignoring non-ICMP to all-routers mcast\n");
+ return 0;
+ }
+
+ if (len < sizeof(*ip6h) + sizeof(*ic6h)) {
+ LOGP(DICMP6, LOGL_NOTICE, "Short ICMPv6 packet: %s\n", osmo_hexdump(pack, len));
+ return -1;
+ }
+
+ switch (ic6h->type) {
+ case 133: /* router solicitation */
+ if (ic6h->code != 0) {
+ LOGP(DICMP6, LOGL_NOTICE, "ICMPv6 type 133 but code %d\n", ic6h->code);
+ return -1;
+ }
+ if (!icmpv6_validate_router_solicit(pack, len)) {
+ LOGP(DICMP6, LOGL_NOTICE, "Invalid Router Solicitation: %s\n",
+ osmo_hexdump(pack, len));
+ return -1;
+ }
+ /* FIXME: Send router advertisement from GGSN link-local
+ * address to MS link-local address, including prefix
+ * allocated to this PDP context */
+ msg = icmpv6_construct_ra(&my_local_addr, &ip6h->ip6_src, &member->addr.v6);
+ /* Send the constructed RA to the MS */
+ gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
+ msgb_free(msg);
+ break;
+ default:
+ LOGP(DICMP6, LOGL_DEBUG, "Unknown ICMPv6 type %u\n", ic6h->type);
+ break;
+ }
+ return 0;
+}
diff --git a/ggsn/icmpv6.h b/ggsn/icmpv6.h
new file mode 100644
index 0000000..ebff04e
--- /dev/null
+++ b/ggsn/icmpv6.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "../gtp/gtp.h"
+#include "../gtp/pdp.h"
+
+int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len);
diff --git a/lib/debug.c b/lib/debug.c
index b3850f9..83423dc 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -26,6 +26,11 @@ static const struct log_info_cat default_categories[] = {
.description = "SGSN Emulator",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DICMP6] = {
+ .name = "DICMP6",
+ .description = "ICMPv6",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
};
const struct log_info log_info = {
diff --git a/lib/in46_addr.c b/lib/in46_addr.c
index a220583..1785377 100644
--- a/lib/in46_addr.c
+++ b/lib/in46_addr.c
@@ -25,6 +25,7 @@ int in46a_to_af(const struct in46_addr *in)
switch (in->len) {
case 4:
return AF_INET;
+ case 8:
case 16:
return AF_INET6;
default:
@@ -175,6 +176,7 @@ int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
eua->v[1] = 0x21; /* IPv4 */
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
break;
+ case 8:
case 16:
eua->l = 18;
eua->v[0] = 0xf1; /* IETF */
diff --git a/lib/ippool.c b/lib/ippool.c
index 683d2d8..1729ec7 100644
--- a/lib/ippool.c
+++ b/lib/ippool.c
@@ -210,8 +210,8 @@ int ippool_new(struct ippool_t **this, const char *dyn, const char *stat,
}
/* we want to work with /64 prefixes, i.e. allocate /64 prefixes rather
* than /128 (single IPv6 addresses) */
- if (addr->len == sizeof(in6_addr))
- addr->len = 64/8;
+ if (addr.len == sizeof(struct in6_addr))
+ addr.len = 64/8;
/* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */
if (flags & IPPOOL_NOGATEWAY) {
@@ -453,7 +453,7 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
return -GTPCAUSE_SYS_FAIL; /* Allready in use / Should not happen */
}
- if (p2->addr.len != addr->len) {
+ if (p2->addr.len != addr->len && !(addr->len == 16 && p2->addr.len == 8)) {
SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
return -GTPCAUSE_UNKNOWN_PDP;
}
diff --git a/lib/syserr.h b/lib/syserr.h
index 0c50a5f..adc5840 100644
--- a/lib/syserr.h
+++ b/lib/syserr.h
@@ -19,6 +19,7 @@ enum {
DTUN,
DGGSN,
DSGSN,
+ DICMP6,
};
#define SYS_ERR(sub, pri, en, fmt, args...) \