diff options
Diffstat (limited to 'src/Erebos')
| -rw-r--r-- | src/Erebos/Network.hs | 29 | ||||
| -rw-r--r-- | src/Erebos/Network/ifaddrs.c | 84 | ||||
| -rw-r--r-- | src/Erebos/Network/ifaddrs.h | 2 | 
3 files changed, 106 insertions, 9 deletions
| diff --git a/src/Erebos/Network.hs b/src/Erebos/Network.hs index 1e06008..2064d1c 100644 --- a/src/Erebos/Network.hs +++ b/src/Erebos/Network.hs @@ -54,6 +54,9 @@ import GHC.Conc.Sync (unsafeIOToSTM)  import Network.Socket hiding (ControlMessage)  import qualified Network.Socket.ByteString as S +import Foreign.C.Types +import Foreign.Marshal.Alloc +  import Erebos.Channel  #ifdef ENABLE_ICE_SUPPORT  import Erebos.ICE @@ -71,6 +74,9 @@ import Erebos.Storage.Merge  discoveryPort :: PortNumber  discoveryPort = 29665 +discoveryMulticastGroup :: HostAddress6 +discoveryMulticastGroup = tupleToHostAddress6 (0xff12, 0xb6a4, 0x6b1f, 0x0969, 0xcaee, 0xacc2, 0x5c93, 0x73e1) -- ff12:b6a4:6b1f:969:caee:acc2:5c93:73e1 +  announceIntervalSeconds :: Int  announceIntervalSeconds = 60 @@ -249,8 +255,6 @@ startServer opt serverOrigHead logd' serverServices = do          either (atomically . logd) return =<< runExceptT =<<              atomically (readTQueue serverIOActions) -    broadcastAddreses <- getBroadcastAddresses discoveryPort -      let open addr = do              sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)              putMVar serverSocket sock @@ -261,9 +265,14 @@ startServer opt serverOrigHead logd' serverServices = do              return sock          loop sock = do -            when (serverLocalDiscovery opt) $ forkServerThread server $ forever $ do -                atomically $ writeFlowBulk serverControlFlow $ map (SendAnnounce . DatagramAddress) broadcastAddreses -                threadDelay $ announceIntervalSeconds * 1000 * 1000 +            when (serverLocalDiscovery opt) $ forkServerThread server $ do +                announceAddreses <- fmap concat $ sequence $ +                    [ map (SockAddrInet6 discoveryPort 0 discoveryMulticastGroup) <$> joinMulticast sock +                    , getBroadcastAddresses discoveryPort +                    ] +                forever $ do +                    atomically $ writeFlowBulk serverControlFlow $ map (SendAnnounce . DatagramAddress) announceAddreses +                    threadDelay $ announceIntervalSeconds * 1000 * 1000              let announceUpdate identity = do                      st <- derivePartialStorage serverStorage @@ -944,9 +953,19 @@ runPeerServiceOn mbservice peer handler = liftIO $ do              logd $ "unhandled service '" ++ show (toUUID svc) ++ "'" +foreign import ccall unsafe "Network/ifaddrs.h join_multicast" cJoinMulticast :: CInt -> Ptr CSize -> IO (Ptr Word32)  foreign import ccall unsafe "Network/ifaddrs.h broadcast_addresses" cBroadcastAddresses :: IO (Ptr Word32)  foreign import ccall unsafe "stdlib.h free" cFree :: Ptr Word32 -> IO () +joinMulticast :: Socket -> IO [ Word32 ] +joinMulticast sock = +    withFdSocket sock $ \fd -> +    alloca $ \pcount -> do +        ptr <- cJoinMulticast fd pcount +        count <- fromIntegral <$> peek pcount +        forM [ 0 .. count - 1 ] $ \i -> +            peekElemOff ptr i +  getBroadcastAddresses :: PortNumber -> IO [SockAddr]  getBroadcastAddresses port = do      ptr <- cBroadcastAddresses diff --git a/src/Erebos/Network/ifaddrs.c b/src/Erebos/Network/ifaddrs.c index efeca18..70685bc 100644 --- a/src/Erebos/Network/ifaddrs.c +++ b/src/Erebos/Network/ifaddrs.c @@ -1,13 +1,89 @@  #include "ifaddrs.h" -#ifndef _WIN32 +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef _WIN32  #include <arpa/inet.h> -#include <ifaddrs.h>  #include <net/if.h> -#include <stdlib.h> -#include <sys/types.h> +#include <ifaddrs.h>  #include <endian.h> +#include <sys/types.h> +#include <sys/socket.h> +#else +#include <winsock2.h> +#include <ws2ipdef.h> +#include <ws2tcpip.h> +#endif + +#define DISCOVERY_MULTICAST_GROUP "ff12:b6a4:6b1f:969:caee:acc2:5c93:73e1" + +uint32_t * join_multicast(int fd, size_t * count) +{ +	size_t capacity = 16; +	*count = 0; +	uint32_t * interfaces = malloc(sizeof(uint32_t) * capacity); + +#ifdef _WIN32 +	interfaces[0] = 0; +	*count = 1; +#else +	struct ifaddrs * addrs; +	if (getifaddrs(&addrs) < 0) +		return 0; + +	for (struct ifaddrs * ifa = addrs; ifa; ifa = ifa->ifa_next) { +		if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6 && +				!(ifa->ifa_flags & IFF_LOOPBACK)) { +			int idx = if_nametoindex(ifa->ifa_name); + +			bool seen = false; +			for (size_t i = 0; i < *count; i++) { +				if (interfaces[i] == idx) { +					seen = true; +					break; +				} +			} +			if (seen) +				continue; + +			if (*count + 1 >= capacity) { +				capacity *= 2; +				uint32_t * nret = realloc(interfaces, sizeof(uint32_t) * capacity); +				if (nret) { +					interfaces = nret; +				} else { +					free(interfaces); +					*count = 0; +					return NULL; +				} +			} + +			interfaces[*count] = idx; +			(*count)++; +		} +	} + +	freeifaddrs(addrs); +#endif + +	for (size_t i = 0; i < *count; i++) { +		struct ipv6_mreq group; +		group.ipv6mr_interface = interfaces[i]; +		inet_pton(AF_INET6, DISCOVERY_MULTICAST_GROUP, &group.ipv6mr_multiaddr); +		int ret = setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, +				(const void *) &group, sizeof(group)); +		if (ret < 0) +			fprintf(stderr, "IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno)); +	} + +	return interfaces; +} + +#ifndef _WIN32  uint32_t * broadcast_addresses(void)  { diff --git a/src/Erebos/Network/ifaddrs.h b/src/Erebos/Network/ifaddrs.h index 06d26ec..8852ec6 100644 --- a/src/Erebos/Network/ifaddrs.h +++ b/src/Erebos/Network/ifaddrs.h @@ -1,3 +1,5 @@ +#include <stddef.h>  #include <stdint.h> +uint32_t * join_multicast(int fd, size_t * count);  uint32_t * broadcast_addresses(void); |