tun-device: Use SIOCAIFADDR to set IP address on FreeBSD 10

FreeBSD 10 deprecated the SIOCSIFADDR etc. commands, so we use this
newer command to set the address and netmask.  A destination address
is now also required.

Fixes #566.
This commit is contained in:
Tobias Brunner 2014-04-25 11:28:52 +02:00
parent 502eeb7f76
commit acc042fa7b
1 changed files with 90 additions and 2 deletions

View File

@ -50,6 +50,10 @@ tun_device_t *tun_device_create(const char *name_tmpl)
#elif defined(__linux__)
#include <linux/types.h>
#include <linux/if_tun.h>
#elif __FreeBSD__ >= 10
#include <net/if_tun.h>
#include <net/if_var.h>
#include <netinet/in_var.h>
#else
#include <net/if_tun.h>
#endif
@ -101,8 +105,79 @@ struct private_tun_device_t {
u_int8_t netmask;
};
METHOD(tun_device_t, set_address, bool,
private_tun_device_t *this, host_t *addr, u_int8_t netmask)
/**
* FreeBSD 10 deprecated the SIOCSIFADDR etc. commands.
*/
#if __FreeBSD__ >= 10
static bool set_address_and_mask(struct in_aliasreq *ifra, host_t *addr,
u_int8_t netmask)
{
host_t *mask;
memcpy(&ifra->ifra_addr, addr->get_sockaddr(addr),
*addr->get_sockaddr_len(addr));
/* set the same address as destination address */
memcpy(&ifra->ifra_dstaddr, addr->get_sockaddr(addr),
*addr->get_sockaddr_len(addr));
mask = host_create_netmask(addr->get_family(addr), netmask);
if (!mask)
{
DBG1(DBG_LIB, "invalid netmask: %d", netmask);
return FALSE;
}
memcpy(&ifra->ifra_mask, mask->get_sockaddr(mask),
*mask->get_sockaddr_len(mask));
mask->destroy(mask);
return TRUE;
}
/**
* Set the address using the more flexible SIOCAIFADDR/SIOCDIFADDR commands
* on FreeBSD 10 an newer.
*/
static bool set_address_impl(private_tun_device_t *this, host_t *addr,
u_int8_t netmask)
{
struct in_aliasreq ifra;
memset(&ifra, 0, sizeof(ifra));
strncpy(ifra.ifra_name, this->if_name, IFNAMSIZ);
if (this->address)
{ /* remove the existing address first */
if (!set_address_and_mask(&ifra, this->address, this->netmask))
{
return FALSE;
}
if (ioctl(this->sock, SIOCDIFADDR, &ifra) < 0)
{
DBG1(DBG_LIB, "failed to remove existing address on %s: %s",
this->if_name, strerror(errno));
return FALSE;
}
}
if (!set_address_and_mask(&ifra, addr, netmask))
{
return FALSE;
}
if (ioctl(this->sock, SIOCAIFADDR, &ifra) < 0)
{
DBG1(DBG_LIB, "failed to add address on %s: %s",
this->if_name, strerror(errno));
return FALSE;
}
return TRUE;
}
#else /* __FreeBSD__ */
/**
* Set the address using the classic SIOCSIFADDR etc. commands on other systems.
*/
static bool set_address_impl(private_tun_device_t *this, host_t *addr,
u_int8_t netmask)
{
struct ifreq ifr;
host_t *mask;
@ -143,6 +218,19 @@ METHOD(tun_device_t, set_address, bool,
this->if_name, strerror(errno));
return FALSE;
}
return TRUE;
}
#endif /* __FreeBSD__ */
METHOD(tun_device_t, set_address, bool,
private_tun_device_t *this, host_t *addr, u_int8_t netmask)
{
if (!set_address_impl(this, addr, netmask))
{
return FALSE;
}
DESTROY_IF(this->address);
this->address = addr->clone(addr);
this->netmask = netmask;
return TRUE;