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.orgReview 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>