PUD: nwif: make setting ttl on tx socket work for IPv6
[olsrd.git] / lib / pud / src / networkInterfaces.c
index 83fbc5a..0fdbdce 100644 (file)
@@ -6,19 +6,49 @@
 #include "netTools.h"
 
 /* OLSRD includes */
-#include "olsr_cfg.h"
 #include "olsr.h"
 
 /* System includes */
-#include <assert.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <errno.h>
 #include <unistd.h>
-#include <stdlib.h>
 #include <fcntl.h>
 #include <ifaddrs.h>
 
+/*
+ * Main IP MAC address
+ */
+
+/** the MAC address of the main IP address */
+static unsigned char mac[PUD_NODEIDTYPE_MAC_BYTES] = { 0 };
+
+/** true when the MAC address of the main IP address has been retrieved */
+static bool macSet = false;
+
+/**
+ * @return
+ * the MAC address of the main IP address
+ */
+unsigned char * getMainIpMacAddress(void) {
+       if (!macSet) {
+               struct ifreq ifr;
+               unsigned char * macInIfr;
+
+               struct interface *mainInterface = if_ifwithaddr(&olsr_cnf->main_addr);
+               if (!mainInterface) {
+                       pudError(true, "Could not get the main interface");
+                       return NULL;
+               }
+               macInIfr = getHardwareAddress(mainInterface->int_name, olsr_cnf->ip_version, &ifr);
+               if (!macInIfr) {
+                       pudError(true, "Could not get the MAC address of the main interface");
+                       return NULL;
+               }
+               memcpy(&mac[0], &macInIfr[0], PUD_NODEIDTYPE_MAC_BYTES);
+               macSet = true;
+       }
+
+       return &mac[0];
+}
+
 /*
  * RX interfaces
  */
