#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <net/route.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netdb.h>
#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#include <net/if.h>
#endif
#include <fcntl.h>
#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "sockmisc.h"
#include "debug.h"
#include "localconf.h"
#include "handler.h"
#include "grabmyaddr.h"
#include "sockmisc.h"
#include "isakmp_var.h"
#include "gcmalloc.h"
#include "nattraversal.h"
#ifndef HAVE_GETIFADDRS
static unsigned int if_maxindex (void);
#endif
static int suitable_ifaddr (const char *, const struct sockaddr *);
#ifdef INET6
static int suitable_ifaddr6 (const char *, const struct sockaddr *);
#endif
#ifndef HAVE_GETIFADDRS
static unsigned int
if_maxindex()
{
struct if_nameindex *p, *p0;
unsigned int max = 0;
p0 = if_nameindex();
for (p = p0; p && p->if_index && p->if_name; p++) {
if (max < p->if_index)
max = p->if_index;
}
if_freenameindex(p0);
return max;
}
#endif
void
clear_myaddr()
{
struct myaddrs *p, *next;
for (p = lcconf->myaddrs; p; p = next) {
next = p->next;
delmyaddr(p);
}
lcconf->myaddrs = NULL;
}
struct myaddrs *
find_myaddr(addr, udp_encap)
struct sockaddr *addr;
int udp_encap;
{
struct myaddrs *q;
char h1[NI_MAXHOST], h2[NI_MAXHOST];
if (getnameinfo(addr, sysdep_sa_len(addr), h1, sizeof(h1), NULL, 0,
NI_NUMERICHOST | niflags) != 0)
return NULL;
for (q = lcconf->myaddrs; q; q = q->next) {
if (!q->addr)
continue;
if ((q->udp_encap && !udp_encap)
|| (!q->udp_encap && udp_encap))
continue;
if (addr->sa_family != q->addr->ss_family)
continue;
if (getnameinfo((struct sockaddr *)q->addr, sysdep_sa_len((struct sockaddr *)q->addr), h2, sizeof(h2),
NULL, 0, NI_NUMERICHOST | niflags) != 0)
return NULL;
if (strcmp(h1, h2) == 0)
return q;
}
return NULL;
}
void
grab_myaddrs()
{
struct myaddrs *p, *q;
struct ifaddrs *ifa0, *ifap;
char addr1[NI_MAXHOST];
if (getifaddrs(&ifa0)) {
plog(ASL_LEVEL_ERR,
"getifaddrs failed: %s\n", strerror(errno));
exit(1);
}
for (p = lcconf->myaddrs; p; p = p->next)
p->in_use = 0;
for (ifap = ifa0; ifap; ifap = ifap->ifa_next) {
if (ifap->ifa_addr->sa_family != AF_INET
#ifdef INET6
&& ifap->ifa_addr->sa_family != AF_INET6
#endif
)
continue;
if (!suitable_ifaddr(ifap->ifa_name, ifap->ifa_addr)) {
plog(ASL_LEVEL_DEBUG,
"unsuitable address: %s %s\n",
ifap->ifa_name,
saddrwop2str(ifap->ifa_addr));
continue;
}
p = find_myaddr(ifap->ifa_addr, 0);
if (p) {
p->in_use = 1;
#ifdef ENABLE_NATT
q = find_myaddr(ifap->ifa_addr, 1);
if (q)
q->in_use = 1;
else if (natt_enabled_in_rmconf ()) {
q = dupmyaddr(p);
if (q == NULL) {
plog(ASL_LEVEL_ERR,
"unable to allocate space for natt addr.\n");
exit(1);
}
q->udp_encap = 1;
}
#endif
} else {
p = newmyaddr();
if (p == NULL) {
plog(ASL_LEVEL_ERR,
"unable to allocate space for addr.\n");
exit(1);
}
p->addr = dupsaddr(ALIGNED_CAST(struct sockaddr_storage*)ifap->ifa_addr);
if (p->addr == NULL) {
plog(ASL_LEVEL_ERR,
"unable to duplicate addr.\n");
exit(1);
}
p->ifname = racoon_strdup(ifap->ifa_name);
if (p->ifname == NULL) {
plog(ASL_LEVEL_ERR,
"unable to duplicate ifname.\n");
exit(1);
}
p->in_use = 1;
if (getnameinfo((struct sockaddr *)p->addr, p->addr->ss_len,
addr1, sizeof(addr1),
NULL, 0,
NI_NUMERICHOST | niflags))
strlcpy(addr1, "(invalid)", sizeof(addr1));
plog(ASL_LEVEL_DEBUG,
"my interface: %s (%s)\n",
addr1, ifap->ifa_name);
p->next = lcconf->myaddrs;
lcconf->myaddrs = p;
#ifdef ENABLE_NATT
if (natt_enabled_in_rmconf ()) {
q = dupmyaddr(p);
if (q == NULL) {
plog(ASL_LEVEL_ERR,
"unable to allocate space for natt addr.\n");
exit(1);
}
q->udp_encap = 1;
}
#endif
}
}
freeifaddrs(ifa0);
}
static int
suitable_ifaddr(ifname, ifaddr)
const char *ifname;
const struct sockaddr *ifaddr;
{
#if 0 //we need to be able to do nested ipsec for BTMM... stub out ifdef ENABLE_HYBRID
if (exclude_cfg_addr(ifaddr) == 0)
return 0;
#endif
switch(ifaddr->sa_family) {
case AF_INET:
return 1;
#ifdef INET6
case AF_INET6:
return suitable_ifaddr6(ifname, ifaddr);
#endif
default:
return 0;
}
}
#ifdef INET6
static int
suitable_ifaddr6(ifname, ifaddr)
const char *ifname;
const struct sockaddr *ifaddr;
{
struct in6_ifreq ifr6;
int s;
if (ifaddr->sa_family != AF_INET6)
return 0;
s = socket(PF_INET6, SOCK_DGRAM, 0);
if (s == -1) {
plog(ASL_LEVEL_ERR,
"socket(SOCK_DGRAM) failed:%s\n", strerror(errno));
return 0;
}
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) {
plog(ASL_LEVEL_ERR, "failed to put IPv6 socket in non-blocking mode\n");
}
memset(&ifr6, 0, sizeof(ifr6));
strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
memcpy(&ifr6.ifr_addr, ifaddr, sizeof(struct sockaddr_in6));
if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
plog(ASL_LEVEL_ERR,
"ioctl(SIOCGIFAFLAG_IN6) failed:%s\n", strerror(errno));
close(s);
return 0;
}
close(s);
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED
|| ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED
|| ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST
|| ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
return 0;
return 1;
}
#endif
int
autoconf_myaddrsport()
{
struct myaddrs *p;
int n;
plog(ASL_LEVEL_DEBUG,
"configuring default isakmp port.\n");
for (p = lcconf->myaddrs, n = 0; p; p = p->next, n++) {
set_port (p->addr, p->udp_encap ? lcconf->port_isakmp_natt : lcconf->port_isakmp);
}
plog(ASL_LEVEL_DEBUG,
"%d addrs are configured successfully\n", n);
return 0;
}
u_short
getmyaddrsport(local)
struct sockaddr_storage *local;
{
struct myaddrs *p, *bestmatch = NULL;
u_short bestmatch_port = PORT_ISAKMP;
for (p = lcconf->myaddrs; p; p = p->next) {
if (!p->addr)
continue;
if (!cmpsaddrwop(local, p->addr)) {
if (! bestmatch) {
bestmatch = p;
continue;
}
switch (p->addr->ss_family) {
case AF_INET:
if (((struct sockaddr_in *)p->addr)->sin_port == PORT_ISAKMP) {
bestmatch = p;
bestmatch_port = ((struct sockaddr_in *)p->addr)->sin_port;
break;
}
break;
#ifdef INET6
case AF_INET6:
if (((struct sockaddr_in6 *)p->addr)->sin6_port == PORT_ISAKMP) {
bestmatch = p;
bestmatch_port = ((struct sockaddr_in6 *)p->addr)->sin6_port;
break;
}
break;
#endif
default:
plog(ASL_LEVEL_ERR,
"unsupported AF %d\n", p->addr->ss_family);
continue;
}
}
}
return htons(bestmatch_port);
}
struct myaddrs *
newmyaddr()
{
struct myaddrs *new;
new = racoon_calloc(1, sizeof(*new));
if (new == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer for myaddrs.\n");
return NULL;
}
new->next = NULL;
new->addr = NULL;
new->source = NULL;
new->sock = -1;
#ifdef __APPLE_
new->ifname = NULL;
#endif
return new;
}
struct myaddrs *
dupmyaddr(struct myaddrs *old)
{
struct myaddrs *new;
new = racoon_calloc(1, sizeof(*new));
if (new == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer for myaddrs.\n");
return NULL;
}
memcpy (new, old, sizeof (*new));
new->addr = dupsaddr (old->addr);
if (new->addr == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer for duplicate addr.\n");
racoon_free(new);
return NULL;
}
if (old->ifname) {
new->ifname = racoon_strdup(old->ifname);
if (new->ifname == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer for duplicate ifname.\n");
racoon_free(new->addr);
racoon_free(new);
return NULL;
}
}
new->source = NULL;
new->sock = -1;
new->next = old->next;
old->next = new;
return new;
}
void
insmyaddr(new, head)
struct myaddrs *new;
struct myaddrs **head;
{
new->next = *head;
*head = new;
}
void
delmyaddr(myaddr)
struct myaddrs *myaddr;
{
if (myaddr->addr)
racoon_free(myaddr->addr);
if (myaddr->ifname)
racoon_free(myaddr->ifname);
racoon_free(myaddr);
}
void
update_myaddrs(void *unused)
{
grab_myaddrs();
isakmp_close_unused();
autoconf_myaddrsport();
isakmp_open();
}
int
initmyaddr(void)
{
if (lcconf->myaddrs == NULL && lcconf->autograbaddr == 1) {
grab_myaddrs();
if (autoconf_myaddrsport() < 0)
return -1;
}
return 0;
}
int
getsockmyaddr(struct sockaddr *my)
{
struct myaddrs *p, *lastresort = NULL;
for (p = lcconf->myaddrs; p; p = p->next) {
if (p->addr == NULL)
continue;
if (my->sa_family == p->addr->ss_family) {
lastresort = p;
} else continue;
if (sysdep_sa_len(my) == sysdep_sa_len((struct sockaddr *)p->addr)
&& memcmp(my, p->addr, sysdep_sa_len(my)) == 0) {
break;
}
}
if (!p)
p = lastresort;
if (!p) {
plog(ASL_LEVEL_ERR,
"no socket matches address family %d\n",
my->sa_family);
return -1;
}
return p->sock;
}
void
pfroute_handler(void *unused)
{
struct rtmessage { struct rt_msghdr rtm;
char discard[BUFSIZ];
} msg;
int len;
while((len = read(lcconf->rtsock, &msg, sizeof(msg))) < 0) {
if (errno == EINTR)
continue;
plog(ASL_LEVEL_DEBUG,
"read(PF_ROUTE) failed: %s\n",
strerror(errno));
return;
}
if (len < msg.rtm.rtm_msglen) {
plog(ASL_LEVEL_DEBUG,
"read(PF_ROUTE) short read\n");
return;
}
switch (msg.rtm.rtm_type) {
case RTM_NEWADDR:
case RTM_DELADDR:
case RTM_DELETE:
case RTM_IFINFO:
break;
case RTM_MISS:
return;
default:
return;
}
plog(ASL_LEVEL_DEBUG,
"caught rtm:%d, need update interface address list\n",
msg.rtm.rtm_type);
update_myaddrs(NULL);
}
void
pfroute_close(void)
{
dispatch_source_cancel(lcconf->rt_source);
lcconf->rt_source = NULL;
}
int
pfroute_init(void)
{
int sock;
lcconf->rtsock = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
if (lcconf->rtsock < 0) {
plog(ASL_LEVEL_DEBUG,
"socket(PF_ROUTE) failed: %s",
strerror(errno));
return -1;
}
if (fcntl(lcconf->rtsock, F_SETFL, O_NONBLOCK) == -1) {
plog(ASL_LEVEL_DEBUG, "failed to put PF_ROUTE socket in non-blocking mode\n");
}
lcconf->rt_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, lcconf->rtsock, 0, dispatch_get_main_queue());
if (lcconf->rt_source == NULL) {
plog(ASL_LEVEL_DEBUG, "could not create pfroute socket source.");
return -1;
}
dispatch_source_set_event_handler_f(lcconf->rt_source, pfroute_handler);
sock = lcconf->rtsock;
dispatch_source_set_cancel_handler(lcconf->rt_source,
^{
close(sock);
});
dispatch_resume(lcconf->rt_source);
return 0;
}