aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ggsn/ggsn.c37
-rw-r--r--ggsn/gtp-kernel.c15
-rw-r--r--ggsn/gtp-kernel.h4
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/in46_addr.c148
-rw-r--r--lib/in46_addr.h19
-rw-r--r--lib/ippool.c238
-rw-r--r--lib/ippool.h32
-rw-r--r--sgsnemu/sgsnemu.c49
9 files changed, 374 insertions, 172 deletions
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 168e907..0d0e569 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -56,6 +56,7 @@
#include "../lib/tun.h"
#include "../lib/ippool.h"
#include "../lib/syserr.h"
+#include "../lib/in46_addr.h"
#include "../gtp/pdp.h"
#include "../gtp/gtp.h"
#include "cmdline.h"
@@ -65,7 +66,8 @@ int end = 0;
int maxfd = 0; /* For select() */
struct in_addr listen_;
-struct in_addr netaddr, destaddr, net, mask; /* Network interface */
+struct in_addr netaddr, destaddr, net; /* Network interface */
+size_t prefixlen;
struct in_addr dns1, dns2; /* PCO DNS address */
char *ipup, *ipdown; /* Filename of scripts */
int debug; /* Print debug output */
@@ -135,9 +137,12 @@ int daemon(int nochdir, int noclose)
static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const struct ippoolm_t *member, const char *var)
{
+ char addrbuf[256];
char val[NAMESIZE];
- snprintf(val, sizeof(val), "%" PRIu64 ",%s", pdp->imsi, inet_ntoa(member->addr));
+ const char *addrstr = in46a_ntop(&member->addr, addrbuf, sizeof(addrbuf));
+
+ snprintf(val, sizeof(val), "%" PRIu64 ",%s", pdp->imsi, addrstr);
if (ctrl_cmd_send_trap(gsn->ctrl, var, val) < 0) {
LOGP(DGGSN, LOGL_ERROR, "Failed to create and send TRAP for IMSI %" PRIu64 " [%s].\n", pdp->imsi, var);
@@ -168,7 +173,7 @@ int delete_context(struct pdp_t *pdp)
int create_context_ind(struct pdp_t *pdp)
{
- struct in_addr addr;
+ struct in46_addr addr;
struct ippoolm_t *member;
DEBUGP(DGGSN, "Received create PDP context request\n");
@@ -183,8 +188,8 @@ int create_context_ind(struct pdp_t *pdp)
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
pdp->qos_neg.l = pdp->qos_req.l;
- if (pdp_euaton(&pdp->eua, &addr)) {
- addr.s_addr = 0; /* Request dynamic */
+ if (pdp_euaton(&pdp->eua, &addr.v4)) {
+ addr.v4.s_addr = 0; /* Request dynamic */
}
if (ippool_newip(ippool, &member, &addr, 0)) {
@@ -192,7 +197,7 @@ int create_context_ind(struct pdp_t *pdp)
return 0; /* Allready in use, or no more available */
}
- pdp_ntoeua(&member->addr, &pdp->eua);
+ pdp_ntoeua(&member->addr.v4, &pdp->eua);
pdp->peer = member;
pdp->ipif = tun; /* TODO */
member->peer = pdp;
@@ -215,10 +220,18 @@ int create_context_ind(struct pdp_t *pdp)
int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
{
struct ippoolm_t *ipm;
- struct in_addr dst;
+ struct in46_addr dst;
struct tun_packet_t *iph = (struct tun_packet_t *)pack;
- dst.s_addr = iph->dst;
+ if (iph->ver == 4) {
+ if (len < sizeof(*iph) || len < 4*iph->ihl)
+ return -1;
+ dst.len = 4;
+ dst.v4.s_addr = iph->dst;
+ } else {
+ LOGP(DGGSN, LOGL_NOTICE, "non-IPv4 packet received from tun\n");
+ return -1;
+ }
DEBUGP(DGGSN, "Received packet from tun!\n");
@@ -383,12 +396,14 @@ int main(int argc, char **argv)
/* net */
/* Store net as in_addr net and mask */
if (args_info.net_arg) {
- if (ippool_aton(&net, &mask, args_info.net_arg, 0)) {
+ struct in46_addr in46;
+ if (ippool_aton(&in46, &prefixlen, args_info.net_arg, 0)) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Invalid network address: %s!",
args_info.net_arg);
exit(1);
}
+ net.s_addr = in46.v4.s_addr;
netaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
destaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
} else {
@@ -547,7 +562,7 @@ int main(int argc, char **argv)
maxfd = gsn->fd1u;
/* use GTP kernel module for data packet encapsulation */
- if (gtp_kernel_init(gsn, &net, &mask, &args_info) < 0)
+ if (gtp_kernel_init(gsn, &net, prefixlen, &args_info) < 0)
goto err;
gtp_set_cb_data_ind(gsn, encaps_tun);
@@ -572,7 +587,7 @@ int main(int argc, char **argv)
}
DEBUGP(DGGSN, "Setting tun IP address\n");
- if (tun_setaddr(tun, &netaddr, &destaddr, &mask)) {
+ if (tun_setaddr(tun, &netaddr, &destaddr, &prefixlen)) {
SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to set tun IP address");
exit(1);
}
diff --git a/ggsn/gtp-kernel.c b/ggsn/gtp-kernel.c
index dbe5a9f..458ac27 100644
--- a/ggsn/gtp-kernel.c
+++ b/ggsn/gtp-kernel.c
@@ -70,17 +70,6 @@ static void pdp_debug(struct pdp_t *pdp)
printf("\n");
}
-static int mask2prefix(struct in_addr *mask)
-{
- uint32_t tmp = ntohl(mask->s_addr);
- int k;
-
- for (k=0; tmp > 0; k++)
- tmp = (tmp << 1);
-
- return k;
-}
-
static struct {
int genl_id;
struct mnl_socket *nl;
@@ -91,7 +80,7 @@ static struct {
#define GTP_DEVNAME "gtp0"
int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
- struct in_addr *mask,
+ size_t prefixlen,
struct gengetopt_args_info *args_info)
{
if (!args_info->gtp_linux_given)
@@ -126,7 +115,7 @@ int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
args_info->net_arg, GTP_DEVNAME);
- if (gtp_dev_config(GTP_DEVNAME, net, mask2prefix(mask)) < 0) {
+ if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Cannot add route to reach network %s\n",
args_info->net_arg);
diff --git a/ggsn/gtp-kernel.h b/ggsn/gtp-kernel.h
index 83280a0..b3b29e3 100644
--- a/ggsn/gtp-kernel.h
+++ b/ggsn/gtp-kernel.h
@@ -8,7 +8,7 @@ extern char *ipup;
#ifdef GTP_KERNEL
int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
- struct in_addr *mask,
+ size_t prefixlen,
struct gengetopt_args_info *args_info);
void gtp_kernel_stop(void);
@@ -19,7 +19,7 @@ int gtp_kernel_enabled(void);
#else
static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
- struct in_addr *mask,
+ size_t prefixlen,
struct gengetopt_args_info *args_info)
{
if (args_info->gtp_linux_given) {
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 756d566..632990c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,7 +1,7 @@
noinst_LIBRARIES = libmisc.a
-noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h
+noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
-libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c
+libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c
diff --git a/lib/in46_addr.c b/lib/in46_addr.c
new file mode 100644
index 0000000..903ceec
--- /dev/null
+++ b/lib/in46_addr.c
@@ -0,0 +1,148 @@
+/*
+ * IPv4/v6 address functions.
+ * Copyright (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 "../lib/in46_addr.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*! Return the address family of given \reff in46_addr argument */
+int in46a_to_af(const struct in46_addr *in)
+{
+ switch (in->len) {
+ case 4:
+ return AF_INET;
+ case 16:
+ return AF_INET6;
+ default:
+ return -1;
+ }
+}
+
+/*! Convert \ref in46_addr to sockaddr_storage */
+int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)out;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)out;
+
+ switch (in->len) {
+ case 4:
+ sin->sin_family = AF_INET;
+ sin->sin_addr = in->v4;
+ break;
+ case 16:
+ sin6->sin6_family = AF_INET;
+ sin6->sin6_addr = in->v6;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! Convenience wrapper around inet_ntop() for \ref in46_addr */
+const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size)
+{
+ int af = in46a_to_af(in);
+ if (af < 0)
+ return NULL;
+
+ return inet_ntop(af, (const void *) &in->v4, dst, dst_size);
+}
+
+/*! Determine if two in46_addr are equal or not
+ * \returns 1 in case they are equal; 0 otherwise */
+int in46a_equal(const struct in46_addr *a, const struct in46_addr *b)
+{
+ if (a->len == b->len && !memcmp(&a->v6, &b->v6, a->len))
+ return 1;
+ else
+ return 0;
+}
+
+/*! Match if IPv6 addr1 + addr2 are within same \a mask */
+static int ipv6_within_mask(const struct in6_addr *addr1, const struct in6_addr *addr2,
+ const struct in6_addr *mask)
+{
+ struct in6_addr masked = *addr2;
+#if defined(__linux__)
+ masked.s6_addr32[0] &= mask->s6_addr32[0];
+ masked.s6_addr32[1] &= mask->s6_addr32[1];
+ masked.s6_addr32[2] &= mask->s6_addr32[2];
+ masked.s6_addr32[3] &= mask->s6_addr32[3];
+#else
+ masked.__u6_addr.__u6_addr32[0] &= mask->__u6_addr.__u6_addr32[0];
+ masked.__u6_addr.__u6_addr32[1] &= mask->__u6_addr.__u6_addr32[1];
+ masked.__u6_addr.__u6_addr32[2] &= mask->__u6_addr.__u6_addr32[2];
+ masked.__u6_addr.__u6_addr32[3] &= mask->__u6_addr.__u6_addr32[3];
+#endif
+ if (!memcmp(addr1, &masked, sizeof(struct in6_addr)))
+ return 1;
+ else
+ return 0;
+}
+
+/*! Create an IPv6 netmask from the given prefix length */
+static void create_ipv6_netmask(struct in6_addr *netmask, int prefixlen)
+{
+ uint32_t *p_netmask;
+ memset(netmask, 0, sizeof(struct in6_addr));
+ if (prefixlen < 0)
+ prefixlen = 0;
+ else if (128 < prefixlen)
+ prefixlen = 128;
+
+#if defined(__linux__)
+ p_netmask = &netmask->s6_addr32[0];
+#else
+ p_netmask = &netmask->__u6_addr.__u6_addr32[0];
+#endif
+ while (32 < prefixlen) {
+ *p_netmask = 0xffffffff;
+ p_netmask++;
+ prefixlen -= 32;
+ }
+ if (prefixlen != 0) {
+ *p_netmask = htonl(0xFFFFFFFF << (32 - prefixlen));
+ }
+}
+
+/*! Determine if given \a addr is within given \a net + \a prefixlen
+ * Builds the netmask from \a net + \a prefixlen and matches it to \a addr
+ * \returns 1 in case of a match, 0 otherwise */
+int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen)
+{
+ struct in_addr netmask;
+ struct in6_addr netmask6;
+
+ if (addr->len != net->len)
+ return 0;
+
+ switch (addr->len) {
+ case 4:
+ netmask.s_addr = htonl(0xFFFFFFFF << (32 - prefixlen));
+ if ((addr->v4.s_addr & netmask.s_addr) == net->v4.s_addr)
+ return 1;
+ else
+ return 0;
+ case 16:
+ create_ipv6_netmask(&netmask6, prefixlen);
+ return ipv6_within_mask(&addr->v6, &net->v6, &netmask6);
+ default:
+ return 0;
+ }
+}
diff --git a/lib/in46_addr.h b/lib/in46_addr.h
new file mode 100644
index 0000000..f28fd8e
--- /dev/null
+++ b/lib/in46_addr.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <stdint.h>
+#include <netinet/in.h>
+
+/* a simple wrapper around an in6_addr to also contain the length of the address,
+ * thereby implicitly indicating the address family of the address */
+struct in46_addr {
+ uint8_t len;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ };
+};
+
+extern int in46a_to_af(const struct in46_addr *in);
+extern int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in);
+extern const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size);
+extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b);
+extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
diff --git a/lib/ippool.c b/lib/ippool.c
index 1f79a77..c3eb267 100644
--- a/lib/ippool.c
+++ b/lib/ippool.c
@@ -1,6 +1,7 @@
/*
* IP address pool functions.
* Copyright (C) 2003, 2004 Mondru AB.
+ * Copyright (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
@@ -16,6 +17,7 @@
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include "syserr.h"
#include "ippool.h"
#include "lookup.h"
@@ -31,13 +33,14 @@ int ippool_printaddr(struct ippool_t *this)
printf("Listsize %d\n", this->listsize);
for (n = 0; n < this->listsize; n++) {
- printf("Unit %d inuse %d prev %d next %d addr %s %x\n",
+ char s[256];
+ in46a_ntop(&this->member[n].addr, s, sizeof(s));
+ printf("Unit %d inuse %d prev %d next %d addr %s\n",
n,
this->member[n].inuse,
this->member[n].prev - this->member,
this->member[n].next - this->member,
- inet_ntoa(this->member[n].addr),
- this->member[n].addr.s_addr);
+ s);
}
return 0;
}
@@ -49,7 +52,7 @@ int ippool_hashadd(struct ippool_t *this, struct ippoolm_t *member)
struct ippoolm_t *p_prev = NULL;
/* Insert into hash table */
- hash = ippool_hash4(&member->addr) & this->hashmask;
+ hash = ippool_hash(&member->addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash)
p_prev = p;
if (!p_prev)
@@ -66,7 +69,7 @@ int ippool_hashdel(struct ippool_t *this, struct ippoolm_t *member)
struct ippoolm_t *p_prev = NULL;
/* Find in hash table */
- hash = ippool_hash4(&member->addr) & this->hashmask;
+ hash = ippool_hash(&member->addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
if (p == member) {
break;
@@ -88,75 +91,100 @@ int ippool_hashdel(struct ippool_t *this, struct ippoolm_t *member)
return 0;
}
-unsigned long int ippool_hash4(struct in_addr *addr)
+static unsigned long int ippool_hash4(struct in_addr *addr)
{
return lookup((unsigned char *)&addr->s_addr, sizeof(addr->s_addr), 0);
}
-#ifndef IPPOOL_NOIP6
-unsigned long int ippool_hash6(struct in6_addr *addr)
+static unsigned long int ippool_hash6(struct in6_addr *addr)
{
- return lookup((unsigned char *)addr->u6_addr8, sizeof(addr->u6_addr8),
+ /* TODO: Review hash spread for IPv6 */
+ return lookup((unsigned char *)addr->s6_addr, sizeof(addr->s6_addr),
0);
}
-#endif
-/* Get IP address and mask */
-int ippool_aton(struct in_addr *addr, struct in_addr *mask,
- char *pool, int number)
+unsigned long int ippool_hash(struct in46_addr *addr)
{
+ if (addr->len == 4)
+ return ippool_hash4(&addr->v4);
+ else
+ return ippool_hash6(&addr->v6);
+}
- /* Parse only first instance of network for now */
- /* Eventually "number" will indicate the token which we want to parse */
-
- unsigned int a1, a2, a3, a4;
- unsigned int m1, m2, m3, m4;
- int c;
- int m;
- int masklog;
-
- c = sscanf(pool, "%u.%u.%u.%u/%u.%u.%u.%u",
- &a1, &a2, &a3, &a4, &m1, &m2, &m3, &m4);
- switch (c) {
- case 4:
- mask->s_addr = 0xffffffff;
- break;
- case 5:
- if (m1 > 32) {
- SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
- return -1; /* Invalid mask */
- }
- mask->s_addr = htonl(0xffffffff << (32 - m1));
- break;
- case 8:
- if (m1 >= 256 || m2 >= 256 || m3 >= 256 || m4 >= 256) {
- SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
- return -1; /* Wrong mask format */
+/* Get IP address and mask */
+int ippool_aton(struct in46_addr *addr, size_t *prefixlen, const char *pool_in, int number)
+{
+ struct addrinfo *ai;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_flags = 0,
+ .ai_protocol = 0
+ };
+ char pool[strlen(pool_in)+1];
+
+ strcpy(pool, pool_in);
+
+ int err;
+
+ /* Find '/' and point to first char after it */
+ char *prefixlen_str = strchr(pool, '/');
+ if (prefixlen_str) {
+ *prefixlen_str = '\0';
+ prefixlen_str++;
+ if (*prefixlen_str == '\0') {
+ SYS_ERR(DIP, LOGL_ERROR, 0, "Empty prefix length specified");
+ return -1;
}
- m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4;
- for (masklog = 0; ((1 << masklog) < ((~m) + 1)); masklog++) ;
- if (((~m) + 1) != (1 << masklog)) {
- SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
- return -1; /* Wrong mask format (not all ones followed by all zeros) */
+ }
+
+ /* convert address */
+ if ((err = getaddrinfo(pool, NULL, &hints, &ai))) {
+ SYS_ERR(DIP, LOGL_ERROR, 0, "Bad address");
+ return -1;
+ }
+
+ /* Copy address, set lengths */
+ if (ai->ai_family == AF_INET) {
+ *prefixlen = 32;
+ addr->len = sizeof(struct in_addr);
+ addr->v4 = ((struct sockaddr_in*)ai->ai_addr)->sin_addr;
+ } else {
+ *prefixlen = 128;
+ addr->len = sizeof(struct in6_addr);
+ addr->v6 = ((struct sockaddr_in6*)ai->ai_addr)->sin6_addr;
+ }
+ freeaddrinfo(ai);
+
+ /* parse prefixlen */
+ if (prefixlen_str) {
+ char *e;
+ *prefixlen = strtol(prefixlen_str, &e, 10);
+ if (*e != '\0') {
+ SYS_ERR(DIP, LOGL_ERROR, 0, "Prefixlen is not an int");
+ return -1;
}
- mask->s_addr = htonl(m);
- break;
- default:
- SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
- return -1; /* Invalid mask */
}
- if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) {
- SYS_ERR(DIP, LOGL_ERROR, 0,
- "Wrong IP address format");
+ if (*prefixlen > (addr->len * 8)) {
+ SYS_ERR(DIP, LOGL_ERROR, 0, "Perfixlen too big");
return -1;
- } else
- addr->s_addr =
- htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
+ }
return 0;
}
+/* Increase IPv4/IPv6 address by 1 */
+void in46a_inc(struct in46_addr *addr)
+{
+ size_t addrlen;
+ uint8_t *a = (uint8_t *)&addr->v6;
+ for (addrlen = addr->len; addrlen > 0; addrlen--) {
+ if (++a[addrlen-1])
+ break;
+ }
+}
+
/* Create new address pool */
int ippool_new(struct ippool_t **this, char *dyn, char *stat,
int allowdyn, int allowstat, int flags)
@@ -165,11 +193,10 @@ int ippool_new(struct ippool_t **this, char *dyn, char *stat,
/* Parse only first instance of pool for now */
int i;
- struct in_addr addr;
- struct in_addr mask;
- struct in_addr stataddr;
- struct in_addr statmask;
- unsigned int m;
+ struct in46_addr addr;
+ size_t addrprefixlen;
+ struct in46_addr stataddr;
+ size_t stataddrprefixlen;
int listsize;
int dynsize;
unsigned int statsize;
@@ -177,7 +204,7 @@ int ippool_new(struct ippool_t **this, char *dyn, char *stat,
if (!allowdyn) {
dynsize = 0;
} else {
- if (ippool_aton(&addr, &mask, dyn, 0)) {
+ if (ippool_aton(&addr, &addrprefixlen, dyn, 0)) {
SYS_ERR(DIP, LOGL_ERROR, 0,
"Failed to parse dynamic pool");
return -1;
@@ -188,8 +215,7 @@ int ippool_new(struct ippool_t **this, char *dyn, char *stat,
flags |= IPPOOL_NONETWORK;
}
- m = ntohl(mask.s_addr);
- dynsize = ((~m) + 1);
+ dynsize = (1 << (addr.len*8 - addrprefixlen)) -1;
if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
dynsize--;
if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */
@@ -200,17 +226,16 @@ int ippool_new(struct ippool_t **this, char *dyn, char *stat,
if (!allowstat) {
statsize = 0;
- stataddr.s_addr = 0;
- statmask.s_addr = 0;
+ stataddr.len = 0;
+ stataddrprefixlen = 0;
} else {
- if (ippool_aton(&stataddr, &statmask, stat, 0)) {
+ if (ippool_aton(&stataddr, &stataddrprefixlen, stat, 0)) {
SYS_ERR(DIP, LOGL_ERROR, 0,
"Failed to parse static range");
return -1;
}
- m = ntohl(statmask.s_addr);
- statsize = ((~m) + 1);
+ statsize = (1 << (addr.len - addrprefixlen + 1)) -1;
if (statsize > IPPOOL_STATSIZE)
statsize = IPPOOL_STATSIZE;
}
@@ -225,8 +250,9 @@ int ippool_new(struct ippool_t **this, char *dyn, char *stat,
(*this)->allowdyn = allowdyn;
(*this)->allowstat = allowstat;
- (*this)->stataddr = stataddr;
- (*this)->statmask = statmask;
+ if (stataddr.len > 0)
+ (*this)->stataddr = stataddr;
+ (*this)->stataddrprefixlen = stataddrprefixlen;
(*this)->listsize += listsize;
if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))) {
@@ -255,17 +281,15 @@ int ippool_new(struct ippool_t **this, char *dyn, char *stat,
(*this)->firstdyn = NULL;
(*this)->lastdyn = NULL;
+ if (flags & IPPOOL_NOGATEWAY) {
+ in46a_inc(&addr);
+ in46a_inc(&addr);
+ } else if (flags & IPPOOL_NONETWORK) {
+ in46a_inc(&addr);
+ }
for (i = 0; i < dynsize; i++) {
-
- if (flags & IPPOOL_NOGATEWAY)
- (*this)->member[i].addr.s_addr =
- htonl(ntohl(addr.s_addr) + i + 2);
- else if (flags & IPPOOL_NONETWORK)
- (*this)->member[i].addr.s_addr =
- htonl(ntohl(addr.s_addr) + i + 1);
- else
- (*this)->member[i].addr.s_addr =
- htonl(ntohl(addr.s_addr) + i);
+ (*this)->member[i].addr = addr;
+ in46a_inc(&addr);
(*this)->member[i].inuse = 0;
@@ -285,8 +309,8 @@ int ippool_new(struct ippool_t **this, char *dyn, char *stat,
(*this)->firststat = NULL;
(*this)->laststat = NULL;
for (i = dynsize; i < listsize; i++) {
-
- (*this)->member[i].addr.s_addr = 0;
+ struct in46_addr *i6al = &(*this)->member[i].addr;
+ memset(i6al, 0, sizeof(*i6al));
(*this)->member[i].inuse = 0;
/* Insert into list of unused */
@@ -316,15 +340,15 @@ int ippool_free(struct ippool_t *this)
/* Find an IP address in the pool */
int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr)
+ struct in46_addr *addr)
{
struct ippoolm_t *p;
uint32_t hash;
/* Find in hash table */
- hash = ippool_hash4(addr) & this->hashmask;
+ hash = ippool_hash(addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
- if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
+ if (in46a_equal(&p->addr, addr)) {
if (member)
*member = p;
return 0;
@@ -344,7 +368,7 @@ int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
* address space.
**/
int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr, int statip)
+ struct in46_addr *addr, int statip)
{
struct ippoolm_t *p;
struct ippoolm_t *p2 = NULL;
@@ -365,17 +389,23 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
if (0)
(void)ippool_printaddr(this);
+ int specified = 0;
+ if (addr) {
+ if (addr->len == 4 && addr->v4.s_addr)
+ specified = 1;
+ if (addr->len == 16 && !IN6_IS_ADDR_UNSPECIFIED(&addr->v6))
+ specified = 1;
+ }
+
/* First check to see if this type of address is allowed */
- if ((addr) && (addr->s_addr) && statip) { /* IP address given */
+ if (specified && statip) { /* IP address given */
if (!this->allowstat) {
SYS_ERR(DIP, LOGL_ERROR, 0,
"Static IP address not allowed");
return -1;
}
- if ((addr->s_addr & this->statmask.s_addr) !=
- this->stataddr.s_addr) {
- SYS_ERR(DIP, LOGL_ERROR, 0,
- "Static out of range");
+ if (!in46a_within_mask(addr, &this->stataddr, this->stataddrprefixlen)) {
+ SYS_ERR(DIP, LOGL_ERROR, 0, "Static out of range");
return -1;
}
} else {
@@ -387,11 +417,11 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
}
/* If IP address given try to find it in dynamic address pool */
- if ((addr) && (addr->s_addr)) { /* IP address given */
+ if (specified) { /* IP address given */
/* Find in hash table */
- hash = ippool_hash4(addr) & this->hashmask;
+ hash = ippool_hash(addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
- if ((p->addr.s_addr == addr->s_addr)) {
+ if (in46a_equal(&p->addr, addr)) {
p2 = p;
break;
}
@@ -420,6 +450,11 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
return -1; /* Allready in use / Should not happen */
}
+ if (p2->addr.len != addr->len) {
+ SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
+ return -1;
+ }
+
/* Remove from linked list of free dynamic addresses */
if (p2->prev)
p2->prev->next = p2->next;
@@ -442,7 +477,7 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
/* It was not possible to allocate from dynamic address pool */
/* Try to allocate from static address space */
- if ((addr) && (addr->s_addr) && (statip)) { /* IP address given */
+ if (specified && (statip)) { /* IP address given */
if (!this->firststat) {
SYS_ERR(DIP, LOGL_ERROR, 0,
"No more IP addresses available");
@@ -450,6 +485,11 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
} else
p2 = this->firststat;
+ if (p2->addr.len != addr->len) {
+ SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
+ return -1;
+ }
+
/* Remove from linked list of free static addresses */
if (p2->prev)
p2->prev->next = p2->next;
@@ -518,7 +558,7 @@ int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member)
this->laststat = member;
member->inuse = 0;
- member->addr.s_addr = 0;
+ memset(&member->addr, 0, sizeof(member->addr));
member->peer = NULL;
member->nexthash = NULL;
if (0)
@@ -530,9 +570,3 @@ int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member)
return -1;
}
}
-
-#ifndef IPPOOL_NOIP6
-extern unsigned long int ippool_hash6(struct in6_addr *addr);
-extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr);
-extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr);
-#endif
diff --git a/lib/ippool.h b/lib/ippool.h
index 534140d..53154f2 100644
--- a/lib/ippool.h
+++ b/lib/ippool.h
@@ -12,6 +12,8 @@
#ifndef _IPPOOL_H
#define _IPPOOL_H
+#include "../lib/in46_addr.h"
+
/* Assuming that the address space is fragmented we need a hash table
in order to return the addresses.
@@ -26,8 +28,6 @@
in RFC2373.
*/
-#define IPPOOL_NOIP6
-
#define IPPOOL_NONETWORK 0x01
#define IPPOOL_NOBROADCAST 0x02
#define IPPOOL_NOGATEWAY 0x04
@@ -40,8 +40,8 @@ struct ippool_t {
unsigned int listsize; /* Total number of addresses */
int allowdyn; /* Allow dynamic IP address allocation */
int allowstat; /* Allow static IP address allocation */
- struct in_addr stataddr; /* Static address range network address */
- struct in_addr statmask; /* Static address range network mask */
+ struct in46_addr stataddr; /* Static address range network address */
+ size_t stataddrprefixlen; /* IPv6 prefix length of stataddr */
struct ippoolm_t *member; /* Listsize array of members */
unsigned int hashsize; /* Size of hash table */
int hashlog; /* Log2 size of hash table */
@@ -54,11 +54,7 @@ struct ippool_t {
};
struct ippoolm_t {
-#ifndef IPPOOL_NOIP6
- struct in6_addr addr; /* IP address of this member */
-#else
- struct in_addr addr; /* IP address of this member */
-#endif
+ struct in46_addr addr; /* IP address of this member */
int inuse; /* 0=available; 1= dynamic; 2 = static */
struct ippoolm_t *nexthash; /* Linked list part of hash table */
struct ippoolm_t *prev, *next; /* Linked list of free dynamic or static */
@@ -70,7 +66,7 @@ struct ippoolm_t {
bytes for each address. */
/* Hash an IP address using code based on Bob Jenkins lookupa */
-extern unsigned long int ippool_hash4(struct in_addr *addr);
+extern unsigned long int ippool_hash(struct in46_addr *addr);
/* Create new address pool */
extern int ippool_new(struct ippool_t **this, char *dyn, char *stat,
@@ -81,24 +77,20 @@ extern int ippool_free(struct ippool_t *this);
/* Find an IP address in the pool */
extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr);
+ struct in46_addr *addr);
/* Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
check to see if the given address is available */
extern int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr, int statip);
+ struct in46_addr *addr, int statip);
/* Return a previously allocated IP address */
extern int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member);
/* Get net and mask based on ascii string */
-extern int ippool_aton(struct in_addr *addr, struct in_addr *mask,
- char *pool, int number);
-
-#ifndef IPPOOL_NOIP6
-extern unsigned long int ippool_hash6(struct in6_addr *addr);
-extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr);
-extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr);
-#endif
+int ippool_aton(struct in46_addr *addr, size_t *prefixlen, const char *pool, int number);
+
+/* Increase IPv4/IPv6 address by 1 */
+extern void in46a_inc(struct in46_addr *addr);
#endif /* !_IPPOOL_H */
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 1567e7e..90a6200 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -58,7 +58,7 @@ struct iphash_t {
uint8_t inuse; /* 0=free. 1=used by somebody */
struct iphash_t *ipnext;
struct pdp_t *pdp;
- struct in_addr addr;
+ struct in46_addr addr;
};
struct iphash_t iparr[MAXCONTEXTS];
struct iphash_t *iphash[MAXCONTEXTS];
@@ -81,7 +81,8 @@ int echoversion = 1; /* First try this version */
struct {
int debug; /* Print debug messages */
int createif; /* Create local network interface */
- struct in_addr netaddr, destaddr, net, mask; /* Network interface */
+ struct in_addr netaddr, destaddr, net; /* Network interface */
+ size_t prefixlen;
char *ipup, *ipdown; /* Filename of scripts */
int defaultroute; /* Set up default route */
struct in_addr pinghost; /* Remote ping host */
@@ -160,13 +161,13 @@ void signal_handler(int signo)
state = 3; /* Tell main loop to finish. */
}
-int ipset(struct iphash_t *ipaddr, struct in_addr *addr)
+int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
{
- int hash = ippool_hash4(addr) % MAXCONTEXTS;
+ int hash = ippool_hash(addr) % MAXCONTEXTS;
struct iphash_t *h;
struct iphash_t *prev = NULL;
ipaddr->ipnext = NULL;
- ipaddr->addr.s_addr = addr->s_addr;
+ ipaddr->addr = *addr;
for (h = iphash[hash]; h; h = h->ipnext)
prev = h;
if (!prev)
@@ -178,7 +179,7 @@ int ipset(struct iphash_t *ipaddr, struct in_addr *addr)
int ipdel(struct iphash_t *ipaddr)
{
- int hash = ippool_hash4(&ipaddr->addr) % MAXCONTEXTS;
+ int hash = ippool_hash(&ipaddr->addr) % MAXCONTEXTS;
struct iphash_t *h;
struct iphash_t *prev = NULL;
for (h = iphash[hash]; h; h = h->ipnext) {
@@ -194,12 +195,12 @@ int ipdel(struct iphash_t *ipaddr)
return EOF; /* End of linked list and not found */
}
-int ipget(struct iphash_t **ipaddr, struct in_addr *addr)
+int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
{
- int hash = ippool_hash4(addr) % MAXCONTEXTS;
+ int hash = ippool_hash(addr) % MAXCONTEXTS;
struct iphash_t *h;
for (h = iphash[hash]; h; h = h->ipnext) {
- if ((h->addr.s_addr == addr->s_addr)) {
+ if (in46a_equal(&h->addr, addr)) {
*ipaddr = h;
return 0;
}
@@ -859,15 +860,17 @@ int process_options(int argc, char **argv)
/* net */
/* Store net as in_addr net and mask */
if (args_info.net_arg) {
+ struct in46_addr in46;
if (ippool_aton
- (&options.net, &options.mask, args_info.net_arg, 0)) {
+ (&in46, &options.prefixlen, args_info.net_arg, 0)) {
SYS_ERR(DSGSN, LOGL_ERROR, 0,
"Invalid network address: %s!",
args_info.net_arg);
exit(1);
}
+ options.net.s_addr = in46.v4.s_addr;
#if defined (__sun__)
- options.netaddr.s_addr = htonl(ntohl(options.net.s_addr) + 1);
+ options.netaddrs_addr = htonl(ntohl(options.net.s_addr) + 1);
options.destaddr.s_addr = htonl(ntohl(options.net.s_addr) + 1);
#else
options.netaddr.s_addr = options.net.s_addr;
@@ -876,7 +879,7 @@ int process_options(int argc, char **argv)
} else {
options.net.s_addr = 0;
- options.mask.s_addr = 0;
+ options.prefixlen = 0;
options.netaddr.s_addr = 0;
options.destaddr.s_addr = 0;
}
@@ -1277,14 +1280,15 @@ int delete_context(struct pdp_t *pdp)
int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
{
struct iphash_t *ipm;
- struct in_addr src;
+ struct in46_addr src;
struct tun_packet_t *iph = (struct tun_packet_t *)pack;
- src.s_addr = iph->src;
+ src.len = 4;
+ src.v4.s_addr = iph->src;
if (ipget(&ipm, &src)) {
printf("Dropping packet from invalid source address: %s\n",
- inet_ntoa(src));
+ inet_ntoa(src.v4));
return 0;
}
@@ -1295,7 +1299,7 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
{
- struct in_addr addr;
+ struct in46_addr addr;
struct iphash_t *iph = (struct iphash_t *)cbp;
@@ -1324,7 +1328,7 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
return EOF; /* Not what we expected */
}
- if (pdp_euaton(&pdp->eua, &addr)) {
+ if (pdp_euaton(&pdp->eua, &addr.v4)) {
printf
("Received create PDP context response. Cause value: %d\n",
cause);
@@ -1335,7 +1339,7 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
}
printf("Received create PDP context response. IP address: %s\n",
- inet_ntoa(addr));
+ inet_ntoa(addr.v4));
if ((options.createif) && (!options.net.s_addr)) {
struct in_addr m;
@@ -1345,11 +1349,11 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
m.s_addr = -1;
#endif
/* printf("Setting up interface and routing\n"); */
- tun_addaddr(tun, &addr, &addr, &m);
+ tun_addaddr(tun, &addr.v4, &addr.v4, &m);
if (options.defaultroute) {
struct in_addr rm;
rm.s_addr = 0;
- tun_addroute(tun, &rm, &addr, &rm);
+ tun_addroute(tun, &rm, &addr.v4, &rm);
}
if (options.ipup)
tun_runscript(tun, options.ipup);
@@ -1472,9 +1476,10 @@ int main(int argc, char **argv)
}
if ((options.createif) && (options.net.s_addr)) {
+ struct in_addr mask;
+ mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
/* printf("Setting up interface and routing\n"); */
- tun_addaddr(tun, &options.netaddr, &options.destaddr,
- &options.mask);
+ tun_addaddr(tun, &options.netaddr, &options.destaddr, &mask);
if (options.defaultroute) {
struct in_addr rm;
rm.s_addr = 0;