596 lines
14 KiB
C
596 lines
14 KiB
C
/* 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;
|
|
}
|