@@ -45,29 +75,37 @@ TRxTxNetworkInterface *getRxNetworkInterfaces(void) {
  information, except for the socket descriptor.
  @param rxSocketHandlerFunction
  The function that handles reception of data on the network interface
+ @param rxMcAddr
+ The receive multicast address (must have same IP version as networkInterface address)
 
  @return
  - the socket descriptor (>= 0)
  - -1 if an error occurred
  */
 static int createRxSocket(TRxTxNetworkInterface * networkInterface,
-               socket_handler_func rxSocketHandlerFunction) {
+               socket_handler_func rxSocketHandlerFunction, union olsr_sockaddr * rxMcAddr) {
        int ipFamilySetting;
        int ipProtoSetting;
        int ipMcLoopSetting;
        int ipAddMembershipSetting;
-       int socketReuseFlagValue = 1;
-       int mcLoopValue = 1;
+
        union olsr_sockaddr address;
+       struct sockaddr * addr;
+       size_t addrSize;
+
        int rxSocket = -1;
 
+       int socketReuseFlagValue = 1;
+       int mcLoopValue = 1;
+
        assert(networkInterface != NULL);
        assert(rxSocketHandlerFunction != NULL);
        assert(strncmp((char *) &networkInterface->name[0], "",
                                        sizeof(networkInterface->name)) != 0);
+       assert(networkInterface->ipAddress.in.sa_family == rxMcAddr->in.sa_family);
 
        memset(&address, 0, sizeof(address));
-       if (olsr_cnf->ip_version == AF_INET) {
+       if (networkInterface->ipAddress.in.sa_family == AF_INET) {
                assert(networkInterface->ipAddress.in4.sin_addr.s_addr != INADDR_ANY);
 
                ipFamilySetting = AF_INET;
@@ -78,6 +116,8 @@ static int createRxSocket(TRxTxNetworkInterface * networkInterface,
                address.in4.sin_family = ipFamilySetting;
                address.in4.sin_addr.s_addr = INADDR_ANY;
                address.in4.sin_port = getRxMcPort();
+               addr = (struct sockaddr *)&address.in4;
+               addrSize = sizeof(struct sockaddr_in);
        } else {
                assert(networkInterface->ipAddress.in6.sin6_addr.s6_addr != in6addr_any.s6_addr);
 
@@ -89,6 +129,8 @@ static int createRxSocket(TRxTxNetworkInterface * networkInterface,
                address.in6.sin6_family = ipFamilySetting;
                address.in6.sin6_addr = in6addr_any;
                address.in6.sin6_port = getRxMcPort();
+               addr = (struct sockaddr *)&address.in6;
+               addrSize = sizeof(struct sockaddr_in6);
        }
 
        /* Create a datagram socket on which to receive. */
@@ -113,7 +155,7 @@ static int createRxSocket(TRxTxNetworkInterface * networkInterface,
        /* Bind to the proper port number with the IP address INADDR_ANY
         * (INADDR_ANY is really required here, do not change it) */
        errno = 0;
-       if (bind(rxSocket, (struct sockaddr *) &address, sizeof(address)) < 0) {
+       if (bind(rxSocket, addr, addrSize) < 0) {
                pudError(true, "Could not bind the receive socket for interface"
                        " %s to port %u", networkInterface->name, ntohs(getRxMcPort()));
                goto bail;
@@ -135,7 +177,7 @@ static int createRxSocket(TRxTxNetworkInterface * networkInterface,
        if (ipFamilySetting == AF_INET) {
                struct ip_mreq mc_settings;
                (void) memset(&mc_settings, 0, sizeof(mc_settings));
-               mc_settings.imr_multiaddr = getRxMcAddr()->in4.sin_addr;
+               mc_settings.imr_multiaddr = rxMcAddr->in4.sin_addr;
                mc_settings.imr_interface = networkInterface->ipAddress.in4.sin_addr;
                errno = 0;
                if (setsockopt(rxSocket, ipProtoSetting, ipAddMembershipSetting,
@@ -147,8 +189,8 @@ static int createRxSocket(TRxTxNetworkInterface * networkInterface,
        } else {
                struct ipv6_mreq mc6_settings;
                (void) memset(&mc6_settings, 0, sizeof(mc6_settings));
-               mc6_settings.ipv6mr_multiaddr = getRxMcAddr()->in6.sin6_addr;
-               mc6_settings.ipv6mr_interface = 0;
+               mc6_settings.ipv6mr_multiaddr = rxMcAddr->in6.sin6_addr;
+               mc6_settings.ipv6mr_interface = if_nametoindex((char *)networkInterface->name);
                errno = 0;
                if (setsockopt(rxSocket, ipProtoSetting, ipAddMembershipSetting,
                                &mc6_settings, sizeof(mc6_settings)) < 0) {
@@ -180,27 +222,22 @@ static int createRxSocket(TRxTxNetworkInterface * networkInterface,
  the IP address of the interface
  @param rxSocketHandlerFunction
  the function that handles reception of data on the network interface
+ @param rxMcAddr
+ The receive multicast address (must have same IP version as ipAddr)
 
  @return
  - true on success
  - false on failure
  */
-static bool createRxInterface(const char * ifName, union olsr_sockaddr ipAddr,
-               socket_handler_func rxSocketHandlerFunction) {
-       unsigned char * hwAddr;
+static bool createRxInterface(const char * ifName, union olsr_sockaddr * ipAddr,
+               socket_handler_func rxSocketHandlerFunction, union olsr_sockaddr * rxMcAddr) {
        int socketFd = -1;
        TRxTxNetworkInterface * networkInterface = NULL;
-       struct ifreq ifReqHwAddr;
 
        if (ifName == NULL) {
                goto bail;
        }
 
-       hwAddr = getHardwareAddress(ifName, olsr_cnf->ip_version, &ifReqHwAddr);
-       if (hwAddr == NULL) {
-               goto bail;
-       }
-
        networkInterface = olsr_malloc(sizeof(TRxTxNetworkInterface),
                        "TRxTxNetworkInterface (PUD)");
        if (networkInterface == NULL) {
@@ -209,17 +246,17 @@ static bool createRxInterface(const char * ifName, union olsr_sockaddr ipAddr,
 
        memcpy(networkInterface->name, ifName, sizeof(networkInterface->name));
        networkInterface->name[IFNAMSIZ] = '\0';
-       networkInterface->ipAddress = ipAddr;
-       memcpy(&networkInterface->hwAddress[0], hwAddr,
-                       sizeof(networkInterface->hwAddress));
+       networkInterface->ipAddress = *ipAddr;
+       networkInterface->handler = NULL;
        networkInterface->next = NULL;
 
        /* networkInterface needs to be filled in when calling createRxSocket */
-       socketFd = createRxSocket(networkInterface, rxSocketHandlerFunction);
+       socketFd = createRxSocket(networkInterface, rxSocketHandlerFunction, rxMcAddr);
        if (socketFd < 0) {
                goto bail;
        }
        networkInterface->socketFd = socketFd;
+       networkInterface->handler = rxSocketHandlerFunction;
 
        /* Add new object to the end of the global list. */
        if (rxNetworkInterfacesListHead == NULL) {
@@ -273,23 +310,28 @@ static int createTxSocket(TRxTxNetworkInterface * networkInterface) {
        int ipProtoSetting;
        int ipMcLoopSetting;
        int ipMcIfSetting;
-       int mcLoopValue = 0;
-       unsigned char txTtl = getTxTtl();
+       int ipTtlSetting;
+
        union olsr_sockaddr address;
+
        int txSocket = -1;
 
+       int mcLoopValue = 0;
+       int txTtl = getTxTtl();
+
        assert(networkInterface != NULL);
        assert(strncmp((char *) &networkInterface->name[0], "",
                                        sizeof(networkInterface->name)) != 0);
 
        memset(&address, 0, sizeof(address));
-       if (olsr_cnf->ip_version == AF_INET) {
+       if (networkInterface->ipAddress.in.sa_family == AF_INET) {
                assert(networkInterface->ipAddress.in4.sin_addr.s_addr != INADDR_ANY);
 
                ipFamilySetting = AF_INET;
                ipProtoSetting = IPPROTO_IP;
                ipMcLoopSetting = IP_MULTICAST_LOOP;
                ipMcIfSetting = IP_MULTICAST_IF;
+               ipTtlSetting = IP_MULTICAST_TTL;
 
                address.in4.sin_family = ipFamilySetting;
                address.in4.sin_addr = networkInterface->ipAddress.in4.sin_addr;
@@ -301,6 +343,7 @@ static int createTxSocket(TRxTxNetworkInterface * networkInterface) {
                ipProtoSetting = IPPROTO_IPV6;
                ipMcLoopSetting = IPV6_MULTICAST_LOOP;
                ipMcIfSetting = IPV6_MULTICAST_IF;
+               ipTtlSetting = IPV6_MULTICAST_HOPS;
 
                address.in6.sin6_family = ipFamilySetting;
                address.in6.sin6_addr = networkInterface->ipAddress.in6.sin6_addr;
@@ -337,7 +380,7 @@ static int createTxSocket(TRxTxNetworkInterface * networkInterface) {
 
        /* Set the TTL on the socket */
        errno = 0;
-       if (setsockopt(txSocket, ipProtoSetting, IP_MULTICAST_TTL, &txTtl,
+       if (setsockopt(txSocket, ipProtoSetting, ipTtlSetting, &txTtl,
                        sizeof(txTtl)) < 0) {
                pudError(true, "Could not set TTL on the transmit socket"
                        " for interface %s", networkInterface->name);
@@ -373,21 +416,14 @@ static int createTxSocket(TRxTxNetworkInterface * networkInterface) {
  - true on success
  - false on failure
  */
-static bool createTxInterface(const char * ifName, union olsr_sockaddr ipAddr) {
-       unsigned char * hwAddr;
+static bool createTxInterface(const char * ifName, union olsr_sockaddr * ipAddr) {
        int socketFd = -1;
        TRxTxNetworkInterface * networkInterface = NULL;
-       struct ifreq ifReqHwAddr;
 
        if (ifName == NULL) {
                goto bail;
        }
 
-       hwAddr = getHardwareAddress(ifName, olsr_cnf->ip_version, &ifReqHwAddr);
-       if (hwAddr == NULL) {
-               goto bail;
-       }
-
        networkInterface = olsr_malloc(sizeof(TRxTxNetworkInterface),
                        "TRxTxNetworkInterface (PUD)");
        if (networkInterface == NULL) {
@@ -396,9 +432,8 @@ static bool createTxInterface(const char * ifName, union olsr_sockaddr ipAddr) {
 
        memcpy(networkInterface->name, ifName, sizeof(networkInterface->name));
        networkInterface->name[IFNAMSIZ] = '\0';
-       networkInterface->ipAddress = ipAddr;
-       memcpy(&networkInterface->hwAddress[0], hwAddr,
-                       sizeof(networkInterface->hwAddress));
+       networkInterface->ipAddress = *ipAddr;
+       networkInterface->handler = NULL;
        networkInterface->next = NULL;
 
        /* networkInterface needs to be filled in when calling createTxSocket */
@@ -425,6 +460,100 @@ static bool createTxInterface(const char * ifName, union olsr_sockaddr ipAddr) {
        return false;
 }
 
+/*
+ * Downlink interface
+ */
+
+/** The socket fd, receiving downlinked messages */
+static int downlinkSocketFd = -1;
+
+/** the downlink handler function */
+static socket_handler_func downlinkHandler = NULL;
+
+
+/**
+ @return
+ The downlink socket fd. -1 when not valid.
+ */
+int getDownlinkSocketFd(void) {
+       return downlinkSocketFd;
+}
+
+/**
+ Create an downlink socket
+
+ @param ipVersion
+ The IP version (AF_INET or AF_INET6) for the socket
+ @param rxSocketHandlerFunction
+ The socket handler function
+
+ @return
+ - the socket descriptor (>= 0)
+ - -1 if an error occurred
+ */
+static int createDownlinkSocket(int ipVersion, socket_handler_func rxSocketHandlerFunction) {
+       union olsr_sockaddr address;
+       struct sockaddr * addr;
+       size_t addrSize;
+
+       int downlinkSocket = -1;
+
+       int socketReuseFlagValue = 1;
+
+       memset(&address, 0, sizeof(address));
+       if (ipVersion == AF_INET) {
+               address.in4.sin_family = AF_INET;
+               address.in4.sin_addr.s_addr = INADDR_ANY;
+               address.in4.sin_port = getDownlinkPort();
+               addr = (struct sockaddr *)&address.in4;
+               addrSize = sizeof(struct sockaddr_in);
+       } else {
+               address.in6.sin6_family = AF_INET6;
+               address.in6.sin6_addr = in6addr_any;
+               address.in6.sin6_port = getDownlinkPort();
+               addr = (struct sockaddr *)&address.in6;
+               addrSize = sizeof(struct sockaddr_in6);
+       }
+
+       /*  Create a datagram socket on which to receive */
+       errno = 0;
+       downlinkSocket = socket(ipVersion, SOCK_DGRAM, 0);
+       if (downlinkSocket < 0) {
+               pudError(true, "Could not create the downlink socket");
+               goto bail;
+       }
+
+       /* Enable SO_REUSEADDR to allow multiple applications to receive the same
+        * messages */
+       errno = 0;
+       if (setsockopt(downlinkSocket, SOL_SOCKET, SO_REUSEADDR, &socketReuseFlagValue,
+                       sizeof(socketReuseFlagValue)) < 0) {
+               pudError(true, "Could not set REUSE option on the downlink socket");
+               goto bail;
+       }
+
+       /* Bind to the proper port number with the IP address INADDR_ANY
+        * (INADDR_ANY is really required here, do not change it) */
+       errno = 0;
+       if (bind(downlinkSocket, addr, addrSize)) {
+               pudError(true, "Could not bind downlink socket to port %d",
+                               getDownlinkPort());
+               goto bail;
+       }
+
+       add_olsr_socket(downlinkSocket, rxSocketHandlerFunction, NULL, NULL,
+                       SP_PR_READ);
+
+       downlinkHandler = rxSocketHandlerFunction;
+
+       return downlinkSocket;
+
+       bail: if (downlinkSocket >= 0) {
+               close(downlinkSocket);
+       }
+       return -1;
+}
+
 /*
  * OLSR interfaces
  */
@@ -468,16 +597,8 @@ TOLSRNetworkInterface * getOlsrNetworkInterface(struct interface *olsrIntf) {
  - false on failure
  */
 static int createOlsrInterface(struct interface *olsrIntf) {
-       unsigned char * hwAddr;
-       struct ifreq ifReqHwAddr;
        TOLSRNetworkInterface * networkInterface = NULL;
 
-       hwAddr = getHardwareAddress(olsrIntf->int_name, olsr_cnf->ip_version,
-                       &ifReqHwAddr);
-       if (hwAddr == NULL) {
-               goto bail;
-       }
-
        networkInterface = olsr_malloc(sizeof(TOLSRNetworkInterface),
                        "TOLSRNetworkInterface (PUD)");
        if (networkInterface == NULL) {
@@ -485,8 +606,6 @@ static int createOlsrInterface(struct interface *olsrIntf) {
        }
 
        networkInterface->olsrIntf = olsrIntf;
-       memcpy(&networkInterface->hwAddress[0], hwAddr,
-                       sizeof(networkInterface->hwAddress));
        networkInterface->next = NULL;
 
        /* Add new object to the end of the global list. */
@@ -516,15 +635,20 @@ static int createOlsrInterface(struct interface *olsrIntf) {
 
  @param rxSocketHandlerFunction
  The function to call upon reception of data on a receive socket
+ @param rxSocketHandlerFunctionDownlink
+ The function to call upon reception of data on a downlink receive socket
 
  @return
  - true on success
  - false on failure
  */
-bool createNetworkInterfaces(socket_handler_func rxSocketHandlerFunction) {
+bool createNetworkInterfaces(socket_handler_func rxSocketHandlerFunction,
+               socket_handler_func rxSocketHandlerFunctionDownlink) {
        int retval = false;
        struct ifaddrs *ifAddrs = NULL;
        struct ifaddrs *ifAddr = NULL;
+       union olsr_sockaddr * rxMcAddr = getRxMcAddr();
+       union olsr_sockaddr * txMcAddr = getTxMcAddr();
 
        errno = 0;
        if (getifaddrs(&ifAddrs) != 0) {
@@ -534,61 +658,61 @@ bool createNetworkInterfaces(socket_handler_func rxSocketHandlerFunction) {
 
        /* loop over all interfaces */
        for (ifAddr = ifAddrs; ifAddr != NULL; ifAddr = ifAddr->ifa_next) {
-               struct sockaddr * addr = ifAddr->ifa_addr;
-               if (addr != NULL) {
-                       int addrFamily = addr->sa_family;
-                       if (addrFamily == olsr_cnf->ip_version) {
-                               char * ifName = ifAddr->ifa_name;
-                               union olsr_sockaddr ipAddr;
-
-                               /* determine whether the iterated interface is an OLSR
-                                * interface: returns NULL when the interface is not an
-                                * OLSR interface */
-                               struct interface *olsrIntf = if_ifwithname(ifName);
-                               bool isOlsrIf = (olsrIntf != NULL);
-
-                               /* determine whether the iterated interface is configured as a
-                                * non-OLSR interface in the plugin parameter list */
-                               bool isRxNonOlsrIf = isRxNonOlsrInterface(ifName);
-                               bool isTxNonOlsrIf = isTxNonOlsrInterface(ifName);
-                               bool isNonOlsrIf = isRxNonOlsrIf || isTxNonOlsrIf;
-
-                               if (!isOlsrIf && !isNonOlsrIf) {
-                                       /* Interface is not an OLSR interface AND interface is not
-                                        * configured as non-OLSR interface: skip */
-                                       continue;
-                               }
-
-                               if (isOlsrIf && !createOlsrInterface(olsrIntf)) {
-                                       /* creating an OLSR interface failed */
-                                       goto end;
-                               }
-
-                               if (!isNonOlsrIf) {
-                                       /* interface is not configured as non-OLSR interface: skip */
-                                       continue;
-                               }
-
-                               if (addrFamily == AF_INET) {
-                                       memcpy(&ipAddr.in4, addr, sizeof(struct sockaddr_in));
-                               } else {
-                                       memcpy(&ipAddr.in6, addr, sizeof(struct sockaddr_in6));
-                               }
-
-                               if (isRxNonOlsrIf && !createRxInterface(ifName, ipAddr,
-                                               rxSocketHandlerFunction)) {
-                                       /* creating a receive interface failed */
-                                       goto end;
-                               }
-
-                               if (isTxNonOlsrIf && !createTxInterface(ifName, ipAddr)) {
-                                       /* creating a transmit interface failed */
-                                       goto end;
-                               }
+               union olsr_sockaddr * addr = (union olsr_sockaddr *)ifAddr->ifa_addr;
+               if ((addr != NULL) && ((addr->in.sa_family == AF_INET) || (addr->in.sa_family == AF_INET6))) {
+                       char * ifName = ifAddr->ifa_name;
+
+                       struct interface * olsrIntf = ((addr->in.sa_family != olsr_cnf->ip_version) ?
+                                       NULL :
+                                       if_ifwithaddr((addr->in.sa_family == AF_INET) ?
+                                                       (union olsr_ip_addr *)&addr->in4.sin_addr :
+                                                       (union olsr_ip_addr *)&addr->in6.sin6_addr));
+                       bool isOlsrIf = (olsrIntf != NULL);
+
+                       /* determine whether the iterated interface is configured as a
+                        * non-OLSR interface in the plugin parameter list */
+                       bool isRxNonOlsrIf = isRxNonOlsrInterface(ifName) && (addr->in.sa_family == rxMcAddr->in.sa_family);
+                       bool isTxNonOlsrIf = isTxNonOlsrInterface(ifName) && (addr->in.sa_family == txMcAddr->in.sa_family);
+
+                       bool isNonOlsrIf = isRxNonOlsrIf || isTxNonOlsrIf;
+
+                       if (!isOlsrIf && !isNonOlsrIf) {
+                               /* Interface is not an OLSR interface AND interface is not
+                                * configured as non-OLSR interface: skip */
+                               continue;
+                       }
+
+                       if (isOlsrIf && !createOlsrInterface(olsrIntf)) {
+                               /* creating an OLSR interface failed */
+                               goto end;
+                       }
+
+                       if (!isNonOlsrIf) {
+                               /* interface is not configured as non-OLSR interface: skip */
+                               continue;
+                       }
+
+                       if (isRxNonOlsrIf && !createRxInterface(ifName, addr, rxSocketHandlerFunction, rxMcAddr)) {
+                               /* creating a receive interface failed */
+                               goto end;
+                       }
+
+                       if (isTxNonOlsrIf && !createTxInterface(ifName, addr)) {
+                               /* creating a transmit interface failed */
+                               goto end;
                        }
                }
        }
 
+       if (isUplinkAddrSet()) {
+               downlinkSocketFd = createDownlinkSocket(getUplinkAddr()->in.sa_family, rxSocketHandlerFunctionDownlink);
+               if (downlinkSocketFd == -1) {
+                       goto end;
+               }
+       } else {
+               downlinkSocketFd = -1;
+       }
+
        retval = true;
 
        end: freeifaddrs(ifAddrs);
@@ -622,6 +746,10 @@ static void closeInterfaces(TRxTxNetworkInterface * networkInterface) {
        while (nextNetworkInterface != NULL) {
                TRxTxNetworkInterface * iteratedNetworkInterface = nextNetworkInterface;
                if (iteratedNetworkInterface->socketFd >= 0) {
+                       if (iteratedNetworkInterface->handler) {
+                               remove_olsr_socket(iteratedNetworkInterface->socketFd,
+                                               iteratedNetworkInterface->handler, NULL);
+                       }
                        close(iteratedNetworkInterface->socketFd);
                        iteratedNetworkInterface->socketFd = -1;
                }
@@ -649,4 +777,13 @@ void closeNetworkInterfaces(void) {
                cleanupOlsrInterfaces(olsrNetworkInterfacesListHead);
                olsrNetworkInterfacesListHead = NULL;
        }
+
+       if (downlinkSocketFd != -1 ) {
+               if (downlinkHandler) {
+                       remove_olsr_socket (downlinkSocketFd, downlinkHandler, NULL);
+                       downlinkHandler = NULL;
+               }
+               close(downlinkSocketFd);
+               downlinkSocketFd = -1;
+       }
 }