Enable IP multicast routing in Linux kernel


In this post I discuss how to enable multicast routing in a Linux system. It is a continuation to the post Wireless Router with Buildroot and Raspberry Pi, where I discussed how to build a basic Wi-Fi router with a Raspberry Pi. You’ll want to read that first.

Linux kernel configuration

Besides the Kernel modules mentioned in the post(s) linked above, you’ll need a few additional modules.

IP multicast routing and tunneling

IPv6 protocol

Under Networking support, Networking options, enable

  • IP: multicasting
  • IP: tunneling – this is required if you want to use tunneling with mrouted
  • IP: multicast routing and its sub-options
  • The IPv6 protocol

In the absence of IPv6 smcroute fails with an error such as

Starting static multicast router daemon: INIT: ICMPv6 socket open; Errno(97): Address family not supported by protocol
INIT: MRT6_INIT failed; Errno(97): Address family not supported by protocol
smcroute.

IPv6 Multicast Routing

Under Networking support, Networking options, The IPv6 protocol, enable IPv6: multicast routing and its sub-options.

Packet Mangling

Enable packet mangling with TTL target support if you require support for changing TTL values with iptables.

Buildroot package configuration

The following Buildroot packages provide daemons for performing multicast routing. Enable mrouted and smcroute under Target packages, Networking applications. mrouted requires a glibc based toolchain, you will have to enable it instead of uClibc if you want to use mrouted.

mrouted

smcroute

Perform build and prepare the SD card.

Setup multicast routing

The following procedure is performed from a root console. I usually use the serial console through the expansion header.

Use mrouted when proper IGMP signaling exists

mrouted

The default configuration file /etc/mrouted.conf should be enough, unless you want to perform tunneling.

If you don’t have proper IGMP signaling happening, you can still perform static multicast routing using

smcroute -d

smcroute requires a configuration file, which in my case is /etc/smcroute.conf and looks something like

mgroup from wlan0 group 225.0.0.1
mroute from wlan0 group 225.0.0.1 to usb0

If you don’t have an application and want to use ping to test mutlicast, you can enable ICMP echo responses thus

echo "0" > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

You can use ping requests and receive responses from destination hosts

ping -t 10 225.0.0.1

Note the use of the time to live (TTL) parameter -t. Linux and Mac OS X will set TTL to 1 before forwarding message to the default gateway. You can dump ping messages with TTL parameter using

tcpdump -v host 224.0.0.1 or 225.0.0.1

Note change in TTL from 10 to 1 in a packet routed through Mac OS X in the following dump

