diff options
-rw-r--r-- | ggsn/ggsn.c | 37 | ||||
-rw-r--r-- | ggsn/gtp-kernel.c | 15 | ||||
-rw-r--r-- | ggsn/gtp-kernel.h | 4 | ||||
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/in46_addr.c | 148 | ||||
-rw-r--r-- | lib/in46_addr.h | 19 | ||||
-rw-r--r-- | lib/ippool.c | 238 | ||||
-rw-r--r-- | lib/ippool.h | 32 | ||||
-rw-r--r-- | sgsnemu/sgsnemu.c | 49 |
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; |