This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
groos@xiplink.com gerrit-no-reply at lists.osmocom.org
Review at https://gerrit.osmocom.org/2870
IPv6 support
Change-Id: If8ce8b4b8cd2ba97f7ba122de4703983111046e4
---
M ggsn/ggsn.c
M ggsn/ippool.c
M ggsn/ippool.h
M ggsn/tun.c
M ggsn/tun.h
M gtp/gtp.c
M gtp/gtp.h
M gtp/pdp.c
M gtp/pdp.h
M gtp/queue.c
M gtp/queue.h
M sgsnemu/ippool.c
M sgsnemu/ippool.h
M sgsnemu/sgsnemu.c
M sgsnemu/tun.c
M sgsnemu/tun.h
16 files changed, 1,215 insertions(+), 480 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/openggsn refs/changes/70/2870/1
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 52a2022..3efba0b 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -57,9 +57,11 @@
int end = 0;
int maxfd = 0; /* For select() */
-struct in_addr listen_;
-struct in_addr netaddr, destaddr, net, mask; /* Network interface */
-struct in_addr dns1, dns2; /* PCO DNS address */
+struct sockaddr_storage listen_;
+struct in6_addr netaddr, destaddr, net; /* Network interface */
+size_t netaddrlen, destaddrlen, netlen, prefixlen;
+struct in6_addr dns1, dns2; /* PCO DNS address */
+size_t dns1len, dns2len;
char *ipup, *ipdown; /* Filename of scripts */
int debug; /* Print debug output */
struct ul255_t pco;
@@ -147,7 +149,8 @@
int create_context_ind(struct pdp_t *pdp) {
- struct in_addr addr;
+ struct in6_addr addr;
+ size_t addrlen = 0;
struct ippoolm_t *member;
if (debug) printf("Received create PDP context request\n");
@@ -160,17 +163,23 @@
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 (ippool_newip(ippool, &member, &addr, 0)) {
+ if (pdp_euaton(&pdp->eua, (struct in_addr*)&addr) == 0)
+ addrlen = 4;
+ else if (pdp_euaton6(&pdp->eua, &addr) == 0)
+ addrlen = 16;
+ else
+ memset(&addr, 0, sizeof(addr)); /* Request dynamic */
+
+ if (ippool_newip(ippool, &member, (struct in_addr*)&addr, addrlen, 0)) {
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
return 0; /* Allready in use, or no more available */
}
- pdp_ntoeua(&member->addr, &pdp->eua);
+ if (member->addrlen == 4)
+ pdp_ntoeua((struct in_addr*)&member->addr, &pdp->eua);
+ else
+ pdp_ntoeua6(&member->addr, &pdp->eua);
pdp->peer = member;
pdp->ipif = tun; /* TODO */
member->peer = pdp;
@@ -183,14 +192,23 @@
/* Callback for receiving messages from tun */
int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) {
struct ippoolm_t *ipm;
- struct in_addr dst;
+ struct in6_addr dst;
+ size_t dstlen;
struct tun_packet_t *iph = (struct tun_packet_t*) pack;
+ struct tun_packet6_t *ip6h = (struct tun_packet6_t*) pack;
- dst.s_addr = iph->dst;
+ if (iph->ver == 4) {
+ memcpy(&dst, &iph->dst, sizeof(struct in_addr));
+ dstlen = 4;
+ }
+ else {
+ memcpy(&dst, ip6h->dst, sizeof(struct in6_addr));
+ dstlen = 16;
+ }
if (debug) printf("Received packet from tun!\n");
- if (ippool_getip(ippool, &ipm, &dst)) {
+ if (ippool_getip(ippool, &ipm, (struct in_addr*)&dst, dstlen)) {
if (debug) printf("Received packet with no destination!!!\n");
return 0;
}
@@ -208,6 +226,16 @@
int main(int argc, char **argv)
{
+ struct addrinfo* ai;
+
+ struct addrinfo hints;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0;
+
+ int err;
+
/* gengeopt declarations */
struct gengetopt_args_info args_info;
@@ -291,13 +319,14 @@
/* Any port listening is not possible as a valid address is */
/* required for create_pdp_context_response messages */
if (args_info.listen_arg) {
- if (!(host = gethostbyname(args_info.listen_arg))) {
+ if ((err = getaddrinfo(args_info.listen_arg, NULL, &hints, &ai))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Invalid listening address: %s!", args_info.listen_arg);
exit(1);
}
else {
- memcpy(&listen_.s_addr, host->h_addr, host->h_length);
+ memcpy(&listen_, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
}
}
else {
@@ -307,18 +336,22 @@
"edit %s configuration file\n", args_info.conf_arg);
exit(1);
}
-
/* 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 in6_addr net;
+ size_t netlen;
+ if(ippool_aton(&net, &netlen, &prefixlen, args_info.net_arg, 0)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Invalid network address: %s!", args_info.net_arg);
exit(1);
}
- netaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
- destaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
+ inc_addr((struct in_addr*)&net, netlen);
+ memcpy(&netaddr, &net, netlen);
+ netaddrlen = netlen;
+ memcpy(&destaddr, &net, netlen);
+ destaddrlen = netlen;
}
else {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
@@ -346,21 +379,37 @@
/* DNS1 and DNS2 */
#ifdef HAVE_INET_ATON
- dns1.s_addr = 0;
if (args_info.pcodns1_arg) {
- if (0 == inet_aton(args_info.pcodns1_arg, &dns1)) {
+ if ((err = getaddrinfo(args_info.pcodns1_arg, NULL, &hints, &ai))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to convert pcodns1!");
exit(1);
}
+ if (ai->ai_family == AF_INET) {
+ dns1len = sizeof(struct in_addr);
+ memcpy(&dns1, &((struct sockaddr_in*)&ai->ai_addr)->sin_addr, dns1len);
+ }
+ else {
+ dns1len = sizeof(struct in6_addr);
+ memcpy(&dns1, &((struct sockaddr_in6*)&ai->ai_addr)->sin6_addr, dns1len);
+ }
+ freeaddrinfo(ai);
}
- dns2.s_addr = 0;
if (args_info.pcodns2_arg) {
- if (0 == inet_aton(args_info.pcodns2_arg, &dns2)) {
+ if ((err = getaddrinfo(args_info.pcodns2_arg, NULL, &hints, &ai))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to convert pcodns2!");
exit(1);
}
+ if (ai->ai_family == AF_INET) {
+ dns2len = sizeof(struct in_addr);
+ memcpy(&dns2, &((struct sockaddr_in*)&ai->ai_addr)->sin_addr, dns2len);
+ }
+ else {
+ dns2len = sizeof(struct in6_addr);
+ memcpy(&dns2, &((struct sockaddr_in6*)&ai->ai_addr)->sin6_addr, dns2len);
+ }
+ freeaddrinfo(ai);
}
#else
dns1.s_addr = 0;
@@ -382,7 +431,6 @@
}
}
#endif
-
pco.l = 20;
pco.v[0] = 0x80; /* x0000yyy x=1, yyy=000: PPP */
@@ -450,7 +498,7 @@
if (debug) printf("gtpclient: Initialising GTP tunnel\n");
- if (gtp_new(&gsn, args_info.statedir_arg, &listen_, GTP_MODE_GGSN)) {
+ if (gtp_new(&gsn, args_info.statedir_arg, (struct sockaddr*)&listen_, GTP_MODE_GGSN)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to create gtp");
exit(1);
@@ -474,7 +522,10 @@
}
if (debug) printf("Setting tun IP address\n");
- if (tun_setaddr(tun, &netaddr, &destaddr, &mask)) {
+ struct in_addr mask;
+ mask.s_addr = htons(0xffffffff << (32 - prefixlen));
+ if ((netaddrlen == 4 && tun_setaddr(tun, (struct in_addr*)&netaddr, (struct in_addr*)&destaddr, &mask))
+ || (netaddrlen == 16 && tun_setaddr6(tun, &netaddr, &destaddr, prefixlen))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to set tun IP address");
if (debug) printf("Failed to set tun IP address\n");
diff --git a/ggsn/ippool.c b/ggsn/ippool.c
index fa3d8af..5f25458 100644
--- a/ggsn/ippool.c
+++ b/ggsn/ippool.c
@@ -16,6 +16,7 @@
#include <syslog.h>
#include <string.h>
#include <sys/socket.h>
+#include <netdb.h>
#include <arpa/inet.h>
#include "syserr.h"
#include "ippool.h"
@@ -32,13 +33,16 @@
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 [1024];
+ char s2 [1024];
+ getnameinfo(&this->member[n].addr, this->member[n].addrlen, s, sizeof(s), s2, sizeof(s2), NI_NUMERICHOST);
+
+ 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;
@@ -50,12 +54,15 @@
struct ippoolm_t *p_prev = NULL;
/* Insert into hash table */
- hash = ippool_hash4(&member->addr) & this->hashmask;
+ if (member->addrlen == 4)
+ hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+ else
+ hash = ippool_hash6(&member->addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash)
p_prev = p;
if (!p_prev)
this->hash[hash] = member;
- else
+ else
p_prev->nexthash = member;
return 0; /* Always OK to insert */
}
@@ -66,7 +73,10 @@
struct ippoolm_t *p_prev = NULL;
/* Find in hash table */
- hash = ippool_hash4(&member->addr) & this->hashmask;
+ if (member->addrlen == 4)
+ hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+ else
+ hash = ippool_hash6(&member->addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
if (p == member) {
break;
@@ -93,66 +103,80 @@
return lookup((unsigned char*) &addr->s_addr, sizeof(addr->s_addr), 0);
}
-#ifndef IPPOOL_NOIP6
unsigned long int ippool_hash6(struct in6_addr *addr) {
- return lookup((unsigned char*) addr->u6_addr8, sizeof(addr->u6_addr8), 0);
+ // TODO: review hash spread for ipv6
+ return lookup((unsigned char*) addr->s6_addr32, sizeof(addr->s6_addr32), 0);
}
-#endif
-
/* Get IP address and mask */
-int ippool_aton(struct in_addr *addr, struct in_addr *mask,
- char *pool, int number) {
+int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t* prefixlen, char *pool, int number) {
+ struct addrinfo* ai;
- /* Parse only first instance of network for now */
- /* Eventually "number" will indicate the token which we want to parse */
+ struct addrinfo hints;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0;
- unsigned int a1, a2, a3, a4;
- unsigned int m1, m2, m3, m4;
- int c;
- int m;
- int masklog;
+ int err;
- 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
- return -1; /* Wrong mask format */
- }
- m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4;
- for (masklog = 0; ((1 << masklog) < ((~m)+1)); masklog++);
- if (((~m)+1) != (1 << masklog)) {
- sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
- return -1; /* Wrong mask format (not all ones followed by all zeros)*/
- }
- mask->s_addr = htonl(m);
- break;
- default:
- sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
- return -1; /* Invalid mask */
- }
+ // Find '/'
+ char* prefixlen_str = strchr(pool, '/');
+ if (prefixlen_str) {
+ *prefixlen_str = '\0';
+ prefixlen_str++;
+ if (*prefixlen_str == '\0') {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Empty prefix length specified");
+ return -1;
+ }
+ }
- if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) {
- sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Wrong IP address format");
- return -1;
- }
- else
- addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
+ // Convert address
+ if ((err = getaddrinfo(pool, NULL, &hints, &ai))) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Bad address");
+ return -1;
+ }
- return 0;
+ // Copy address, set lengths
+ if (ai->ai_family == AF_INET) {
+ *prefixlen = 32;
+ *addrlen = sizeof(struct in_addr);
+ memcpy(addr, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, sizeof(struct in_addr));
+ }
+ else {
+ *prefixlen = 128;
+ *addrlen = sizeof(struct in6_addr);
+ memcpy(addr, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, sizeof(struct in6_addr));
+ }
+
+ freeaddrinfo(ai);
+
+ // Parse prefixlen
+ if (prefixlen_str) {
+ char* e;
+ *prefixlen = strtol(prefixlen_str, &e, 10);
+ if (*e != '\0') {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen is not an int");
+ return -1;
+ }
+ }
+
+ if (*prefixlen > (*addrlen * 8)) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen too big");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Increases ip4/6 address by 1 */
+void inc_addr(struct in_addr* addr, size_t addrlen)
+{
+ uint8_t* a = (uint8_t*)&addr->s_addr;
+ for (; addrlen > 0; addrlen--) {
+ if (++a[addrlen-1])
+ break;
+ }
}
/* Create new address pool */
@@ -162,10 +186,12 @@
/* 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;
+ struct in6_addr addr;
+ size_t addrlen;
+ size_t addrprefixlen;
+ struct in6_addr stataddr;
+ size_t stataddrlen;
+ size_t stataddrprefixlen;
unsigned int m;
int listsize;
int dynsize;
@@ -175,7 +201,7 @@
dynsize = 0;
}
else {
- if (ippool_aton(&addr, &mask, dyn, 0)) {
+ if (ippool_aton(&addr, &addrlen, &addrprefixlen, dyn, 0)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to parse dynamic pool");
return -1;
@@ -186,8 +212,10 @@
flags |= IPPOOL_NONETWORK;
}
- m = ntohl(mask.s_addr);
- dynsize = ((~m)+1);
+ dynsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
+
+ //m = ntohl(mask.s_addr);
+ //dynsize = ((~m)+1);
if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
dynsize--;
if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */
@@ -198,18 +226,17 @@
if (!allowstat) {
statsize = 0;
- stataddr.s_addr = 0;
- statmask.s_addr = 0;
+ stataddrlen = 0;
+ stataddrprefixlen = 0;
}
else {
- if (ippool_aton(&stataddr, &statmask, stat, 0)) {
+ if (ippool_aton(&stataddr, &stataddrlen, &stataddrprefixlen, stat, 0)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to parse static range");
return -1;
}
- m = ntohl(statmask.s_addr);
- statsize = ((~m)+1);
+ statsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE;
}
@@ -223,8 +250,10 @@
(*this)->allowdyn = allowdyn;
(*this)->allowstat = allowstat;
- (*this)->stataddr = stataddr;
- (*this)->statmask = statmask;
+ if (stataddrlen > 0)
+ memcpy(&(*this)->stataddr, &stataddr, stataddrlen);
+ (*this)->stataddrlen = stataddrlen;
+ (*this)->stataddrprefixlen = stataddrprefixlen;
(*this)->listsize += listsize;
if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){
@@ -252,15 +281,20 @@
(*this)->firstdyn = NULL;
(*this)->lastdyn = NULL;
+
+ if (flags & IPPOOL_NOGATEWAY) {
+ inc_addr(&addr, addrlen);
+ inc_addr(&addr, addrlen);
+ }
+ else if (flags & IPPOOL_NONETWORK)
+ inc_addr(&addr, addrlen);
+
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);
-
+ memcpy(&(*this)->member[i].addr, &addr, addrlen);
+ (*this)->member[i].addrlen = addrlen;
+ inc_addr(&addr, addrlen);
+
(*this)->member[i].inuse = 0;
/* Insert into list of unused */
@@ -280,8 +314,10 @@
(*this)->firststat = NULL;
(*this)->laststat = NULL;
for (i = dynsize; i<listsize; i++) {
+ // TODO: set addrlen to 0?
+ memset(&(*this)->member[i].addr, 0, addrlen);
+ (*this)->member[i].addrlen = addrlen;
- (*this)->member[i].addr.s_addr = 0;
(*this)->member[i].inuse = 0;
/* Insert into list of unused */
@@ -312,14 +348,19 @@
/* Find an IP address in the pool */
int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr) {
+ struct in_addr *addr, size_t addrlen) {
struct ippoolm_t *p;
uint32_t hash;
/* Find in hash table */
- hash = ippool_hash4(addr) & this->hashmask;
+ if (addrlen == 4)
+ hash = ippool_hash4(addr) & this->hashmask;
+ else
+ hash = ippool_hash6(addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
- if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
+ if (p->addrlen == addrlen
+ && memcmp(&p->addr, addr, addrlen) == 0
+ && (p->inuse)) {
if (member) *member = p;
return 0;
}
@@ -337,7 +378,7 @@
* address space.
**/
int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr, int statip) {
+ struct in_addr *addr, size_t addrlen, int statip) {
struct ippoolm_t *p;
struct ippoolm_t *p2 = NULL;
uint32_t hash;
@@ -357,15 +398,25 @@
if (0) (void)ippool_printaddr(this);
/* First check to see if this type of address is allowed */
- if ((addr) && (addr->s_addr) && statip) { /* IP address given */
+ int specified = 0;
+ if (addr) {
+ if (addrlen == 4 && ((struct in_addr*)&addr)->s_addr)
+ specified = 1;
+ if (addrlen == 16 && !IN6_IS_ADDR_UNSPECIFIED(addr))
+ specified = 1;
+ }
+ if (specified && statip) { /* IP address given */
if (!this->allowstat) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static IP address not allowed");
return -1;
}
+ // TODO: ipv6 mask check
+/*
if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static out of range");
return -1;
}
+*/
}
else {
if (!this->allowdyn) {
@@ -376,11 +427,14 @@
}
/* 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;
+ if (addrlen == 4)
+ hash = ippool_hash4(addr) & this->hashmask;
+ else
+ hash = ippool_hash6(addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
- if ((p->addr.s_addr == addr->s_addr)) {
+ if (p->addrlen == addrlen && !memcmp(&p->addr, &addr, addrlen)) {
p2 = p;
break;
}
@@ -431,7 +485,7 @@
/* 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(LOG_ERR, __FILE__, __LINE__, 0,
"No more IP addresses available");
@@ -452,7 +506,7 @@
p2->next = NULL;
p2->prev = NULL;
p2->inuse = 2; /* Static address in use */
- memcpy(&p2->addr, addr, sizeof(addr));
+ memcpy(&p2->addr, addr, addrlen);
*member = p2;
(void)ippool_hashadd(this, *member);
if (0) (void)ippool_printaddr(this);
@@ -507,7 +561,8 @@
this->laststat = member;
member->inuse = 0;
- member->addr.s_addr = 0;
+ memset(&member->addr, 0, sizeof(member->addr));
+ member->addrlen = 0;
member->peer = NULL;
member->nexthash = NULL;
if (0) (void)ippool_printaddr(this);
@@ -517,10 +572,3 @@
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/ggsn/ippool.h b/ggsn/ippool.h
index 02691a6..432fa8d 100644
--- a/ggsn/ippool.h
+++ b/ggsn/ippool.h
@@ -26,8 +26,6 @@
in RFC2373.
*/
-#define IPPOOL_NOIP6
-
#define IPPOOL_NONETWORK 0x01
#define IPPOOL_NOBROADCAST 0x02
#define IPPOOL_NOGATEWAY 0x04
@@ -40,7 +38,9 @@
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 in6_addr stataddr; /* Static address range network address */
+ size_t stataddrlen;
+ size_t stataddrprefixlen;
struct in_addr statmask; /* Static address range network mask */
struct ippoolm_t *member; /* Listsize array of members */
unsigned int hashsize; /* Size of hash table */
@@ -54,11 +54,8 @@
};
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
+ size_t addrlen;
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 */
@@ -81,25 +78,22 @@
/* Find an IP address in the pool */
extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr);
+ struct in_addr *addr, size_t addrlen);
/* 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 in_addr *addr, size_t addrlen, 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,
+extern int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t *prefixlen,
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
+
+void inc_addr(struct in_addr* addr, size_t addrlen);
#endif /* !_IPPOOL_H */
diff --git a/ggsn/tun.c b/ggsn/tun.c
index 1cc706b..96ee7ca 100644
--- a/ggsn/tun.c
+++ b/ggsn/tun.c
@@ -71,6 +71,13 @@
#if defined(__linux__)
+// From linux/ipv6.h
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+
int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
{
int len = RTA_LENGTH(dlen);
@@ -230,6 +237,9 @@
}
*/
+#ifdef foo
+// None of this is currently required for IPv6
+//
int tun_addaddr(struct tun_t *this,
struct in_addr *addr,
struct in_addr *dstaddr,
@@ -396,6 +406,7 @@
#endif
}
+#endif //foo
int tun_setaddr(struct tun_t *this,
@@ -431,7 +442,7 @@
}
if (addr) { /* Set the interface address */
- this->addr.s_addr = addr->s_addr;
+ ((struct in_addr*)&this->addr)->s_addr = addr->s_addr;
((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = addr->s_addr;
if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
if (errno != EEXIST) {
@@ -448,7 +459,7 @@
}
if (dstaddr) { /* Set the destination address */
- this->dstaddr.s_addr = dstaddr->s_addr;
+ ((struct in_addr*)&this->dstaddr)->s_addr = addr->s_addr;
((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr =
dstaddr->s_addr;
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
@@ -501,6 +512,88 @@
return 0;
}
+
+int tun_setaddr6(struct tun_t *this,
+ struct in6_addr *addr,
+ struct in6_addr *dstaddr,
+ size_t prefixlen)
+{
+ struct in6_ifreq ifr;
+ struct ifreq namereq;
+ int fd;
+
+ this->ip6 = 1;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ memset (&namereq, '\0', sizeof (namereq));
+
+ /* Create a channel to the NET kernel. */
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "socket() failed");
+ return -1;
+ }
+
+ // Get interface index
+ strncpy(namereq.ifr_name, this->devname, IFNAMSIZ);
+ namereq.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+ if (ioctl(fd, SIOGIFINDEX, &namereq) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Could not get ifindex");
+ return -1;
+ }
+ ifr.ifr6_ifindex = namereq.ifr_ifindex;
+
+ ifr.ifr6_prefixlen = prefixlen;
+
+ if (addr) { /* Set the interface address */
+ memcpy(&this->addr, addr, sizeof(struct in6_addr));
+ memcpy(&ifr.ifr6_addr, addr, sizeof(struct in6_addr));
+ if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
+ if (errno != EEXIST) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "ioctl(SIOCSIFADDR) failed");
+ }
+ else {
+ sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
+ "ioctl(SIOCSIFADDR): Address already exists");
+ }
+ close(fd);
+ return -1;
+ }
+ }
+
+#ifdef xxx
+ // Looks like this is not possible/necessary for ipv6?
+ if (dstaddr) { /* Set the destination address */
+ memcpy(&this->dstaddr, dstaddr, sizeof(struct in6_addr));
+ memcpy(&ifr.ifr6_addr, dstaddr, sizeof(struct in6_addr));
+ if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "ioctl(SIOCSIFDSTADDR) failed");
+ close(fd);
+ return -1;
+ }
+ }
+#endif //xxx
+
+ close(fd);
+ this->addrs++;
+
+ /* On linux the route to the interface is set automatically
+ on FreeBSD we have to do this manually */
+
+ /* TODO: How does it work on Solaris? */
+
+ tun_sifflags(this, IFF_UP | IFF_RUNNING);
+
+#if defined(__FreeBSD__) || defined (__APPLE__)
+ tun_addroute(this, dstaddr, addr, netmask);
+ this->routes = 1;
+#endif
+
+ return 0;
+}
+
int tun_route(struct tun_t *this,
struct in_addr *dst,
@@ -666,6 +759,7 @@
(*tun)->cb_ind = NULL;
(*tun)->addrs = 0;
(*tun)->routes = 0;
+ (*tun)->ip6 = 0;
#if defined(__linux__)
/* Open the actual tun device */
@@ -797,8 +891,9 @@
int tun_free(struct tun_t *tun)
{
+ // TODO: ipv6
if (tun->routes) {
- tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
+ //tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
}
if (close(tun->fd)) {
@@ -883,9 +978,10 @@
char snet[TUN_ADDRSIZE];
char smask[TUN_ADDRSIZE];
- strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
+ //TODO: fix for IPv6
+ //strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
snet[sizeof(snet)-1] = 0;
- strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
+ //strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
smask[sizeof(smask)-1] = 0;
/* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
diff --git a/ggsn/tun.h b/ggsn/tun.h
index 7972c53..1c239eb 100644
--- a/ggsn/tun.h
+++ b/ggsn/tun.h
@@ -17,9 +17,18 @@
#define TUN_ADDRSIZE 128
#define TUN_NLBUFSIZE 1024
+/* This should not be re-declared! Why not just use netinet/ip(6).h?
+ * It is not completely functional as it is here. Just the minimum
+ * amount of hacking has been done to make it work. */
struct tun_packet_t {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int ihl:4;
+ unsigned int ver:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
unsigned int ver:4;
unsigned int ihl:4;
+#endif
unsigned int dscp:6;
unsigned int ecn:2;
unsigned int length:16;
@@ -29,8 +38,19 @@
unsigned int ttl:8;
unsigned int protocol:8;
unsigned int check:16;
- unsigned int src:32;
- unsigned int dst:32;
+ uint32_t src;
+ uint32_t dst;
+};
+
+struct tun_packet6_t {
+ unsigned int ver:4;
+ unsigned int tclass:8;
+ unsigned int flowlabel:20;
+ unsigned int payloadlen:16;
+ unsigned int nexthdr:8;
+ unsigned int hoplim:8;
+ unsigned char src[16];
+ unsigned char dst[16];
};
@@ -39,10 +59,12 @@
*************************************************************/
struct tun_t {
+ int ip6;
int fd; /* File descriptor to tun interface */
- struct in_addr addr;
- struct in_addr dstaddr;
- struct in_addr netmask;
+ struct in6_addr addr;
+ struct in6_addr dstaddr;
+ struct in_addr netmask; //ipv4
+ size_t prefixlen;
int addrs; /* Number of allocated IP addresses */
int routes; /* One if we allocated an automatic route */
char devname[IFNAMSIZ];/* Name of the tun device */
@@ -55,19 +77,13 @@
extern int tun_decaps(struct tun_t *this);
extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
-extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
- struct in_addr *dstaddr, struct in_addr *netmask);
-
-
extern int tun_setaddr(struct tun_t *this, struct in_addr *our_adr,
struct in_addr *his_adr, struct in_addr *net_mask);
-
-int tun_addroute(struct tun_t *this, struct in_addr *dst,
- struct in_addr *gateway, struct in_addr *mask);
+extern int tun_setaddr6(struct tun_t *this, struct in6_addr *our_adr,
+ struct in6_addr *his_adr, size_t prefixlen);
extern int tun_set_cb_ind(struct tun_t *this,
int (*cb_ind) (struct tun_t *tun, void *pack, unsigned len));
-
extern int tun_runscript(struct tun_t *tun, char* script);
diff --git a/gtp/gtp.c b/gtp/gtp.c
index 5e6c4ab..cceabda 100644
--- a/gtp/gtp.c
+++ b/gtp/gtp.c
@@ -42,6 +42,7 @@
#include <string.h>
#include <errno.h>
#include <fcntl.h>
+#include <netdb.h>
#include <arpa/inet.h>
@@ -132,13 +133,13 @@
int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
- int (*cb) (struct sockaddr_in *peer)) {
+ int (*cb) (struct sockaddr *peer)) {
gsn->cb_unsup_ind = cb;
return 0;
}
int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
- int (*cb) (struct sockaddr_in *peer)) {
+ int (*cb) (struct sockaddr *peer)) {
gsn->cb_extheader_ind = cb;
return 0;
}
@@ -397,20 +398,13 @@
int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
union gtp_packet *packet, int len,
- struct in_addr *inetaddr, void *cbp) {
- struct sockaddr_in addr;
+ struct sockaddr *sockaddr, void *cbp) {
struct qmsg_t *qmsg;
int fd;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = *inetaddr;
-#if defined(__FreeBSD__) || defined(__APPLE__)
- addr.sin_len = sizeof(addr);
-#endif
-
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
- addr.sin_port = htons(GTP0_PORT);
+ // sin_port in same place for sockaddr_in and sockaddr_in6
+ ((struct sockaddr_in*)sockaddr)->sin_port = htons(GTP0_PORT);
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
packet->gtp0.h.seq = hton16(gsn->seq_next);
if (pdp)
@@ -424,7 +418,7 @@
fd = gsn->fd0;
}
else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
- addr.sin_port = htons(GTP1C_PORT);
+ ((struct sockaddr_in*)sockaddr)->sin_port = htons(GTP1C_PORT);
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
packet->gtp1l.h.seq = hton16(gsn->seq_next);
if (pdp && ((packet->gtp1l.h.type == GTP_GPDU) ||
@@ -439,14 +433,14 @@
}
if (sendto(fd, packet, len, 0,
- (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ sockaddr, sockaddr->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
gsn->err_sendto++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, len, strerror(errno));
return -1;
}
/* Use new queue structure */
- if (queue_newmsg(gsn->queue_req, &qmsg, &addr, gsn->seq_next)) {
+ if (queue_newmsg(gsn->queue_req, &qmsg, sockaddr, gsn->seq_next)) {
gsn->err_queuefull++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "Retransmit queue is full");
}
@@ -509,7 +503,7 @@
}
else {
if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
- (struct sockaddr *) &qmsg->peer, sizeof(struct sockaddr_in)) < 0) {
+ (struct sockaddr *) &qmsg->peer, qmsg->peer.ss_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
gsn->err_sendto++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd0, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
}
@@ -550,7 +544,7 @@
int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
union gtp_packet *packet, int len,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
uint16_t seq, uint64_t tid) {
struct qmsg_t *qmsg;
@@ -583,7 +577,7 @@
}
if (sendto(fd, packet, len, 0,
- (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
+ peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
gsn->err_sendto++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, len, strerror(errno));
return -1;
@@ -651,7 +645,7 @@
}
int gtp_dublicate(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, uint16_t seq) {
+ struct sockaddr *peer, uint16_t seq) {
struct qmsg_t *qmsg;
if(queue_seqget(gsn->queue_resp, &qmsg, peer, seq)) {
@@ -664,7 +658,7 @@
}
if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
- (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
+ peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
gsn->err_sendto++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", qmsg->fd, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
}
@@ -718,10 +712,10 @@
-int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
+int gtp_new(struct gsn_t **gsn, char *statedir, struct sockaddr *listen,
int mode)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
syslog(LOG_ERR, "GTP: gtp_newgsn() started");
@@ -748,71 +742,74 @@
(*gsn)->cb_data_ind = 0;
/* Store function parameters */
- (*gsn)->gsnc = *listen;
- (*gsn)->gsnu = *listen;
+ memcpy(&(*gsn)->gsnc, listen, listen->sa_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
+ memcpy(&(*gsn)->gsnu, listen, listen->sa_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
(*gsn)->mode = mode;
+ int domain = AF_INET;
+ socklen_t sa_len = sizeof(struct sockaddr_in);
+ if (listen->sa_family == AF_INET6) {
+ domain = AF_INET6;
+ sa_len = sizeof(struct sockaddr_in6);
+ }
/* Create GTP version 0 socket */
- if (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+ if (((*gsn)->fd0 = socket(domain, SOCK_DGRAM, 0)) < 0 ) {
(*gsn)->err_socket++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
return -1;
}
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = *listen; /* Same IP for user traffic and signalling*/
- addr.sin_port = htons(GTP0_PORT);
#if defined(__FreeBSD__) || defined(__APPLE__)
addr.sin_len = sizeof(addr);
#endif
+ memcpy(&addr, listen, sa_len);
+ // port member in same position for sockaddr_in and sockaddr_in6
+ ((struct sockaddr_in*)&addr)->sin_port = htons(GTP0_PORT);
- if (bind((*gsn)->fd0, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind((*gsn)->fd0, (struct sockaddr *) &addr, sa_len) < 0) {
(*gsn)->err_socket++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd0=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd0, (unsigned long) &addr, sizeof(addr), strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd0=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd0, (unsigned long) &addr, sa_len, strerror(errno));
return -1;
}
/* Create GTP version 1 control plane socket */
- if (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+ if (((*gsn)->fd1c = socket(domain, SOCK_DGRAM, 0)) < 0 ) {
(*gsn)->err_socket++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
return -1;
}
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = *listen; /* Same IP for user traffic and signalling*/
- addr.sin_port = htons(GTP1C_PORT);
#if defined(__FreeBSD__) || defined(__APPLE__)
addr.sin_len = sizeof(addr);
#endif
+ memcpy(&addr, listen, sa_len);
+ // port member in same position for sockaddr_in and sockaddr_in6
+ ((struct sockaddr_in*)&addr)->sin_port = htons(GTP1C_PORT);
- if (bind((*gsn)->fd1c, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind((*gsn)->fd1c, (struct sockaddr *) &addr, sa_len) < 0) {
(*gsn)->err_socket++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sizeof(addr), strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sa_len, strerror(errno));
return -1;
}
/* Create GTP version 1 user plane socket */
- if (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+ if (((*gsn)->fd1u = socket(domain, SOCK_DGRAM, 0)) < 0 ) {
(*gsn)->err_socket++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
return -1;
}
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = *listen; /* Same IP for user traffic and signalling*/
- addr.sin_port = htons(GTP1U_PORT);
#if defined(__FreeBSD__) || defined(__APPLE__)
addr.sin_len = sizeof(addr);
#endif
+ memcpy(&addr, listen, sa_len);
+ // port member in same position for sockaddr_in and sockaddr_in6
+ ((struct sockaddr_in*)&addr)->sin_port = htons(GTP1U_PORT);
- if (bind((*gsn)->fd1u, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind((*gsn)->fd1u, (struct sockaddr *) &addr, sa_len) < 0) {
(*gsn)->err_socket++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sizeof(addr), strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sa_len, strerror(errno));
return -1;
}
@@ -864,7 +861,7 @@
/* Send off an echo request */
int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
- struct in_addr *inetaddr)
+ struct sockaddr *inetaddr)
{
union gtp_packet packet;
unsigned int length = get_default_gtp(version, GTP_ECHO_REQ, &packet);
@@ -873,7 +870,7 @@
/* Send off an echo reply */
int gtp_echo_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len)
{
union gtp_packet packet;
@@ -885,7 +882,7 @@
/* Handle a received echo request */
-int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr *peer,
int fd, void *pack, unsigned len) {
/* Check if it was a dublicate request */
@@ -896,7 +893,7 @@
}
/* Handle a received echo reply */
-int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr *peer,
void *pack, unsigned len) {
union gtpie_member *ie[GTPIE_SIZE];
unsigned char recovery;
@@ -942,7 +939,7 @@
* only listen to the GTP0 port, and therefore will never receive
* anything else than GTP0 */
-int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr *peer,
int fd, void *pack, unsigned len)
{
union gtp_packet packet;
@@ -954,7 +951,7 @@
}
/* Handle a Version Not Supported message */
-int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr *peer,
void *pack, unsigned len) {
if (gsn->cb_unsup_ind) gsn->cb_unsup_ind(peer);
@@ -963,7 +960,7 @@
}
/* Send off an Supported Extension Headers Notification */
-int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr *peer,
int fd, void *pack, unsigned len)
{
union gtp_packet packet;
@@ -983,7 +980,7 @@
}
/* Handle a Supported Extension Headers Notification */
-int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr *peer,
void *pack, unsigned len) {
if (gsn->cb_extheader_ind) gsn->cb_extheader_ind(peer);
@@ -1223,7 +1220,7 @@
/* Handle Create PDP Context Request */
int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len) {
struct pdp_t *pdp, *pdp_old;
struct pdp_t pdp_buf;
@@ -1246,7 +1243,7 @@
}
pdp->seq = seq;
- pdp->sa_peer = *peer;
+ memcpy(&pdp->sa_peer, peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
pdp->fd = fd;
pdp->version = version;
@@ -1473,9 +1470,32 @@
}
/* Initialize our own IP addresses */
- in_addr2gsna(&pdp->gsnlc, &gsn->gsnc);
- in_addr2gsna(&pdp->gsnlu, &gsn->gsnu);
-
+ struct in6_addr* a;
+ size_t alen;
+ if (gsn->gsnc.ss_family == AF_INET) {
+ struct sockaddr_in* s = (struct sockaddr_in*)&gsn->gsnc;
+ a = (struct in6_addr*)&s->sin_addr;
+ alen = sizeof(struct in_addr);
+ }
+ else {
+ struct sockaddr_in6* s = (struct sockaddr_in6*)&gsn->gsnc;
+ a = &s->sin6_addr;
+ alen = sizeof(struct in6_addr);
+ }
+ in_addr2gsna(&pdp->gsnlc, a, alen);
+
+ if (gsn->gsnu.ss_family == AF_INET) {
+ struct sockaddr_in* s = (struct sockaddr_in*)&gsn->gsnu;
+ a = (struct in6_addr*)&s->sin_addr;
+ alen = sizeof(struct in_addr);
+ }
+ else {
+ struct sockaddr_in6* s = (struct sockaddr_in6*)&gsn->gsnu;
+ a = &s->sin6_addr;
+ alen = sizeof(struct in6_addr);
+ }
+ in_addr2gsna(&pdp->gsnlu, a, alen);
+
if (GTP_DEBUG) printf("gtp_create_pdp_ind: Before pdp_tidget\n");
if (!pdp_getimsi(&pdp_old, pdp->imsi, pdp->nsapi)) {
@@ -1550,7 +1570,7 @@
/* Handle Create PDP Context Response */
int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
void *pack, unsigned len) {
struct pdp_t *pdp;
union gtpie_member *ie[GTPIE_SIZE];
@@ -1741,7 +1761,7 @@
/* API: Send Update PDP Context Request */
int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
- struct in_addr* inetaddr) {
+ struct sockaddr* inetaddr) {
union gtp_packet packet;
unsigned int length = get_default_gtp(pdp->version, GTP_UPDATE_PDP_REQ, &packet);
@@ -1817,7 +1837,7 @@
/* Send Update PDP Context Response */
int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len,
struct pdp_t *pdp, uint8_t cause) {
@@ -1878,7 +1898,7 @@
/* Handle Update PDP Context Request */
int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len) {
struct pdp_t *pdp;
struct pdp_t pdp_backup;
@@ -2098,7 +2118,7 @@
/* Handle Update PDP Context Response */
int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
void *pack, unsigned len) {
struct pdp_t *pdp;
union gtpie_member *ie[GTPIE_SIZE];
@@ -2199,13 +2219,15 @@
int teardown) {
union gtp_packet packet;
unsigned int length = get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet);
- struct in_addr addr;
+ struct in6_addr addr;
+ size_t addrlen;
struct pdp_t *linked_pdp;
struct pdp_t *secondary_pdp;
int n;
int count = 0;
- if (gsna2in_addr(&addr, &pdp->gsnrc)) {
+ addrlen = sizeof(addr);
+ if (gsna2in_addr(&addr, &addrlen, &pdp->gsnrc)) {
gsn->err_address++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "GSN address conversion failed");
return EOF;
@@ -2235,7 +2257,22 @@
pdp->nsapi);
}
- gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp);
+ struct sockaddr_storage sa_s;
+ struct sockaddr_in* sa_in = (struct sockaddr_in*)&sa_s;
+ struct sockaddr_in6* sa_in6 = (struct sockaddr_in*)&sa_s;
+ memset(&sa_s, 0, sizeof(sa_s));
+ void* p;
+ if (addrlen == 4) {
+ sa_in->sin_family = AF_INET;
+ p = &sa_in->sin_addr;
+ }
+ else {
+ sa_in6->sin6_family = AF_INET6;
+ p = &sa_in6->sin6_addr;
+ }
+ memcpy(p, &addr, addrlen);
+
+ gtp_req(gsn, pdp->version, pdp, &packet, length, (struct sockaddr*)&sa_s, cbp);
if (teardown) { /* Remove all contexts */
for (n=0; n< PDP_MAXNSAPI; n++) {
@@ -2268,7 +2305,7 @@
/* Send Delete PDP Context Response */
int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len,
struct pdp_t *pdp, struct pdp_t *linked_pdp,
uint8_t cause, int teardown)
@@ -2317,7 +2354,7 @@
/* Handle Delete PDP Context Request */
int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len) {
struct pdp_t *pdp = NULL;
struct pdp_t *linked_pdp = NULL;
@@ -2399,7 +2436,7 @@
/* Handle Delete PDP Context Response */
int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
void *pack, unsigned len) {
union gtpie_member *ie[GTPIE_SIZE];
uint8_t cause;
@@ -2445,7 +2482,7 @@
/* Send Error Indication (response to a GPDU message */
int gtp_error_ind_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len)
{
union gtp_packet packet;
@@ -2457,7 +2494,7 @@
/* Handle Error Indication */
int gtp_error_ind_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
void *pack, unsigned len) {
struct pdp_t *pdp;
@@ -2479,7 +2516,7 @@
}
int gtp_gpdu_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len) {
int hlen = GTP1_HEADER_SIZE_SHORT;
@@ -2516,7 +2553,16 @@
}
/* If the GPDU was not from the peer GSN tell him to delete context */
- if (memcmp(&peer->sin_addr, pdp->gsnru.v, pdp->gsnru.l)) { /* TODO Range? */
+ struct sockaddr_in* peer4 = NULL;
+ struct sockaddr_in6* peer6 = NULL;
+ if (peer->sa_family == AF_INET)
+ peer4 = (struct sockaddr_in*)peer;
+ else
+ peer6 = (struct sockaddr_in6*)peer;
+
+ if ((peer4 && memcmp(&peer4->sin_addr, pdp->gsnru.v, pdp->gsnru.l))
+ || (peer6 && memcmp(&peer6->sin6_addr, pdp->gsnru.v, pdp->gsnru.l)) ) {
+
gsn->err_unknownpdp++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Unknown PDP context");
@@ -2540,7 +2586,7 @@
int gtp_decaps0(struct gsn_t *gsn)
{
unsigned char buffer[PACKET_MAX];
- struct sockaddr_in peer;
+ struct sockaddr_storage peer;
size_t peerlen;
int status;
struct gtp0_header *pheader;
@@ -2672,7 +2718,7 @@
int gtp_decaps1c(struct gsn_t *gsn)
{
unsigned char buffer[PACKET_MAX];
- struct sockaddr_in peer;
+ struct sockaddr_storage peer;
size_t peerlen;
int status;
struct gtp1_header_short *pheader;
@@ -2831,7 +2877,7 @@
int gtp_decaps1u(struct gsn_t *gsn)
{
unsigned char buffer[PACKET_MAX];
- struct sockaddr_in peer;
+ struct sockaddr_storage peer;
size_t peerlen;
int status;
struct gtp1_header_short *pheader;
@@ -2953,22 +2999,33 @@
void *pack, unsigned len)
{
union gtp_packet packet;
- struct sockaddr_in addr;
+ struct sockaddr_storage addr_s;
+ struct sockaddr_in* addr = (struct sockaddr_in*)&addr_s;
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr_s;
int fd;
int length;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
-#if defined(__FreeBSD__) || defined(__APPLE__)
- addr.sin_len = sizeof(addr);
-#endif
+ memset(&addr_s, 0, sizeof(addr_s));
- memcpy(&addr.sin_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
+ if (pdp->gsnru.l == 4) {
+ addr->sin_family = AF_INET;
+ memcpy(&addr->sin_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ addr->sin_len = sizeof(*addr);
+#endif
+ }
+ else {
+ addr6->sin6_family = AF_INET6;
+ memcpy(&addr6->sin6_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ addr6->sin6_len = sizeof(*addr6);
+#endif
+ }
if (pdp->version == 0) {
length = GTP0_HEADER_SIZE+len;
- addr.sin_port = htons(GTP0_PORT);
+ addr->sin_port = htons(GTP0_PORT);
fd = gsn->fd0;
get_default_gtp(0, GTP_GPDU, &packet);
@@ -2989,7 +3046,7 @@
else if (pdp->version == 1) {
length = GTP1_HEADER_SIZE_LONG+len;
- addr.sin_port = htons(GTP1U_PORT);
+ addr->sin_port = htons(GTP1U_PORT);
fd = gsn->fd1u;
get_default_gtp(1, GTP_GPDU, &packet);
@@ -3019,7 +3076,7 @@
}
if (sendto(fd, &packet, length, 0,
- (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ (struct sockaddr *) &addr_s, addr_s.ss_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
gsn->err_sendto++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, GTP0_HEADER_SIZE+len, strerror(errno));
return EOF;
@@ -3067,6 +3124,7 @@
return 0;
}
+// TODO: ipv6?
int eua2ipv4(struct in_addr *dst, struct ul66_t *eua) {
if ((eua->l != 6) ||
(eua->v[0] != 0xf1) ||
@@ -3076,16 +3134,21 @@
return 0;
}
-int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna) {
- memset(dst, 0, sizeof(struct in_addr));
- if (gsna->l != 4) return EOF; /* Return if not IPv4 */
+int gsna2in_addr(struct in6_addr *dst, size_t* dstlen, struct ul16_t *gsna) {
+ memset(dst, 0, *dstlen);
+ if (gsna->l != 4 && gsna->l != 16) return EOF; /* Return if not IPv4 */
+ if (*dstlen < gsna->l) {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "dstlen too short");
+ return EOF;
+ }
memcpy(dst, gsna->v, gsna->l);
+ *dstlen = gsna->l;
return 0;
}
-int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src) {
+int in_addr2gsna(struct ul16_t *gsna, struct in6_addr *src, size_t srclen) {
memset(gsna, 0, sizeof(struct ul16_t));
- gsna->l = 4;
+ gsna->l = srclen;
memcpy(gsna->v, src, gsna->l);
return 0;
}
diff --git a/gtp/gtp.h b/gtp/gtp.h
index 7b5083b..5ca22eb 100644
--- a/gtp/gtp.h
+++ b/gtp/gtp.h
@@ -239,8 +239,8 @@
int fd1c; /* GTP1 control plane file descriptor */
int fd1u; /* GTP0 user plane file descriptor */
int mode; /* Mode of operation: GGSN or SGSN */
- struct in_addr gsnc; /* IP address of this gsn for signalling */
- struct in_addr gsnu; /* IP address of this gsn for user traffic */
+ struct sockaddr_storage gsnc; /* IP address of this gsn for signalling */
+ struct sockaddr_storage gsnu; /* IP address of this gsn for user traffic */
/* Parameters related to signalling messages */
uint16_t seq_next; /* Next sequence number to use */
@@ -256,8 +256,8 @@
/* Call back functions */
int (*cb_delete_context) (struct pdp_t*);
int (*cb_create_context_ind) (struct pdp_t*);
- int (*cb_unsup_ind) (struct sockaddr_in *peer);
- int (*cb_extheader_ind) (struct sockaddr_in *peer);
+ int (*cb_unsup_ind) (struct sockaddr *peer);
+ int (*cb_extheader_ind) (struct sockaddr *peer);
int (*cb_conf) (int type, int cause, struct pdp_t *pdp, void* cbp);
int (*cb_data_ind) (struct pdp_t* pdp, void* pack, unsigned len);
@@ -290,7 +290,7 @@
/* External API functions */
extern const char* gtp_version();
-extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
+extern int gtp_new(struct gsn_t **gsn, char *statedir, struct sockaddr *listen,
int mode);
extern int gtp_free(struct gsn_t *gsn);
@@ -309,7 +309,7 @@
int cause);
extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp,
- void *cbp, struct in_addr* inetaddr);
+ void *cbp, struct sockaddr* inetaddr);
extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp, int teardown);
@@ -334,10 +334,10 @@
int (*cb_create_context) (struct pdp_t* pdp)); */
extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
- int (*cb) (struct sockaddr_in *peer));
+ int (*cb) (struct sockaddr *peer));
extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
- int (*cb) (struct sockaddr_in *peer));
+ int (*cb) (struct sockaddr *peer));
extern int gtp_set_cb_conf(struct gsn_t *gsn,
@@ -347,58 +347,58 @@
/* Internal functions (not part of the API */
extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
- struct in_addr *inetaddrs);
+ struct sockaddr *inetaddrs);
extern int gtp_echo_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len);
extern int gtp_echo_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len);
extern int gtp_echo_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
void *pack, unsigned len);
extern int gtp_unsup_req(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
int fd, void *pack, unsigned len);
-extern int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+extern int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr *peer,
void *pack, unsigned len);
extern int gtp_create_pdp_resp(struct gsn_t *gsn, int version,
struct pdp_t *pdp, uint8_t cause);
extern int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len);
extern int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
void *pack, unsigned len);
extern int gtp_update_pdp_req(struct gsn_t *gsn, int version, void *cbp,
- struct in_addr* inetaddr, struct pdp_t *pdp);
+ struct sockaddr* inetaddr, struct pdp_t *pdp);
extern int gtp_delete_pdp_req(struct gsn_t *gsn, int version, void *cbp,
struct pdp_t *pdp);
extern int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len,
struct pdp_t *pdp, struct pdp_t *linked_pdp,
uint8_t cause, int teardown);
extern int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, int fd,
+ struct sockaddr *peer, int fd,
void *pack, unsigned len);
extern int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr *peer,
void *pack, unsigned len);
extern int ipv42eua(struct ul66_t *eua, struct in_addr *src);
extern int eua2ipv4(struct in_addr *dst, struct ul66_t *eua);
-extern int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna);
-extern int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src);
+extern int gsna2in_addr(struct in6_addr *dst, size_t* dstlen, struct ul16_t *gsna);
+extern int in_addr2gsna(struct ul16_t *gsna, struct in6_addr *src, size_t srclen);
#endif /* !_GTP_H */
diff --git a/gtp/pdp.c b/gtp/pdp.c
index 648d70d..642547c 100644
--- a/gtp/pdp.c
+++ b/gtp/pdp.c
@@ -328,6 +328,14 @@
return 0;
}
+int pdp_ntoeua6(struct in6_addr *src, struct ul66_t *eua) {
+ eua->l=18;
+ eua->v[0]=0xf1; /* IETF */
+ eua->v[1]=0x57; /* IPv6 */
+ memcpy(&eua->v[2], src, 16); /* Copy a 16 byte address */
+ return 0;
+}
+
int pdp_euaton(struct ul66_t *eua, struct in_addr *dst) {
if((eua->l!=6) || (eua->v[0]!=0xf1) || (eua->v[1]!=0x21)) {
return EOF;
@@ -335,6 +343,15 @@
memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
return 0;
}
+
+int pdp_euaton6(struct ul66_t *eua, struct in6_addr *dst) {
+ if((eua->l!=18) || (eua->v[0]!=0xf1) || (eua->v[1]!=0x57)) {
+ return EOF;
+ }
+ memcpy(dst, &eua->v[2], 16); /* Copy a 16 byte address */
+ return 0;
+}
+
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi) {
return (imsi & 0x0fffffffffffffffull) + ((uint64_t)nsapi << 60);
@@ -349,3 +366,18 @@
}
else return EOF;
}
+
+int pdp_testip(struct ul66_t* eua) {
+ unsigned char type = eua->v[1];
+
+ switch (type) {
+ case 0x21: /* IPv4 */
+ return 4;
+ case 0x57: /* IPv6 */
+ return 6;
+ case 0x8d: /* IPv4v6 */
+ return 8; // for lack of anything better...
+ default:
+ return -1;
+ }
+}
diff --git a/gtp/pdp.h b/gtp/pdp.h
index 624c4ac..6723b05 100644
--- a/gtp/pdp.h
+++ b/gtp/pdp.h
@@ -158,9 +158,13 @@
/*struct ul16_t ggsnu; * The IP address of the GGSN currently used. (User plane) */
struct ul16_t gsnlc; /* The IP address of the local GSN. (Control plane) */
+ size_t gsnlc_len;
struct ul16_t gsnlu; /* The IP address of the local GSN. (User plane) */
+ size_t gsnlu_len;
struct ul16_t gsnrc; /* The IP address of the remote GSN. (Control plane) */
+ size_t gsnrc_len;
struct ul16_t gsnru; /* The IP address of the remote GSN. (User plane) */
+ size_t gsnru_len;
uint8_t vplmn_allow; /* Specifies whether the MS is allowed to use the APN in the domain of the HPLMN only, or additionally the APN in the domain of the VPLMN. (1 bit) */
uint8_t qos_sub0[3]; /* The quality of service profile subscribed. */
@@ -195,7 +199,7 @@
uint64_t tid; /* Combination of imsi and nsapi */
uint16_t seq; /* Sequence number of last request */
- struct sockaddr_in sa_peer; /* Address of last request */
+ struct sockaddr_storage sa_peer; /* Address of last request */
int fd; /* File descriptor request was received on */
uint8_t teic_confirmed; /* 0: Not confirmed. 1: Confirmed */
@@ -209,8 +213,8 @@
uint32_t secondary_tei[PDP_MAXNSAPI];
/* IP address used for Create and Update PDP Context Requests */
- struct in_addr hisaddr0; /* Server address */
- struct in_addr hisaddr1; /* Server address */
+ struct sockaddr_storage hisaddr0; /* Server address */
+ struct sockaddr_storage hisaddr1; /* Server address */
/* to be used by libgtp callers/users (to attach their own private state) */
void *priv;
@@ -243,8 +247,12 @@
*/
int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua);
+int pdp_ntoeua6(struct in6_addr *src, struct ul66_t *eua);
int pdp_euaton(struct ul66_t *eua, struct in_addr *dst);
+int pdp_euaton6(struct ul66_t *eua, struct in6_addr *dst);
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
int ulcpy(void* dst, void* src, size_t size);
+int pdp_testip(struct ul66_t* eua);
+
#endif /* !_PDP_H */
diff --git a/gtp/queue.c b/gtp/queue.c
index 7fdf9df..23804fb 100644
--- a/gtp/queue.c
+++ b/gtp/queue.c
@@ -57,7 +57,7 @@
}
int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg,
- struct sockaddr_in *peer, uint16_t seq) {
+ struct sockaddr *peer, uint16_t seq) {
int hash = queue_seqhash(peer, seq);
struct qmsg_t *qmsg2;
struct qmsg_t *qmsg_prev = NULL;
@@ -66,7 +66,7 @@
if (QUEUE_DEBUG) printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer), sizeof(*peer));
qmsg->seq = seq;
- memcpy(&qmsg->peer, peer, sizeof(*peer));
+ memcpy(&qmsg->peer, peer, sizeof(struct sockaddr_storage));
for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext)
qmsg_prev = qmsg2;
@@ -123,7 +123,7 @@
}
int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
- struct sockaddr_in *peer, uint16_t seq) {
+ struct sockaddr *peer, uint16_t seq) {
if (QUEUE_DEBUG) printf("queue_newmsg %d\n", (int) seq);
if (queue->qmsga[queue->next].state == 1) {
return EOF; /* Queue is full */
@@ -204,13 +204,13 @@
}
int queue_getseqx(struct queue_t *queue, struct qmsg_t **qmsg,
- struct sockaddr_in *peer, uint16_t seq) {
+ struct sockaddr *peer, uint16_t seq) {
int n;
if (QUEUE_DEBUG) printf("queue_getseq, %d\n", (int) seq);
if (QUEUE_DEBUG) queue_print(queue);
for (n=0; n<QUEUE_SIZE; n++) {
if ((queue->qmsga[n].seq == seq) &&
- (!memcmp(&queue->qmsga[n].peer, peer, sizeof(*peer)))) {
+ (!memcmp(&queue->qmsga[n].peer, peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)))) {
*qmsg = &queue->qmsga[n];
return 0;
}
@@ -219,13 +219,13 @@
}
int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
- struct sockaddr_in *peer, uint16_t seq) {
+ struct sockaddr *peer, uint16_t seq) {
int hash = queue_seqhash(peer, seq);
struct qmsg_t *qmsg2;
if (QUEUE_DEBUG) printf("Begin queue_seqget seq = %d\n", (int) seq);
for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
if ((qmsg2->seq == seq) &&
- (!memcmp(&qmsg2->peer, peer, sizeof(*peer)))) {
+ (!memcmp(&qmsg2->peer, peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)))) {
*qmsg = qmsg2;
if (QUEUE_DEBUG) printf("End queue_seqget. Found\n");
return 0;
@@ -235,7 +235,7 @@
return EOF; /* End of linked list and not found */
}
-int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
+int queue_freemsg_seq(struct queue_t *queue, struct sockaddr *peer,
uint16_t seq, uint8_t *type, void **cbp) {
struct qmsg_t *qmsg;
if (queue_seqget(queue, &qmsg, peer, seq)) {
diff --git a/gtp/queue.h b/gtp/queue.h
index 46fa22d..11978c1 100644
--- a/gtp/queue.h
+++ b/gtp/queue.h
@@ -30,7 +30,7 @@
union gtp_packet p; /* The packet stored */
int l; /* Length of the packet */
int fd; /* Socket packet was sent to / received from */
- struct sockaddr_in peer;/* Address packet was sent to / received from */
+ struct sockaddr_storage peer;/* Address packet was sent to / received from */
struct qmsg_t *seqnext; /* Pointer to next in sequence hash list */
int next; /* Pointer to the next in queue. -1: Last */
int prev; /* Pointer to the previous in queue. -1: First */
@@ -54,7 +54,7 @@
int queue_free(struct queue_t *queue);
/* Find a new queue element. Return EOF if allready full */
int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
- struct sockaddr_in *peer, uint16_t seq);
+ struct sockaddr *peer, uint16_t seq);
/* Remove an element from the queue. */
int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg);
/* Move an element to the back of the queue */
@@ -63,9 +63,9 @@
int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg);
/* Get the element with a particular sequence number */
int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
- struct sockaddr_in *peer, uint16_t seq);
+ struct sockaddr *peer, uint16_t seq);
/* Free message based on sequence number */
-int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
+int queue_freemsg_seq(struct queue_t *queue, struct sockaddr *peer,
uint16_t seq, uint8_t *type, void **cbp);
diff --git a/sgsnemu/ippool.c b/sgsnemu/ippool.c
index fa3d8af..9408d47 100644
--- a/sgsnemu/ippool.c
+++ b/sgsnemu/ippool.c
@@ -16,6 +16,7 @@
#include <syslog.h>
#include <string.h>
#include <sys/socket.h>
+#include <netdb.h>
#include <arpa/inet.h>
#include "syserr.h"
#include "ippool.h"
@@ -32,13 +33,32 @@
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 [1024];
+ char s2 [1024];
+ struct sockaddr_storage ss;
+
+ size_t addrlen = this->member[n].addrlen;
+ size_t sockaddrlen;
+
+ memcpy(((struct sockaddr*)&ss)->sa_data, &this->member[n].addr, addrlen);
+
+ if (addrlen == 4) {
+ ss.ss_family = AF_INET;
+ sockaddrlen = sizeof(struct sockaddr_in);
+ }
+ else {
+ ss.ss_family = AF_INET6;
+ sockaddrlen = sizeof(struct sockaddr_in6);
+ }
+
+ getnameinfo((struct sockaddr*)&ss, sockaddrlen, s, sizeof(s), s2, sizeof(s2), NI_NUMERICHOST);
+
+ 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;
@@ -50,12 +70,15 @@
struct ippoolm_t *p_prev = NULL;
/* Insert into hash table */
- hash = ippool_hash4(&member->addr) & this->hashmask;
+ if (member->addrlen == 4)
+ hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+ else
+ hash = ippool_hash6(&member->addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash)
p_prev = p;
if (!p_prev)
this->hash[hash] = member;
- else
+ else
p_prev->nexthash = member;
return 0; /* Always OK to insert */
}
@@ -66,7 +89,10 @@
struct ippoolm_t *p_prev = NULL;
/* Find in hash table */
- hash = ippool_hash4(&member->addr) & this->hashmask;
+ if (member->addrlen == 4)
+ hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+ else
+ hash = ippool_hash6(&member->addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
if (p == member) {
break;
@@ -93,66 +119,80 @@
return lookup((unsigned char*) &addr->s_addr, sizeof(addr->s_addr), 0);
}
-#ifndef IPPOOL_NOIP6
unsigned long int ippool_hash6(struct in6_addr *addr) {
- return lookup((unsigned char*) addr->u6_addr8, sizeof(addr->u6_addr8), 0);
+ // TODO: review hash spread for ipv6
+ return lookup((unsigned char*) addr->s6_addr32, sizeof(addr->s6_addr32), 0);
}
-#endif
-
/* Get IP address and mask */
-int ippool_aton(struct in_addr *addr, struct in_addr *mask,
- char *pool, int number) {
+int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t* prefixlen, char *pool, int number) {
+ struct addrinfo* ai;
- /* Parse only first instance of network for now */
- /* Eventually "number" will indicate the token which we want to parse */
+ struct addrinfo hints;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0;
- unsigned int a1, a2, a3, a4;
- unsigned int m1, m2, m3, m4;
- int c;
- int m;
- int masklog;
+ int err;
- 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
- return -1; /* Wrong mask format */
- }
- m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4;
- for (masklog = 0; ((1 << masklog) < ((~m)+1)); masklog++);
- if (((~m)+1) != (1 << masklog)) {
- sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
- return -1; /* Wrong mask format (not all ones followed by all zeros)*/
- }
- mask->s_addr = htonl(m);
- break;
- default:
- sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
- return -1; /* Invalid mask */
- }
+ // Find '/'
+ char* prefixlen_str = strchr(pool, '/');
+ if (prefixlen_str) {
+ *prefixlen_str = '\0';
+ prefixlen_str++;
+ if (*prefixlen_str == '\0') {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Empty prefix length specified");
+ return -1;
+ }
+ }
- if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) {
- sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Wrong IP address format");
- return -1;
- }
- else
- addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
+ // Convert address
+ if ((err = getaddrinfo(pool, NULL, &hints, &ai))) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Bad address");
+ return -1;
+ }
- return 0;
+ // Copy address, set lengths
+ if (ai->ai_family == AF_INET) {
+ *prefixlen = 32;
+ *addrlen = sizeof(struct in_addr);
+ memcpy(addr, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, sizeof(struct in_addr));
+ }
+ else {
+ *prefixlen = 128;
+ *addrlen = sizeof(struct in6_addr);
+ memcpy(addr, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, sizeof(struct in6_addr));
+ }
+
+ freeaddrinfo(ai);
+
+ // Parse prefixlen
+ if (prefixlen_str) {
+ char* e;
+ *prefixlen = strtol(prefixlen_str, &e, 10);
+ if (*e != '\0') {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen is not an int");
+ return -1;
+ }
+ }
+
+ if (*prefixlen > (*addrlen * 8)) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen too big");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Increases ip4/6 address by 1 */
+void inc_addr(struct in_addr* addr, size_t addrlen)
+{
+ uint8_t* a = (uint8_t*)&addr->s_addr;
+ for (; addrlen > 0; addrlen--) {
+ if (++a[addrlen-1])
+ break;
+ }
}
/* Create new address pool */
@@ -162,10 +202,12 @@
/* 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;
+ struct in6_addr addr;
+ size_t addrlen;
+ size_t addrprefixlen;
+ struct in6_addr stataddr;
+ size_t stataddrlen;
+ size_t stataddrprefixlen;
unsigned int m;
int listsize;
int dynsize;
@@ -175,7 +217,7 @@
dynsize = 0;
}
else {
- if (ippool_aton(&addr, &mask, dyn, 0)) {
+ if (ippool_aton((struct in_addr*)&addr, &addrlen, &addrprefixlen, dyn, 0)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to parse dynamic pool");
return -1;
@@ -185,9 +227,11 @@
if (flags & IPPOOL_NOGATEWAY) {
flags |= IPPOOL_NONETWORK;
}
-
- m = ntohl(mask.s_addr);
- dynsize = ((~m)+1);
+
+ dynsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
+
+ //m = ntohl(mask.s_addr);
+ //dynsize = ((~m)+1);
if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
dynsize--;
if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */
@@ -198,18 +242,17 @@
if (!allowstat) {
statsize = 0;
- stataddr.s_addr = 0;
- statmask.s_addr = 0;
+ stataddrlen = 0;
+ stataddrprefixlen = 0;
}
else {
- if (ippool_aton(&stataddr, &statmask, stat, 0)) {
+ if (ippool_aton((struct in_addr*)&stataddr, &stataddrlen, &stataddrprefixlen, stat, 0)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to parse static range");
return -1;
}
- m = ntohl(statmask.s_addr);
- statsize = ((~m)+1);
+ statsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE;
}
@@ -223,8 +266,10 @@
(*this)->allowdyn = allowdyn;
(*this)->allowstat = allowstat;
- (*this)->stataddr = stataddr;
- (*this)->statmask = statmask;
+ if (stataddrlen > 0)
+ memcpy(&(*this)->stataddr, &stataddr, stataddrlen);
+ (*this)->stataddrlen = stataddrlen;
+ (*this)->stataddrprefixlen = stataddrprefixlen;
(*this)->listsize += listsize;
if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){
@@ -252,15 +297,20 @@
(*this)->firstdyn = NULL;
(*this)->lastdyn = NULL;
+
+ if (flags & IPPOOL_NOGATEWAY) {
+ inc_addr((struct in_addr*)&addr, addrlen);
+ inc_addr((struct in_addr*)&addr, addrlen);
+ }
+ else if (flags & IPPOOL_NONETWORK)
+ inc_addr((struct in_addr*)&addr, addrlen);
+
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);
-
+ memcpy(&(*this)->member[i].addr, &addr, addrlen);
+ (*this)->member[i].addrlen = addrlen;
+ inc_addr((struct in_addr*)&addr, addrlen);
+
(*this)->member[i].inuse = 0;
/* Insert into list of unused */
@@ -280,8 +330,10 @@
(*this)->firststat = NULL;
(*this)->laststat = NULL;
for (i = dynsize; i<listsize; i++) {
+ // TODO: set addrlen to 0?
+ memset(&(*this)->member[i].addr, 0, addrlen);
+ (*this)->member[i].addrlen = addrlen;
- (*this)->member[i].addr.s_addr = 0;
(*this)->member[i].inuse = 0;
/* Insert into list of unused */
@@ -312,14 +364,19 @@
/* Find an IP address in the pool */
int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr) {
+ struct in_addr *addr, size_t addrlen) {
struct ippoolm_t *p;
uint32_t hash;
/* Find in hash table */
- hash = ippool_hash4(addr) & this->hashmask;
+ if (addrlen == 4)
+ hash = ippool_hash4(addr) & this->hashmask;
+ else
+ hash = ippool_hash6(addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
- if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
+ if (p->addrlen == addrlen
+ && memcmp(&p->addr, addr, addrlen) == 0
+ && (p->inuse)) {
if (member) *member = p;
return 0;
}
@@ -337,7 +394,7 @@
* address space.
**/
int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr, int statip) {
+ struct in_addr *addr, size_t addrlen, int statip) {
struct ippoolm_t *p;
struct ippoolm_t *p2 = NULL;
uint32_t hash;
@@ -357,15 +414,25 @@
if (0) (void)ippool_printaddr(this);
/* First check to see if this type of address is allowed */
- if ((addr) && (addr->s_addr) && statip) { /* IP address given */
+ int specified = 0;
+ if (addr) {
+ if (addrlen == 4 && ((struct in_addr*)&addr)->s_addr)
+ specified = 1;
+ if (addrlen == 16 && !IN6_IS_ADDR_UNSPECIFIED(addr))
+ specified = 1;
+ }
+ if (specified && statip) { /* IP address given */
if (!this->allowstat) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static IP address not allowed");
return -1;
}
+ // TODO: ipv6 mask check
+/*
if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static out of range");
return -1;
}
+*/
}
else {
if (!this->allowdyn) {
@@ -376,11 +443,14 @@
}
/* 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;
+ if (addrlen == 4)
+ hash = ippool_hash4(addr) & this->hashmask;
+ else
+ hash = ippool_hash6(addr) & this->hashmask;
for (p = this->hash[hash]; p; p = p->nexthash) {
- if ((p->addr.s_addr == addr->s_addr)) {
+ if (p->addrlen == addrlen && !memcmp(&p->addr, &addr, addrlen)) {
p2 = p;
break;
}
@@ -431,7 +501,7 @@
/* 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(LOG_ERR, __FILE__, __LINE__, 0,
"No more IP addresses available");
@@ -452,7 +522,7 @@
p2->next = NULL;
p2->prev = NULL;
p2->inuse = 2; /* Static address in use */
- memcpy(&p2->addr, addr, sizeof(addr));
+ memcpy(&p2->addr, addr, addrlen);
*member = p2;
(void)ippool_hashadd(this, *member);
if (0) (void)ippool_printaddr(this);
@@ -507,7 +577,8 @@
this->laststat = member;
member->inuse = 0;
- member->addr.s_addr = 0;
+ memset(&member->addr, 0, sizeof(member->addr));
+ member->addrlen = 0;
member->peer = NULL;
member->nexthash = NULL;
if (0) (void)ippool_printaddr(this);
@@ -519,8 +590,3 @@
}
-#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/sgsnemu/ippool.h b/sgsnemu/ippool.h
index 02691a6..432fa8d 100644
--- a/sgsnemu/ippool.h
+++ b/sgsnemu/ippool.h
@@ -26,8 +26,6 @@
in RFC2373.
*/
-#define IPPOOL_NOIP6
-
#define IPPOOL_NONETWORK 0x01
#define IPPOOL_NOBROADCAST 0x02
#define IPPOOL_NOGATEWAY 0x04
@@ -40,7 +38,9 @@
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 in6_addr stataddr; /* Static address range network address */
+ size_t stataddrlen;
+ size_t stataddrprefixlen;
struct in_addr statmask; /* Static address range network mask */
struct ippoolm_t *member; /* Listsize array of members */
unsigned int hashsize; /* Size of hash table */
@@ -54,11 +54,8 @@
};
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
+ size_t addrlen;
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 */
@@ -81,25 +78,22 @@
/* Find an IP address in the pool */
extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
- struct in_addr *addr);
+ struct in_addr *addr, size_t addrlen);
/* 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 in_addr *addr, size_t addrlen, 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,
+extern int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t *prefixlen,
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
+
+void inc_addr(struct in_addr* addr, size_t addrlen);
#endif /* !_IPPOOL_H */
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 9b5c2e2..42350e5 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -59,7 +59,8 @@
uint8_t inuse; /* 0=free. 1=used by somebody */
struct iphash_t *ipnext;
struct pdp_t *pdp;
- struct in_addr addr;
+ struct in6_addr addr;
+ size_t addrlen;
};
struct iphash_t iparr[MAXCONTEXTS];
struct iphash_t *iphash[MAXCONTEXTS];
@@ -82,17 +83,20 @@
struct {
int debug; /* Print debug messages */
int createif; /* Create local network interface */
- struct in_addr netaddr, destaddr, net, mask; /* Network interface */
+ struct sockaddr_storage netaddr;
+ struct sockaddr_storage destaddr;
+ struct sockaddr_storage net;
+ size_t net_prefixlen;
char *ipup, *ipdown; /* Filename of scripts */
int defaultroute; /* Set up default route */
- struct in_addr pinghost; /* Remote ping host */
+ struct sockaddr_storage pinghost; /* Remote ping host */
int pingrate;
int pingsize;
int pingcount;
int pingquiet;
- struct in_addr listen;
- struct in_addr remote;
- struct in_addr dns;
+ struct sockaddr_storage listen;
+ struct sockaddr_storage remote;
+ struct sockaddr_storage dns;
int contexts; /* Number of contexts to create */
int timelimit; /* Number of seconds to be connected */
char *statedir;
@@ -145,12 +149,17 @@
int pingseq = 0; /* Ping sequence counter */
struct timeval firstping;
-int ipset(struct iphash_t *ipaddr, struct in_addr *addr) {
- int hash = ippool_hash4(addr) % MAXCONTEXTS;
+int ipset(struct iphash_t *ipaddr, struct in_addr *addr, size_t addrlen) {
+ int hash;
+ if (addrlen == 4)
+ hash = ippool_hash4(addr) % MAXCONTEXTS;
+ else
+ hash = ippool_hash6((struct in6_addr*)addr) % MAXCONTEXTS;
struct iphash_t *h;
struct iphash_t *prev = NULL;
ipaddr->ipnext = NULL;
- ipaddr->addr.s_addr = addr->s_addr;
+ memcpy(&ipaddr->addr, addr, addrlen);
+ ipaddr->addrlen = addrlen;
for (h = iphash[hash]; h; h = h->ipnext)
prev = h;
if (!prev)
@@ -161,7 +170,11 @@
}
int ipdel(struct iphash_t *ipaddr) {
- int hash = ippool_hash4(&ipaddr->addr) % MAXCONTEXTS;
+ int hash;
+ if (ipaddr->addrlen == 4)
+ hash = ippool_hash4((struct in_addr*)&ipaddr->addr) % MAXCONTEXTS;
+ else
+ hash = ippool_hash6(&ipaddr->addr) % MAXCONTEXTS;
struct iphash_t *h;
struct iphash_t *prev = NULL;
for (h = iphash[hash]; h; h = h->ipnext) {
@@ -177,18 +190,21 @@
return EOF; /* End of linked list and not found */
}
-int ipget(struct iphash_t **ipaddr, struct in_addr *addr) {
- int hash = ippool_hash4(addr) % MAXCONTEXTS;
+int ipget(struct iphash_t **ipaddr, struct in_addr *addr, size_t addrlen) {
+ int hash;
+ if (addrlen == 4)
+ hash = ippool_hash4(addr) % MAXCONTEXTS;
+ else
+ hash = ippool_hash6((struct in6_addr*)addr) % MAXCONTEXTS;
struct iphash_t *h;
for (h = iphash[hash]; h; h = h->ipnext) {
- if ((h->addr.s_addr == addr->s_addr)) {
+ if (h->addrlen == addrlen && memcmp(&h->addr, addr, addrlen) == 0) {
*ipaddr = h;
return 0;
}
}
return EOF; /* End of linked list and not found */
}
-
/* Used to write process ID to file. Assume someone else will delete */
void log_pid(char *pidfile) {
@@ -209,8 +225,16 @@
/* gengeopt declarations */
struct gengetopt_args_info args_info;
- struct hostent *host;
+ struct addrinfo* ai;
unsigned int n;
+
+ struct addrinfo hints;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0;
+
+ int err;
if (cmdline_parser (argc, argv, &args_info) != 0)
return -1;
@@ -306,21 +330,26 @@
/* Do hostname lookup to translate hostname to IP address */
printf("\n");
if (args_info.dns_arg) {
- if (!(host = gethostbyname(args_info.dns_arg))) {
+ if ((err = getaddrinfo(args_info.dns_arg, NULL, &hints, &ai))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
- "Invalid DNS address: %s!", args_info.dns_arg);
+ "Invalid DNS address: %s! %s %i", args_info.dns_arg, gai_strerror(err), err);
return -1;
}
else {
- memcpy(&options.dns.s_addr, host->h_addr, host->h_length);
- _res.nscount = 1;
- _res.nsaddr_list[0].sin_addr = options.dns;
- printf("Using DNS server: %s (%s)\n",
- args_info.dns_arg, inet_ntoa(options.dns));
+ memcpy(&options.dns, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+
+ // TODO: ipv6
+ if (options.dns.ss_family == AF_INET) {
+ _res.nscount = 1;
+ _res.nsaddr_list[0].sin_addr = ((struct sockaddr_in*)&options.dns)->sin_addr;
+ //printf("Using DNS server: %s (%s)\n",
+ //args_info.dns_arg, inet_ntoa(options.dns));
+ }
}
}
else {
- options.dns.s_addr= 0;
+ options.dns.ss_family = 0; // Indicates not specified
printf("Using default DNS server\n");
}
@@ -328,15 +357,17 @@
/* If no listen option is specified listen to any local port */
/* Do hostname lookup to translate hostname to IP address */
if (args_info.listen_arg) {
- if (!(host = gethostbyname(args_info.listen_arg))) {
+ if ((err = getaddrinfo(args_info.listen_arg, NULL, &hints, &ai))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
- "Invalid listening address: %s!", args_info.listen_arg);
+ "Invalid listening address: %s! %s %i", args_info.listen_arg, gai_strerror(err), err);
return -1;
}
else {
- memcpy(&options.listen.s_addr, host->h_addr, host->h_length);
- printf("Local IP address is: %s (%s)\n",
- args_info.listen_arg, inet_ntoa(options.listen));
+ memcpy(&options.listen, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+ // TODO: ipv6
+ //printf("Local IP address is: %s (%s)\n",
+ //args_info.listen_arg, inet_ntoa(options.listen));
}
}
else {
@@ -350,15 +381,17 @@
/* If no remote option is specified terminate */
/* Do hostname lookup to translate hostname to IP address */
if (args_info.remote_arg) {
- if (!(host = gethostbyname(args_info.remote_arg))) {
+ if ((err = getaddrinfo(args_info.remote_arg, NULL, &hints, &ai))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
- "Invalid remote address: %s!", args_info.remote_arg);
+ "Invalid remote address: %s! %s %i", args_info.remote_arg, gai_strerror(err), err);
return -1;
}
else {
- memcpy(&options.remote.s_addr, host->h_addr, host->h_length);
- printf("Remote IP address is: %s (%s)\n",
- args_info.remote_arg, inet_ntoa(options.remote));
+ memcpy(&options.remote, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+ // TODO: ipv6
+ //printf("Remote IP address is: %s (%s)\n",
+ //args_info.remote_arg, inet_ntoa(options.remote));
}
}
else {
@@ -498,26 +531,31 @@
/* net */
/* Store net as in_addr net and mask */
if (args_info.net_arg) {
- if(ippool_aton(&options.net, &options.mask, args_info.net_arg, 0)) {
+ size_t addrlen;
+ if(ippool_aton(&(((struct sockaddr_in*)&options.net)->sin_addr), &addrlen, &options.net_prefixlen, args_info.net_arg, 0)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Invalid network address: %s!", args_info.net_arg);
exit(1);
}
+ if (addrlen == 4)
+ options.net.ss_family = AF_INET;
+ else
+ options.net.ss_family = AF_INET6;
#if defined (__sun__)
options.netaddr.s_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;
- options.destaddr.s_addr = options.net.s_addr;
+ memcpy(&options.netaddr, &options.net, sizeof(options.net));
+ memcpy(&options.destaddr, &options.net, sizeof(options.net));
#endif
}
else {
- options.net.s_addr = 0;
- options.mask.s_addr = 0;
- options.netaddr.s_addr = 0;
- options.destaddr.s_addr = 0;
+ options.net.ss_family = 0;
+ options.net_prefixlen = 0;
+ options.netaddr.ss_family = 0;
+ options.destaddr.ss_family = 0;
}
/* ipup */
@@ -536,16 +574,21 @@
/* pinghost */
/* Store ping host as in_addr */
if (args_info.pinghost_arg) {
- if (!(host = gethostbyname(args_info.pinghost_arg))) {
+ if ((err = getaddrinfo(args_info.pinghost_arg, NULL, &hints, &ai))) {
+ //if (!(host = gethostbyname(args_info.pinghost_arg))) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
- "Invalid ping host: %s!", args_info.pinghost_arg);
+ "Invalid ping host: %s! %s %i", args_info.pinghost_arg, gai_strerror(err), err);
return -1;
}
else {
- memcpy(&options.pinghost.s_addr, host->h_addr, host->h_length);
- printf("Using ping host: %s (%s)\n",
- args_info.pinghost_arg, inet_ntoa(options.pinghost));
+ memcpy(&options.pinghost, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+ //printf("Using ping host: %s (%s)\n",
+// args_info.pinghost_arg, inet_ntoa(options.pinghost));
}
+ }
+ else {
+ options.pinghost.ss_family = 0; // Indicates not specified
}
/* Other ping parameters */
@@ -687,7 +730,7 @@
struct timezone tz;
struct timeval tv;
int diff;
- if ((options.pinghost.s_addr) && (2 == state) &&
+ if ((options.pinghost.ss_family) && (2 == state) &&
((pingseq < options.pingcount) || (options.pingcount == 0))) {
gettimeofday(&tv, &tz);
diff = 1000000 / options.pingrate * pingseq -
@@ -715,7 +758,8 @@
elapsed = 1000000 * (tv.tv_sec - firstping.tv_sec) +
(tv.tv_usec - firstping.tv_usec); /* Microseconds */
printf("\n");
- printf("\n----%s PING Statistics----\n", inet_ntoa(options.pinghost));
+ //TODO: ipv6
+ //printf("\n----%s PING Statistics----\n", inet_ntoa(options.pinghost));
printf("%d packets transmitted in %.3f seconds, ", ntransmitted,
elapsed / 1000000.0);
printf("%d packets received, ", nreceived );
@@ -899,12 +943,21 @@
/* Callback for receiving messages from tun */
int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) {
struct iphash_t *ipm;
- struct in_addr src;
+ struct in6_addr src;
+ size_t srclen;
struct tun_packet_t *iph = (struct tun_packet_t*) pack;
+ struct tun_packet6_t *ip6h = (struct tun_packet6_t*) pack;
- src.s_addr = iph->src;
+ if (iph->ver == 4) {
+ memcpy(&src, &iph->src, sizeof(struct in_addr));
+ srclen = 4;
+ }
+ else {
+ memcpy(&src, ip6h->src, sizeof(struct in6_addr));
+ srclen = 16;
+ }
- if (ipget(&ipm, &src)) {
+ if (ipget(&ipm, (struct in_addr*)&src, srclen)) {
printf("Received packet without a valid source address!!!\n");
return 0;
}
@@ -916,6 +969,8 @@
int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) {
struct in_addr addr;
+ struct in6_addr addr6;
+ int ip6 = 0;
struct iphash_t *iph = (struct iphash_t*) cbp;
@@ -944,17 +999,20 @@
}
if (pdp_euaton(&pdp->eua, &addr)) {
- printf("Received create PDP context response. Cause value: %d\n", cause);
- pdp_freepdp(iph->pdp);
- iph->pdp = NULL;
- state = 0;
- return EOF; /* Not a valid IP address */
+ if (pdp_euaton6(&pdp->eua, &addr6)) {
+ printf("Received create PDP context response. Cause value: %d\n", cause);
+ pdp_freepdp(iph->pdp);
+ iph->pdp = NULL;
+ state = 0;
+ return EOF; /* Not a valid IP address */
+ }
+ ip6 = 1;
}
printf("Received create PDP context response. IP address: %s\n",
inet_ntoa(addr));
- if ((options.createif) && (!options.net.s_addr)) {
+ if ((options.createif) && (!options.net.ss_family)) {
struct in_addr m;
#ifdef HAVE_INET_ATON
inet_aton("255.255.255.255", &m);
@@ -962,7 +1020,10 @@
m.s_addr = -1;
#endif
/* printf("Setting up interface and routing\n");*/
- tun_addaddr(tun, &addr, &addr, &m);
+ if (ip6)
+ tun_addaddr6(tun, &addr6, &addr6, 128);
+ else
+ tun_addaddr(tun, &addr, &addr, &m);
if (options.defaultroute) {
struct in_addr rm;
rm.s_addr = 0;
@@ -971,7 +1032,10 @@
if (options.ipup) tun_runscript(tun, options.ipup);
}
- ipset((struct iphash_t*) pdp->peer, &addr);
+ if (ip6)
+ ipset((struct iphash_t*) pdp->peer, (struct in_addr*)&addr6, sizeof(addr6));
+ else
+ ipset((struct iphash_t*) pdp->peer, &addr, sizeof(addr));
state = 2; /* Connected */
@@ -990,7 +1054,7 @@
if (echoversion == 1) {
printf("Retrying with version 0\n");
echoversion = 0;
- gtp_echo_req(gsn, echoversion, NULL, &options.remote);
+ gtp_echo_req(gsn, echoversion, NULL, (struct sockaddr*)&options.remote);
return 0;
}
else {
@@ -1056,7 +1120,7 @@
exit(1);
printf("\nInitialising GTP library\n");
- if (gtp_new(&gsn, options.statedir, &options.listen, GTP_MODE_SGSN)) {
+ if (gtp_new(&gsn, options.statedir, (struct sockaddr*)&options.listen, GTP_MODE_SGSN)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"Failed to create gtp");
exit(1);
@@ -1084,13 +1148,23 @@
if (tun->fd > maxfd) maxfd = tun->fd;
}
- if ((options.createif) && (options.net.s_addr)) {
+ if ((options.createif) && (options.net.ss_family)) {
/* printf("Setting up interface and routing\n");*/
- tun_addaddr(tun, &options.netaddr, &options.destaddr, &options.mask);
+ if (options.net.ss_family == AF_INET) {
+ struct in_addr mask;
+ mask.s_addr = 0xFFFFFFFF << (32-options.net_prefixlen);
+ tun_addaddr(tun, &((struct sockaddr_in*)&options.netaddr)->sin_addr, &((struct sockaddr_in*)&options.destaddr)->sin_addr, &mask);
+ }
+ else {
+ tun_addaddr6(tun, &((struct sockaddr_in6*)&options.netaddr)->sin6_addr, &((struct sockaddr_in6*)&options.destaddr)->sin6_addr, options.net_prefixlen);
+ }
if (options.defaultroute) {
- struct in_addr rm;
- rm.s_addr = 0;
- tun_addroute(tun, &rm, &options.destaddr, &rm);
+ if (options.destaddr.ss_family == AF_INET) {
+ struct in_addr rm;
+ rm.s_addr = 0;
+ tun_addroute(tun, &rm, &((struct sockaddr_in*)&options.destaddr)->sin_addr, &rm);
+ }
+ // TODO: IPv6 default route
}
if (options.ipup) tun_runscript(tun, options.ipup);
}
@@ -1105,7 +1179,7 @@
/* See if anybody is there */
printf("Sending off echo request\n");
echoversion = options.gtpversion;
- gtp_echo_req(gsn, echoversion, NULL, &options.remote); /* Is remote alive? */
+ gtp_echo_req(gsn, echoversion, NULL, (struct sockaddr*)&options.remote); /* Is remote alive? */
for(n=0; n<options.contexts; n++) {
uint64_t myimsi;
@@ -1147,10 +1221,20 @@
memcpy(pdp->apn_use.v, options.apn.v, options.apn.l);
}
- pdp->gsnlc.l = sizeof(options.listen);
- memcpy(pdp->gsnlc.v, &options.listen, sizeof(options.listen));
- pdp->gsnlu.l = sizeof(options.listen);
- memcpy(pdp->gsnlu.v, &options.listen, sizeof(options.listen));
+ if (options.listen.ss_family == AF_INET) {
+ struct sockaddr_in* s = (struct sockaddr_in*)&options.listen;
+ memcpy(pdp->gsnlc.v, &s->sin_addr, sizeof(s->sin_addr));
+ memcpy(pdp->gsnlu.v, &s->sin_addr, sizeof(s->sin_addr));
+ pdp->gsnlc.l = sizeof(s->sin_addr);
+ pdp->gsnlu.l = sizeof(s->sin_addr);
+ }
+ else {
+ struct sockaddr_in6* s = (struct sockaddr_in6*)&options.listen;
+ memcpy(pdp->gsnlc.v, &s->sin6_addr, sizeof(s->sin6_addr));
+ memcpy(pdp->gsnlu.v, &s->sin6_addr, sizeof(s->sin6_addr));
+ pdp->gsnlc.l = sizeof(s->sin6_addr);
+ pdp->gsnlu.l = sizeof(s->sin6_addr);
+ }
if (options.msisdn.l > sizeof(pdp->msisdn.v)) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0, "MSISDN length too big");
@@ -1173,8 +1257,9 @@
pdp->version = options.gtpversion;
- pdp->hisaddr0 = options.remote;
- pdp->hisaddr1 = options.remote;
+ memcpy(&pdp->hisaddr0, &options.remote, options.remote.ss_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
+ memcpy(&pdp->hisaddr1, &options.remote, options.remote.ss_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
+
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
512 = Flat rate, 256 = Hot billing */
@@ -1231,7 +1316,7 @@
/* Delete context */
printf("Disconnecting PDP context #%d\n", n);
gtp_delete_context_req(gsn, iparr[n].pdp, NULL, 1);
- if ((options.pinghost.s_addr !=0) && ntransmitted) ping_finish();
+ if ((options.pinghost.ss_family !=0) && ntransmitted) ping_finish();
}
}
@@ -1239,7 +1324,7 @@
diff = 0;
while (( diff<=0 ) &&
/* Send off an ICMP ping packet */
- /*if (*/(options.pinghost.s_addr) && (2 == state) &&
+ /*if (*/(options.pinghost.ss_family) && (2 == state) &&
((pingseq < options.pingcount) || (options.pingcount == 0))) {
if (!pingseq) gettimeofday(&firstping, &tz); /* Set time of first ping */
gettimeofday(&tv, &tz);
@@ -1249,7 +1334,7 @@
if (diff <=0) {
if (options.debug) printf("Create_ping %d\n", diff);
create_ping(gsn, iparr[pingseq % options.contexts].pdp,
- &options.pinghost, pingseq, options.pingsize);
+ &((struct sockaddr_in*)&options.pinghost)->sin_addr, pingseq, options.pingsize);
pingseq++;
}
}
diff --git a/sgsnemu/tun.c b/sgsnemu/tun.c
index 1cc706b..2bb852f 100644
--- a/sgsnemu/tun.c
+++ b/sgsnemu/tun.c
@@ -71,6 +71,13 @@
#if defined(__linux__)
+// From linux/ipv6.h
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+
int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
{
int len = RTA_LENGTH(dlen);
@@ -397,6 +404,174 @@
}
+int tun_addaddr6(struct tun_t *this,
+ struct in6_addr *addr,
+ struct in6_addr *dstaddr,
+ size_t prefixlen) {
+
+#if defined(__linux__)
+ struct {
+ struct nlmsghdr n;
+ struct ifaddrmsg i;
+ char buf[TUN_NLBUFSIZE];
+ } req;
+
+ struct sockaddr_nl local;
+ socklen_t addr_len;
+ int fd;
+ int status;
+
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg;
+
+ if (!this->addrs) /* Use ioctl for first addr to make ping work */
+ return tun_setaddr6(this, addr, dstaddr, prefixlen);
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
+ req.n.nlmsg_type = RTM_NEWADDR;
+ req.i.ifa_family = AF_INET6;
+ req.i.ifa_prefixlen = 128; /* 128 FOR IPv6 */
+ req.i.ifa_flags = 0;
+ req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
+ if (tun_gifindex(this, &req.i.ifa_index)) {
+ return -1;
+ }
+
+ tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
+ tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
+
+ if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "socket() failed");
+ return -1;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.nl_family = AF_NETLINK;
+ local.nl_groups = 0;
+
+ if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "bind() failed");
+ close(fd);
+ return -1;
+ }
+
+ addr_len = sizeof(local);
+ if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "getsockname() failed");
+ close(fd);
+ return -1;
+ }
+
+ if (addr_len != sizeof(local)) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "Wrong address length %d", addr_len);
+ close(fd);
+ return -1;
+ }
+
+ if (local.nl_family != AF_NETLINK) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "Wrong address family %d", local.nl_family);
+ close(fd);
+ return -1;
+ }
+
+ iov.iov_base = (void*)&req.n;
+ iov.iov_len = req.n.nlmsg_len;
+
+ msg.msg_name = (void*)&nladdr;
+ msg.msg_namelen = sizeof(nladdr),
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ req.n.nlmsg_seq = 0;
+ req.n.nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(fd, &msg, 0); /* TODO Error check */
+
+ tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
+ close(fd);
+ this->addrs++;
+ return 0;
+
+
+
+#elif defined (__FreeBSD__) || defined (__APPLE__)
+
+ int fd;
+ struct ifaliasreq areq;
+
+ /* TODO: Is this needed on FreeBSD? */
+ if (!this->addrs) /* Use ioctl for first addr to make ping work */
+ return tun_setaddr(this, addr, dstaddr, netmask); /* TODO dstaddr */
+
+ memset(&areq, 0, sizeof(areq));
+
+ /* Set up interface name */
+ strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
+ areq.ifra_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+
+ ((struct sockaddr_in*) &areq.ifra_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in*) &areq.ifra_addr)->sin_len = sizeof(areq.ifra_addr);
+ ((struct sockaddr_in*) &areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
+
+ ((struct sockaddr_in*) &areq.ifra_mask)->sin_family = AF_INET;
+ ((struct sockaddr_in*) &areq.ifra_mask)->sin_len = sizeof(areq.ifra_mask);
+ ((struct sockaddr_in*) &areq.ifra_mask)->sin_addr.s_addr = netmask->s_addr;
+
+ /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
+ ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_family = AF_INET;
+ ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_len =
+ sizeof(areq.ifra_broadaddr);
+ ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_addr.s_addr =
+ dstaddr->s_addr;
+
+ /* Create a channel to the NET kernel. */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "socket() failed");
+ return -1;
+ }
+
+ if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "ioctl(SIOCAIFADDR) failed");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ this->addrs++;
+ return 0;
+
+#elif defined (__sun__)
+
+ if (!this->addrs) /* Use ioctl for first addr to make ping work */
+ return tun_setaddr(this, addr, dstaddr, netmask);
+
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "Setting multiple addresses not possible on Solaris");
+ return -1;
+
+#else
+#error "Unknown platform!"
+#endif
+}
+
int tun_setaddr(struct tun_t *this,
struct in_addr *addr,
@@ -431,7 +606,7 @@
}
if (addr) { /* Set the interface address */
- this->addr.s_addr = addr->s_addr;
+ ((struct in_addr*)&this->addr)->s_addr = addr->s_addr;
((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = addr->s_addr;
if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
if (errno != EEXIST) {
@@ -448,7 +623,7 @@
}
if (dstaddr) { /* Set the destination address */
- this->dstaddr.s_addr = dstaddr->s_addr;
+ ((struct in_addr*)&this->dstaddr)->s_addr = dstaddr->s_addr;
((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr =
dstaddr->s_addr;
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
@@ -487,6 +662,87 @@
close(fd);
this->addrs++;
+ /* On linux the route to the interface is set automatically
+ on FreeBSD we have to do this manually */
+
+ /* TODO: How does it work on Solaris? */
+
+ tun_sifflags(this, IFF_UP | IFF_RUNNING);
+
+#if defined(__FreeBSD__) || defined (__APPLE__)
+ tun_addroute(this, dstaddr, addr, netmask);
+ this->routes = 1;
+#endif
+
+ return 0;
+}
+
+int tun_setaddr6(struct tun_t *this,
+ struct in6_addr *addr,
+ struct in6_addr *dstaddr,
+ size_t prefixlen)
+{
+ struct in6_ifreq ifr;
+ struct ifreq namereq;
+ int fd;
+
+ this->ip6 = 1;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ memset (&namereq, '\0', sizeof (namereq));
+
+ /* Create a channel to the NET kernel. */
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "socket() failed");
+ return -1;
+ }
+
+ // Get interface index
+ strncpy(namereq.ifr_name, this->devname, IFNAMSIZ);
+ namereq.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+ if (ioctl(fd, SIOGIFINDEX, &namereq) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Could not get ifindex");
+ return -1;
+ }
+ ifr.ifr6_ifindex = namereq.ifr_ifindex;
+
+ ifr.ifr6_prefixlen = prefixlen;
+
+ if (addr) { /* Set the interface address */
+ memcpy(&this->addr, addr, sizeof(struct in6_addr));
+ memcpy(&ifr.ifr6_addr, addr, sizeof(struct in6_addr));
+ if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
+ if (errno != EEXIST) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "ioctl(SIOCSIFADDR) failed");
+ }
+ else {
+ sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
+ "ioctl(SIOCSIFADDR): Address already exists");
+ }
+ close(fd);
+ return -1;
+ }
+ }
+
+#ifdef xxx
+ // Looks like this is not possible/necessary for ipv6?
+ if (dstaddr) { /* Set the destination address */
+ memcpy(&this->dstaddr, dstaddr, sizeof(struct in6_addr));
+ memcpy(&ifr.ifr6_addr, dstaddr, sizeof(struct in6_addr));
+ if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+ "ioctl(SIOCSIFDSTADDR) failed");
+ close(fd);
+ return -1;
+ }
+ }
+#endif //xxx
+
+ close(fd);
+ this->addrs++;
+
/* On linux the route to the interface is set automatically
on FreeBSD we have to do this manually */
@@ -883,9 +1139,10 @@
char snet[TUN_ADDRSIZE];
char smask[TUN_ADDRSIZE];
- strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
+ //TODO: fix for IPv6
+ //strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
snet[sizeof(snet)-1] = 0;
- strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
+ //strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
smask[sizeof(smask)-1] = 0;
/* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
diff --git a/sgsnemu/tun.h b/sgsnemu/tun.h
index 7972c53..a75cc43 100644
--- a/sgsnemu/tun.h
+++ b/sgsnemu/tun.h
@@ -17,9 +17,18 @@
#define TUN_ADDRSIZE 128
#define TUN_NLBUFSIZE 1024
+/* This should not be re-declared! Why not just use netinet/ip(6).h?
+ * It is not completely functional as it is here. Just the minimum
+ * amount of hacking has been done to make it work. */
struct tun_packet_t {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int ihl:4;
+ unsigned int ver:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
unsigned int ver:4;
unsigned int ihl:4;
+#endif
unsigned int dscp:6;
unsigned int ecn:2;
unsigned int length:16;
@@ -29,8 +38,19 @@
unsigned int ttl:8;
unsigned int protocol:8;
unsigned int check:16;
- unsigned int src:32;
- unsigned int dst:32;
+ uint32_t src;
+ uint32_t dst;
+};
+
+struct tun_packet6_t {
+ unsigned int ver:4;
+ unsigned int tclass:8;
+ unsigned int flowlabel:20;
+ unsigned int payloadlen:16;
+ unsigned int nexthdr:8;
+ unsigned int hoplim:8;
+ unsigned char src[16];
+ unsigned char dst[16];
};
@@ -39,10 +59,12 @@
*************************************************************/
struct tun_t {
+ int ip6;
int fd; /* File descriptor to tun interface */
- struct in_addr addr;
- struct in_addr dstaddr;
- struct in_addr netmask;
+ struct in6_addr addr;
+ struct in6_addr dstaddr;
+ struct in_addr netmask; //ipv4
+ size_t prefixlen;
int addrs; /* Number of allocated IP addresses */
int routes; /* One if we allocated an automatic route */
char devname[IFNAMSIZ];/* Name of the tun device */
@@ -57,10 +79,13 @@
extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask);
-
+extern int tun_addaddr6(struct tun_t *this, struct in6_addr *addr,
+ struct in6_addr *dstaddr, size_t prefixlen);
extern int tun_setaddr(struct tun_t *this, struct in_addr *our_adr,
struct in_addr *his_adr, struct in_addr *net_mask);
+extern int tun_setaddr6(struct tun_t *this, struct in6_addr *our_adr,
+ struct in6_addr *his_adr, size_t prefixlen);
int tun_addroute(struct tun_t *this, struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask);
--
To view, visit https://gerrit.osmocom.org/2870
To unsubscribe, visit https://gerrit.osmocom.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: If8ce8b4b8cd2ba97f7ba122de4703983111046e4
Gerrit-PatchSet: 1
Gerrit-Project: openggsn
Gerrit-Branch: master
Gerrit-Owner: groos at xiplink.com <groos at xiplink.com>