summaryrefslogtreecommitdiff
path: root/src/starter/interfaces.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/starter/interfaces.c')
-rw-r--r--src/starter/interfaces.c595
1 files changed, 595 insertions, 0 deletions
diff --git a/src/starter/interfaces.c b/src/starter/interfaces.c
new file mode 100644
index 000000000..a7c8efd44
--- /dev/null
+++ b/src/starter/interfaces.c
@@ -0,0 +1,595 @@
+/* strongSwan IPsec interfaces management
+ * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id: interfaces.c,v 1.15 2006/02/05 10:51:55 as Exp $
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <freeswan.h>
+#include <ipsec_tunnel.h>
+
+#include <constants.h>
+#include <defs.h>
+#include <log.h>
+
+#include "interfaces.h"
+#include "exec.h"
+#include "files.h"
+
+#define MIN(a,b) ( ((a)>(b)) ? (b) : (a) )
+
+#define N_IPSEC_IF 4
+
+struct st_ipsec_if {
+ char name[IFNAMSIZ];
+ char phys[IFNAMSIZ];
+ int up;
+};
+
+static struct st_ipsec_if _ipsec_if[N_IPSEC_IF];
+
+static char *
+_find_physical_iface(int sock, char *iface)
+{
+ static char _if[IFNAMSIZ];
+ char *b;
+ struct ifreq req;
+ FILE *fd;
+ char line[BUF_LEN];
+
+ strncpy(req.ifr_name, iface, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)==0)
+ {
+ if (req.ifr_flags & IFF_UP)
+ {
+ strncpy(_if, iface, IFNAMSIZ);
+ return _if;
+ }
+ }
+ else
+ {
+ /* If there is a file named /var/run/dynip/<iface>, look if we
+ * can get interface name from there (IP_PHYS)
+ */
+ b = (char *)alloc_bytes(strlen(DYNIP_DIR) + strlen(iface) + 10, "iface");
+ if (b)
+ {
+ sprintf(b, "%s/%s", DYNIP_DIR, iface);
+ fd = fopen(b, "r");
+ pfree(b);
+ if (fd)
+ {
+ memset(_if, 0, sizeof(_if));
+ memset(line, 0, sizeof(line));
+ while (fgets(line, sizeof(line), fd) != 0)
+ {
+ if ((strncmp(line,"IP_PHYS=\"", 9) == 0)
+ && (line[strlen(line) - 2] == '"')
+ && (line[strlen(line) - 1] == '\n'))
+ {
+ strncpy(_if, line + 9, MIN(strlen(line) - 11, IFNAMSIZ));
+ break;
+ }
+ else if ((strncmp(line,"IP_PHYS=", 8) == 0)
+ && (line[8] != '"')
+ && (line[strlen(line) - 1] == '\n'))
+ {
+ strncpy(_if, line + 8, MIN(strlen(line) - 9, IFNAMSIZ));
+ break;
+ }
+ }
+ fclose(fd);
+
+ if (*_if)
+ {
+ strncpy(req.ifr_name, _if, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
+ {
+ if (req.ifr_flags & IFF_UP)
+ return _if;
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+int
+starter_iface_find(char *iface, int af, ip_address *dst, ip_address *nh)
+{
+ char *phys;
+ struct ifreq req;
+ struct sockaddr_in *sa = (struct sockaddr_in *)(&req.ifr_addr);
+ int sock;
+
+ if (!iface)
+ return -1;
+
+ sock = socket(af, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return -1;
+
+ phys = _find_physical_iface(sock, iface);
+ if (!phys)
+ goto failed;
+
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)!=0)
+ goto failed;
+ if (!(req.ifr_flags & IFF_UP))
+ goto failed;
+
+ if ((req.ifr_flags & IFF_POINTOPOINT)
+ && nh
+ && ioctl(sock, SIOCGIFDSTADDR, &req) == 0)
+ {
+ if (sa->sin_family == af)
+ initaddr((const void *)&sa->sin_addr, sizeof(struct in_addr), af, nh);
+ }
+ if ((dst) && (ioctl(sock, SIOCGIFADDR, &req) == 0))
+ {
+ if (sa->sin_family == af)
+ initaddr((const void *)&sa->sin_addr, sizeof(struct in_addr), af, dst);
+ }
+ close(sock);
+ return 0;
+
+failed:
+ close(sock);
+ return -1;
+}
+
+static int
+valid_str(char *str, unsigned int *pn, char **pphys
+, defaultroute_t *defaultroute)
+{
+ if (streq(str, "%defaultroute"))
+ {
+ if (!defaultroute->defined)
+ {
+ return 0;
+ }
+ *pn = 0;
+ *pphys = defaultroute->iface;
+ }
+ else
+ {
+ if (strlen(str) < 8
+ || str[0] != 'i' || str[1] != 'p' || str[2] !='s' || str[3] != 'e'
+ || str[4] != 'c' || str[5] < '0' || str[5] > '9' || str[6] != '=')
+ {
+ return 0;
+ }
+ *pn = str[5] - '0';
+ *pphys = &(str[7]);
+ }
+ return 1;
+}
+
+static int
+_iface_up (int sock, struct st_ipsec_if *iface, char *phys
+, unsigned int mtu, bool nat_t)
+{
+ struct ifreq req;
+ struct ipsectunnelconf *shc=(struct ipsectunnelconf *)&req.ifr_data;
+ short phys_flags;
+ int ret = 0;
+ /* sscholz@astaro.com: for network mask 32 bit
+ struct sockaddr_in *inp;
+ */
+
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) !=0 )
+ return ret;
+ phys_flags = req.ifr_flags;
+
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) != 0)
+ return ret;
+
+ if ((!(req.ifr_flags & IFF_UP)) || (!iface->up))
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("attaching interface %s to %s", iface->name, phys)
+ )
+ ret = 1;
+ }
+
+ if ((*iface->phys) && (strcmp(iface->phys, phys) != 0 ))
+ {
+ /* tncfg --detach if phys has changed */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, IPSEC_DEL_DEV, &req);
+ ret = 1;
+ }
+
+ /* tncfg --attach */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ strncpy(shc->cf_name, phys, sizeof(shc->cf_name));
+ ioctl(sock, IPSEC_SET_DEV, &req);
+
+ /* set ipsec addr = phys addr */
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFADDR, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, SIOCSIFADDR, &req);
+ }
+
+ /* set ipsec mask = phys mask */
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFNETMASK, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ /* sscholz@astaro.com: changed netmask to 32 bit
+ * in order to prevent network routes from being created
+
+ inp = (struct sockaddr_in *)&req.ifr_addr;
+ inp->sin_addr.s_addr = 0xFFFFFFFFL;
+
+ */
+ ioctl(sock, SIOCSIFNETMASK, &req);
+ }
+
+ /* set other flags & addr */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)==0)
+ {
+/* removed by sscholz@astaro.com (caused trouble with DSL/ppp0) */
+/* if (phys_flags & IFF_POINTOPOINT)
+ {
+ req.ifr_flags |= IFF_POINTOPOINT;
+ req.ifr_flags &= ~IFF_BROADCAST;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFDSTADDR, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, SIOCSIFDSTADDR, &req);
+ }
+ }
+ else
+ */
+ if (phys_flags & IFF_BROADCAST)
+ {
+ req.ifr_flags &= ~IFF_POINTOPOINT;
+ req.ifr_flags |= IFF_BROADCAST;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFBRDADDR, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, SIOCSIFBRDADDR, &req);
+ }
+ }
+ else
+ {
+ req.ifr_flags &= ~IFF_POINTOPOINT;
+ req.ifr_flags &= ~IFF_BROADCAST;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ }
+ }
+
+ /*
+ * guess MTU = phys interface MTU - ESP Overhead
+ *
+ * ESP overhead : 10+16+7+2+12=57 -> 60 by security
+ * NAT-T overhead : 20
+ */
+ if (mtu == 0)
+ {
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ ioctl(sock, SIOCGIFMTU, &req);
+ mtu = req.ifr_mtu - 60;
+ if (nat_t)
+ mtu -= 20;
+ }
+ /* set MTU */
+ if (mtu)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ req.ifr_mtu = mtu;
+ ioctl(sock, SIOCSIFMTU, &req);
+ }
+
+ /* ipsec interface UP */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
+ {
+ req.ifr_flags |= IFF_UP;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ }
+
+ iface->up = 1;
+ strncpy(iface->phys, phys, IFNAMSIZ);
+ return ret;
+}
+
+static int
+_iface_down(int sock, struct st_ipsec_if *iface)
+{
+ struct ifreq req;
+ int ret = 0;
+
+ iface->up = 0;
+
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)!=0)
+ return ret;
+
+ if (req.ifr_flags & IFF_UP)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("shutting down interface %s/%s", iface->name, iface->phys)
+ )
+ req.ifr_flags &= ~IFF_UP;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ ret = 1;
+ }
+
+ /* unset addr */
+ memset(&req.ifr_addr, 0, sizeof(req.ifr_addr));
+ req.ifr_addr.sa_family = AF_INET;
+ ioctl(sock, SIOCSIFADDR, &req);
+
+ /* tncfg --detach */
+ ioctl(sock, IPSEC_DEL_DEV, &req);
+
+ memset(iface->phys, 0, sizeof(iface->phys));
+
+ return ret;
+}
+
+void
+starter_ifaces_init(void)
+{
+ int i;
+
+ memset(_ipsec_if, 0, sizeof(_ipsec_if));
+ for (i = 0; i < N_IPSEC_IF; i++)
+ snprintf(_ipsec_if[i].name, IFNAMSIZ, "ipsec%d", i);
+}
+
+void
+starter_ifaces_clear (void)
+{
+ int sock;
+ unsigned int i;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return;
+
+ for (i = 0; i < N_IPSEC_IF; i++)
+ _iface_down (sock, &(_ipsec_if[i]));
+}
+
+int
+starter_ifaces_load(char **ifaces, unsigned int omtu, bool nat_t
+, defaultroute_t *defaultroute)
+{
+ char *tmp_phys, *phys;
+ int n;
+ char **i;
+ int sock;
+ int j, found;
+ int ret = 0;
+ struct ifreq physreq, ipsecreq; // re-attach interface
+ struct sockaddr_in *inp1, *inp2; // re-attach interface
+
+ DBG(DBG_CONTROL,
+ DBG_log("starter_ifaces_load()")
+ )
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return -1;
+
+ for (j = 0; j < N_IPSEC_IF; j++)
+ {
+ found = 0;
+
+ for (i = ifaces; i && *i; i++)
+ {
+ if (valid_str(*i, &n, &tmp_phys, defaultroute)
+ && tmp_phys
+ && n >= 0
+ && n < N_IPSEC_IF)
+ {
+ if (n==j)
+ {
+ if (found)
+ {
+ plog( "ignoring duplicate entry for interface ipsec%d", j);
+ }
+ else
+ {
+ found++;
+ phys = _find_physical_iface(sock, tmp_phys);
+
+ /* Re-attach ipsec interface if IP address changes
+ * sscholz@astaro.com
+ */
+ if (phys)
+ {
+ memset ((void*)&physreq, 0, sizeof(physreq));
+ memset ((void*)&ipsecreq, 0, sizeof(ipsecreq));
+ strncpy(physreq.ifr_name, phys, IFNAMSIZ);
+ sprintf(ipsecreq.ifr_name, "ipsec%d", j);
+ ioctl(sock, SIOCGIFADDR, &physreq);
+ ioctl(sock, SIOCGIFADDR, &ipsecreq);
+ inp1 = (struct sockaddr_in *)&physreq.ifr_addr;
+ inp2 = (struct sockaddr_in *)&ipsecreq.ifr_addr;
+ if (inp1->sin_addr.s_addr != inp2->sin_addr.s_addr)
+ {
+ plog("IP address of physical interface changed "
+ "-> reinit of ipsec interface");
+ _iface_down (sock, &(_ipsec_if[n]));
+ }
+ ret += _iface_up (sock, &(_ipsec_if[n]), phys, omtu, nat_t);
+ }
+ else
+ {
+ ret += _iface_down (sock, &(_ipsec_if[n]));
+ }
+ }
+ }
+ }
+ else if (j == 0)
+ {
+ /* Only log in the first loop */
+ plog("ignoring invalid interface '%s'", *i);
+ }
+ }
+ if (!found)
+ ret += _iface_down (sock, &(_ipsec_if[j]));
+ }
+
+ close(sock);
+ return ret; /* = number of changes - 'whack --listen' if > 0 */
+}
+
+/*
+ * initialize a defaultroute_t struct
+ */
+static void
+init_defaultroute(defaultroute_t *defaultroute)
+{
+ memset(defaultroute, 0, sizeof(defaultroute_t));
+}
+
+/*
+ * discover the default route via /proc/net/route
+ */
+void
+get_defaultroute(defaultroute_t *defaultroute)
+{
+ FILE *fd;
+ char line[BUF_LEN];
+ bool first = TRUE;
+
+ init_defaultroute(defaultroute);
+
+ fd = fopen("/proc/net/route", "r");
+
+ if (!fd)
+ {
+ plog("could not open 'proc/net/route'");
+ return;
+ }
+
+ while (fgets(line, sizeof(line), fd) != 0)
+ {
+ char iface[11];
+ char destination[9];
+ char gateway[11];
+ char flags[5];
+ char mask[9];
+
+ int refcnt;
+ int use;
+ int metric;
+ int items;
+
+ /* proc/net/route returns IP addresses in host order */
+ strcpy(gateway, "0h");
+
+ /* skip the header line */
+ if (first)
+ {
+ first = FALSE;
+ continue;
+ }
+
+ /* parsing a single line of proc/net/route */
+ items = sscanf(line, "%10s\t%8s\t%8s\t%5s\t%d\t%d\t%d\t%8s\t"
+ , iface, destination, gateway+2, flags, &refcnt, &use, &metric, mask);
+ if (items < 8)
+ {
+ plog("parsing error while scanning /proc/net/route");
+ continue;
+ }
+
+ /* check for defaultroute (destination 0.0.0.0 and mask 0.0.0.0) */
+ if (streq(destination, "00000000") && streq(mask, "00000000"))
+ {
+ if (defaultroute->defined)
+ {
+ plog("multiple default routes - cannot cope with %%defaultroute!!!");
+ defaultroute->defined = FALSE;
+ fclose(fd);
+ return;
+ }
+ ttoaddr(gateway, strlen(gateway), AF_INET, &defaultroute->nexthop);
+ strncpy(defaultroute->iface, iface, IFNAMSIZ);
+ defaultroute->defined = TRUE;
+ }
+ }
+ fclose(fd);
+
+ if (!defaultroute->defined)
+ {
+ plog("no default route - cannot cope with %%defaultroute!!!");
+ }
+ else
+ {
+ char addr_buf[20], nexthop_buf[20];
+ struct ifreq physreq;
+
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+ /* determine IP address of iface */
+ if (sock < 0)
+ {
+ plog("could not open SOCK_DGRAM socket");
+ defaultroute->defined = FALSE;
+ return;
+ }
+ memset ((void*)&physreq, 0, sizeof(physreq));
+ strncpy(physreq.ifr_name, defaultroute->iface, IFNAMSIZ);
+ ioctl(sock, SIOCGIFADDR, &physreq);
+ close(sock);
+ defaultroute->addr.u.v4 = *((struct sockaddr_in *)&physreq.ifr_addr);
+
+ addrtot(&defaultroute->addr, 0, addr_buf, sizeof(addr_buf));
+ addrtot(&defaultroute->nexthop, 0, nexthop_buf, sizeof(nexthop_buf));
+
+ DBG(DBG_CONTROL,
+ DBG_log("Default route found: iface=%s, addr=%s, nexthop=%s"
+ , defaultroute->iface, addr_buf, nexthop_buf)
+ )
+
+ /* for backwards-compatibility with the awk shell scripts
+ * store the defaultroute in /var/run/ipsec.info
+ */
+ fd = fopen(INFO_FILE, "w");
+
+ if (fd)
+ {
+ fprintf(fd, "defaultroutephys=%s\n", defaultroute->iface );
+ fprintf(fd, "defaultroutevirt=ipsec0\n");
+ fprintf(fd, "defaultrouteaddr=%s\n", addr_buf);
+ fprintf(fd, "defaultroutenexthop=%s\n", nexthop_buf);
+ fclose(fd);
+ }
+ }
+ return;
+}