14:49:19.642140 IP (tos 0x0, ttl 10, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.211.55.12 > 225.0.0.1: ICMP echo request, id 4113, seq 92, length 64
14:49:19.642190 IP (tos 0x0, ttl 1, id 32573, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.2.10 > 225.0.0.1: ICMP echo request, id 4113, seq 92, length 64

If all is well with the routing daemon, IP variable /proc/sys/net/ipv4/conf/{all,interface}/mc_forwarding will be set to 1.

Some other files offer useful hints related to multicast routing. The following lists interfaces where multicast routing is active

cat /proc/net/ip_mr_vif

This lists multicast routing cache entries

cat /proc/net/ip_mr_cache

When using static multicast routing with smcroute, routing will work only when TTL is greater than 1. If the downstream hosts are transmitting packets with TTL at 1, you can use iptables to set TTL thus

iptables -t mangle -A PREROUTING -i wlan0 -j TTL --ttl-set 64

I’ve also had to wait a while after executing smcroute for NAT to kick in, so that source IP address is translated to address of interface on the destination network. Note the change in source IP address in a message sequence captured using

tcpdump -v -i usb0 host 224.0.0.1 or 225.0.0.1
03:03:25.393056 IP (tos 0x0, ttl 63, id 16103, offset 0, flags [none], proto UDP (17), length 41)
    192.168.2.10.61312 > 225.0.0.1.4007: UDP, length 13
03:04:22.277348 IP (tos 0x0, ttl 63, id 8095, offset 0, flags [none], proto UDP (17), length 41)
    192.168.10.2.61312 > 225.0.0.1.4007: UDP, length 13

IP multicasting


IP multicasting is used to target a group of hosts by sending a single datagram. IP addresses in the range 224.0.0.0 through 239.255.255.255 are reserved for multicasting.

To find out which hosts on your subnet support multicasting, try

ping 224.0.0.1

Here’s a Node.js code snippet that sends UDP datagrams to multicast group 225.0.0.1 at port 8001

var dgram = require('dgram');
var s = dgram.createSocket('udp4');
s.bind(8000);
var b = new Buffer("Hello");
s.send(b, 0, b.length, 8001, "225.0.0.1", function(err, bytes) {
  console.log("Sent " + bytes + " bytes");
  s.close();
});

A host that desires to receive a datagram sent to a multicast group, must first request membership to that group. Here’s a Node.js code snippet that receives datagram sent by the code above

var dgram = require('dgram');
var s = dgram.createSocket('udp4');
s.bind(8001, function() {
  s.addMembership('225.0.0.1');
});
s.on("message", function (msg, rinfo) {
  console.log("server got: " + msg + " from " +
    rinfo.address + ":" + rinfo.port);
});

.NET code that does something similar can be found in the UDP Tool at GitHUb.

Receiving multicasts on Linux does not work when you bind the socket to a specific interface, for instance s.bind(8001, 192.168.1.1... does not work. It looks like a Linux-only (nay Unix?) quirk because it does not happen with either Mono .NET runtime or Node.js on Windows.

Another quirk observed on Linux is the need to add a route to forward multicast IP packets received over a wireless LAN interface in access point mode

route add -net 225.0.0.0 netmask 255.0.0.0 gw 192.168.2.1

Or, a more generic

route add -net 224.0.0.0/4 gw 192.168.2.1

Check that the route has been added with

netstat -nr

Or, just

route

Mono .NET runtime on Raspberry Pi with Buildroot


Mono is a popular way to run .NET applications on platforms other than Windows. In this post I’ll show how to build and copy Mono onto a Raspberry Pi, with Buildroot.

Obtain source code for Mono

Let’s start by obtaining Mono from GitHub. I suggest using a folder outside of Buildroot. Later, we’ll prepare Buildroot to fetch the source code from this folder.

git clone https://github.com/mono/mono.git

Let’s head into the cloned repo. We’ll need to switch to a different branch. After much trial and error I’ve discovered that branch mono-3.6.0-branch builds without any error

cd mono
git checkout mono-3.6.0-branch

Create a package for Mono in Buildroot

We’ll create a new package called mono in Buildroot, that will fetch the source code for Mono from the cloned repo, and prepare and build it.

The location of source code for the mono package needs to be specified in file local.mk in Buildroot’s root folder

MONO_OVERRIDE_SRCDIR = /home/parallels/mono/

Add a new package called mono to package/Config.in. I’ve added it under menu “Interpreter languages and scripting”, but you can choose another menu

menu "Interpreter languages and scripting"
        source "package/mono/Config.in"

We’ll create a new folder called mono under folder package, and make a configuration file called Config.in

mkdir package/mono
vi package/mono/Config.in

Here’s the content of Config.in

config BR2_PACKAGE_MONO
	bool "mono"
	help
	  Sponsored by Xamarin, Mono is an open source implementation of Microsoft's .NET Framework based on the ECMA standards for C# and the Common Language Runtime.

	  https://github.com/mono/mono

Next, we’ll tell Buildroot how to build the mono package

vi package/mono/mono.mk

Here’s the content for package/mono/mono.mk

################################################################################
#
# mono
#
################################################################################

MONO_AUTORECONF = YES
MONO_DEPENDENCIES = libglib2

$(eval $(autotools-package))

Configure Buildroot and perform the build

Now that package mono has been created, select it by invoking the configuration menu

make menuconfig

You’ll also need to switch to glibc under Toolchain. Build with uClibc fails with

os_dep.c:3990:23: execinfo.h: No such file or directory

glibc

Now, all that’s left to do is to perform the build

make clean
make

After the build, prepare the SD card. Mono’s build does not copy .NET core libraries to the target filesystem. You’ll need to copy those manually, like so

sudo cp -R output/build/mono-custom/mcs/class/lib/basic/* /media/parallels/System/usr/lib/mono/2.0

If you forget to do that, .NET applications will fail with a message such as

The assembly mscorlib.dll was not found or could not be loaded.
It should have been installed in the `/usr/lib/mono/2.0/mscorlib.dll' directory.

Other notes

Builds on branches master and mono-3.12.0-branch fail with

mono/mini/Makefile.am:86: error: JIT_SUPPORTED does not appear in AM_CONDITIONAL
mono/mini/Makefile.am:722: error: JIT_SUPPORTED does not appear in AM_CONDITIONAL
mono/unit-tests/Makefile.am:7: warning: variable 'TEST_LDFLAGS' is defined but no program or
mono/unit-tests/Makefile.am:7: library has 'TEST' as canonical name (possible typo)
autoreconf: /home/devendra/buildroot-2014.08/output/host/usr/bin/automake failed with exit status: 1

Builds on branches mono-3.8.0-branch and mono-3.10.0-branch fail with link time error

mini.c:(.text+0x7878): undefined reference to `mono_cross_helpers_run'