aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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...) \