Below is a minimal example program that compiles under both MacOS/X and Linux. Its purpose is to send a single node-scoped UDP/IPv6 multicast packet over the machine's loopback-device. I believe it is correct, and it does work as expected under MacOS/X, but under Linux it fails with this output:
Attempting to send an IPv6/UDP packet to multicast address [ff11:0:1:0:94a4:2318:6300:4d51] on interface at index 1 (aka lo)
FAILURE! sendto() returned -1, errno=101 aka [Network is unreachable]
... however if I modify line 26 to set the constant interfaceIdx = 0
rather than interfaceIdx = if_nametoindex(ifaceName)
, it successfully sends the packet under Linux:
Attempting to send an IPv6/UDP packet to multicast address [ff11:0:1:0:94a4:2318:6300:4d51] on interface at index 0 (aka lo)
SUCCESS! sendto() returned 18
... so I could just put in an #ifdef __linux__
to force the interfaceIdx
variable to 0, but I feel like that isn't the correct fix, since AFAIK setting the interface-index correctly is a requirement for IPv6-scoped-multicast. Does anyone know why setting the interfaceIdx
to its correct value breaks the program under Linux?
Here is the program:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
// This function is here solely to make sure I pass in the right pointer to inet_pton's third argument
static int Inet_PtoN(int af, const char * src, struct in6_addr * dst)
{
return inet_pton(af, src, dst);
}
int main(void)
{
const char * dest = "ff11:0:1:0:94a4:2318:6300:4d51"; // my local-node-scoped IPv6 multicast address
#ifdef __linux__
const char * ifaceName = "lo"; // name of loopback device under Linux
#else
const char * ifaceName = "lo0"; // name of loopback device under MacOS/X
#endif
const int interfaceIdx = if_nametoindex(ifaceName);
printf("Attempting to send an IPv6/UDP packet to multicast address [%s] on interface at index %i (aka %s)\n", dest, interfaceIdx, ifaceName);
int s = socket(AF_INET6, SOCK_DGRAM, 0);
if (s<0) {perror("socket()"); return 10;}
if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interfaceIdx, sizeof(interfaceIdx)) != 0) {perror("setsockopt(IPV6_MULTICAST_IF)"); return -1;}
struct sockaddr_in6 toAddr; memset(&toAddr, 0, sizeof(toAddr));
toAddr.sin6_family = AF_INET6;
toAddr.sin6_port = htons(12345);
if (Inet_PtoN(AF_INET6, dest, &toAddr.sin6_addr) <= 0) {perror("inet_pton()"); return -1;}
toAddr.sin6_scope_id = interfaceIdx;
char buf[] = "dummy payload text";
const int r = sendto(s, buf, strlen(buf), 0, (struct sockaddr *)&toAddr, sizeof(toAddr));
if (r >= 0) printf("SUCCESS! sendto() returned %i\n", r);
else printf("FAILURE! sendto() returned %i, errno=%i aka [%s]\n", r, errno, strerror(errno));
close(s);
return 0;
}
... and here, just for reference, is the output of ifconfig -a
in my Linux VM (Ubuntu 18.0.5):
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.158.154 netmask 255.255.255.0 broadcast 172.16.158.255
inet6 fe80::bcf8:ae62:d420:b850 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:d5:92:67 txqueuelen 1000 (Ethernet)
RX packets 41456 bytes 60446720 (60.4 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 15899 bytes 1058502 (1.0 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 5442 bytes 983303 (983.3 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5442 bytes 983303 (983.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
.... and here is the output of ifconfig -a
on my Mac (which hosts the Linux Ubuntu VM via VMWare Fusion):
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
inet 127.0.0.1 netmask 0xff000000
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
inet 127.94.0.2 netmask 0xff000000
inet 127.94.0.1 netmask 0xff000000
nd6 options=201<PERFORMNUD,DAD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
XHC0: flags=0<> mtu 0
XHC1: flags=0<> mtu 0
XHC20: flags=0<> mtu 0
VHC128: flags=0<> mtu 0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=50b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV,CHANNEL_IO>
ether f0:18:98:e8:e4:81
inet6 fe80::4ef:a7f5:734c:5a82%en0 prefixlen 64 secured scopeid 0x8
inet 10.0.1.26 netmask 0xffffff00 broadcast 10.0.1.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (<unknown type>)
status: active
en6: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether ac:de:48:00:11:22
inet6 fe80::aede:48ff:fe00:1122%en6 prefixlen 64 scopeid 0x9
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (100baseTX <full-duplex>)
status: active
en8: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=400<CHANNEL_IO>
ether 52:de:06:3a:29:9f
inet6 fe80::1438:7fb7:7145:e06c%en8 prefixlen 64 secured scopeid 0xa
inet 169.254.73.131 netmask 0xffff0000 broadcast 169.254.255.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (100baseTX <full-duplex>)
status: active
ap1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
options=400<CHANNEL_IO>
ether f2:18:98:a8:a3:b4
media: autoselect
status: inactive
en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=400<CHANNEL_IO>
ether f0:18:98:a8:a3:b4
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (<unknown type>)
status: inactive
p2p0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 2304
options=400<CHANNEL_IO>
ether 02:18:98:a8:a3:b4
media: autoselect
status: inactive
awdl0: flags=8902<BROADCAST,PROMISC,SIMPLEX,MULTICAST> mtu 1484
options=400<CHANNEL_IO>
ether 2a:72:ad:a2:71:2b
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: inactive
llw0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=400<CHANNEL_IO>
ether 2a:72:ad:a2:71:2b
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: inactive
en5: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
options=460<TSO4,TSO6,CHANNEL_IO>
ether 82:32:b3:81:34:04
media: autoselect <full-duplex>
status: inactive
en2: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
options=460<TSO4,TSO6,CHANNEL_IO>
ether 82:32:b3:81:34:01
media: autoselect <full-duplex>
status: inactive
en3: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
options=460<TSO4,TSO6,CHANNEL_IO>
ether 82:32:b3:81:34:00
media: autoselect <full-duplex>
status: inactive
en4: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
options=460<TSO4,TSO6,CHANNEL_IO>
ether 82:32:b3:81:34:05
media: autoselect <full-duplex>
status: inactive
bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=63<RXCSUM,TXCSUM,TSO4,TSO6>
ether 82:32:b3:81:34:01
Configuration:
id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
ipfilter disabled flags 0x0
member: en2 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 17 priority 0 path cost 0
member: en3 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 18 priority 0 path cost 0
member: en4 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 19 priority 0 path cost 0
member: en5 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 16 priority 0 path cost 0
nd6 options=201<PERFORMNUD,DAD>
media: <unknown type>
status: inactive
utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380
inet6 fe80::d1a3:48e0:11d1:fc27%utun0 prefixlen 64 scopeid 0x15
nd6 options=201<PERFORMNUD,DAD>
utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 2000
inet6 fe80::ee71:43c3:4648:31b0%utun1 prefixlen 64 scopeid 0x16
nd6 options=201<PERFORMNUD,DAD>
utun2: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
inet 172.27.224.165 --> 172.27.224.165 netmask 0xffffffc0
vmnet1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 00:50:56:c0:00:08
inet 172.16.158.1 netmask 0xffffff00 broadcast 172.16.158.255
Since ffx1:
should not go out of the system, it should normally be fine to rendevouz on the system default interface (unless maybe interfaces go up and down between when client and servers setup.) One could add firewall rules, if the OS might accidentally send or accept these addresses on the wire.
If you want to make the actual lo interface act as a valid multicast to explicitly rendevouz on the private link, it needs the same configuration as a normal interface. How to make this persistent on boot would vary but most I think most modern Linuxes would understand these commands:
ifconfig lo multicast
ip -6 route show table local
# find an interface route and copy it for lo and create it with a lower metric:
ip -6 route add table local ff00::/8 dev lo metric 255 pref medium
User contributions licensed under CC BY-SA 3.0