tun-device: Use SIOCAIFADDR to set IP address on FreeBSD 10
authorTobias Brunner <tobias@strongswan.org>
Fri, 25 Apr 2014 09:28:52 +0000 (11:28 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 25 Apr 2014 10:18:06 +0000 (12:18 +0200)
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.

src/libstrongswan/networking/tun_device.c

index ecefdc2..f2c7b16 100644 (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 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
 #else
 #include <net/if_tun.h>
 #endif
@@ -101,8 +105,79 @@ struct private_tun_device_t {
        u_int8_t netmask;
 };
 
        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;
 {
        struct ifreq ifr;
        host_t *mask;
@@ -143,6 +218,19 @@ METHOD(tun_device_t, set_address, bool,
                         this->if_name, strerror(errno));
                return FALSE;
        }
                         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;
        this->address = addr->clone(addr);
        this->netmask = netmask;
        return TRUE;