Skip to content

Multicast with PIM-SSM

This lab shows a multicast routing example using PIM in Source Specific Multicast mode, with pimd.

PIM-SSM is a simplification of PIM-SM: a receiver does not ask for "everything sent to group G"; it asks for "what source S sends to group G". Because the source is known from the start, the router builds the (S,G) shortest-path tree immediately. There is no shared tree, so the lab needs neither a Rendezvous Point nor a Bootstrap Router.

The trade-off is that receivers must use IGMPv3 (FreeBSD does by default) and the group must belong to the SSM range, 232.0.0.0/8 per RFC 4607.

Overview

Network diagram

Here is the logical and physical view:

flowchart TD
    VM1["VM1 (multicast sources)<br/>vtnet0: 10.0.12.1<br/>alias: 10.0.12.11"]
    VM2["VM2 (PIM router)<br/>vtnet0: 10.0.12.2<br/>vtnet1: 10.0.23.2"]
    VM3["VM3 (PIM router)<br/>vtnet1: 10.0.23.3<br/>vtnet2: 10.0.34.3"]
    VM4["VM4 (multicast receiver)<br/>vtnet2: 10.0.34.4"]

    VM1 ---|"10.0.12.0/24<br/>source LAN"| VM2
    VM2 ---|"10.0.23.0/24<br/>PIM adjacency"| VM3
    VM3 ---|"10.0.34.0/24<br/>receiver LAN"| VM4

VM1 carries two addresses and will emit the same group 232.1.1.1 from both: 10.0.12.1 is the source the receiver subscribes to, 10.0.12.11 is a decoy used to show that SSM filters on the source.

Setting up the lab

Downloading BSD Router Project images

Download the BSDRP serial image (to avoid needing an X display) from SourceForge.

Download lab scripts

More information on the BSDRP lab scripts is available in How to build a BSDRP router lab.

Start the lab with 4 routers. The -r pimssm flag makes the lab script build per-VM cloud-init disks that apply the configuration below automatically on first boot (through the labconfig script shipped in the image):

tools/BSDRP-lab-bhyve.sh -i BSDRP-2.3-full-amd64.img.xz -n 4 -r pimssm

Without -r pimssm, the 4 VMs boot unconfigured and you can enter the configuration of each router by hand, as described below.

Router configuration

VM1 (multicast sources)

A plain host, no PIM. The /32 alias is the decoy source:

sysrc hostname=VM1 \
 gateway_enable=no \
 ipv6_gateway_enable=no \
 ifconfig_vtnet0="inet 10.0.12.1/24" \
 ifconfig_vtnet0_alias0="inet 10.0.12.11/32" \
 defaultrouter=10.0.12.2
service hostname restart
service netif restart
service routing restart
config save

VM2 (PIM router, source side)

No rp-candidate, no bsr-candidate: the only thing to declare is the group range handled as source-specific. ssm-range default is 232.0.0.0/8, which is also what pimd uses when nothing is configured, but writing it down documents the range for whoever reads the router configuration:

sysrc hostname=VM2 \
 gateway_enable=yes \
 ipv6_gateway_enable=yes \
 ifconfig_vtnet0="inet 10.0.12.2/24" \
 ifconfig_vtnet1="inet 10.0.23.2/24" \
 defaultrouter=10.0.23.3 \
 pimd_enable=yes

cat > /usr/local/etc/pimd.conf <<EOF
ssm-range default
EOF

service hostname restart
service netif restart
service routing restart
service pimd start
config save

VM3 (PIM router, receiver side)

Same configuration, mirrored:

sysrc hostname=VM3 \
 gateway_enable=yes \
 ipv6_gateway_enable=yes \
 ifconfig_vtnet1="inet 10.0.23.3/24" \
 ifconfig_vtnet2="inet 10.0.34.3/24" \
 defaultrouter=10.0.23.2 \
 pimd_enable=yes

cat > /usr/local/etc/pimd.conf <<EOF
ssm-range default
EOF

service hostname restart
service netif restart
service routing restart
service pimd start
config save

All routers must agree on the SSM range

Nothing in PIM advertises the SSM range: it is local configuration on every router, like Cisco's ip pim ssm range. A group inside the range on one router and outside it on the next will not work. Note also that configuring any ssm-range line replaces the default range instead of adding to it, so keeping 232.0.0.0/8 alongside a range of your own needs an explicit ssm-range default line.

VM4 (multicast receiver)

Another plain host, no PIM:

sysrc hostname=VM4 \
 gateway_enable=no \
 ipv6_gateway_enable=no \
 ifconfig_vtnet2="inet 10.0.34.4/24" \
 defaultrouter=10.0.34.3
service hostname restart
service netif restart
service routing restart
config save

Checking pimd behavior

The examples below were captured with pimd 3.1.0 on BSDRP 2.3. The -p flag of pimctl disables the terminal control characters used to highlight table headings, which keeps the output copy-pasteable.

No Rendezvous Point, no Bootstrap Router

This is the visible difference from the PIM-SM lab: the RP set holds only the static SSM entry (169.254.0.1 is the placeholder pimd uses for "no RP needed"), and there is no BSR line at all.

[root@VM2]~# pimctl -p show rp
_______________________________________________________________________________
PIM Rendez-Vous Point Set Table
===============================================================================
Group Address     RP Address       Prio  Holdtime  Type
===============================================================================
232.0.0.0/8       169.254.0.1         1   Forever  Static

The PIM adjacency between the two routers is built exactly as in sparse mode:

[root@VM2]~# pimctl -p show neighbor
_______________________________________________________________________________
PIM Neighbor Table
==================================================================================
Interface         Address            Priority  Mode  Uptime/Expires
==================================================================================
vtnet1            10.0.23.3                 1  DR    0h0m7s/0h1m40s

Testing

1. Start the receiver on VM4

iperf joins a source-specific group with -H (--ssm-host), which makes it send an IGMPv3 include-mode report instead of a plain any-source join:

[root@VM4]~# iperf -s -u -B 232.1.1.1%vtnet2 -H 10.0.12.1 -i 1
------------------------------------------------------------
Server listening on UDP port 5001
Joining multicast (S,G)=10.0.12.1,232.1.1.1 w/iface vtnet2
Server set to single client traffic mode (per multicast receive)
UDP buffer size: 41.1 KByte (default)
------------------------------------------------------------

The kernel confirms the membership is in include mode, that is, restricted to a source list:

[root@VM4]~# ifmcstat -i vtnet2 -f inet
                group 232.1.1.1 mode include
                        mcast-macaddr 01:00:5e:01:01:01

2. Check that VM3 learns the (S,G) request

VM3 records which source the receiver asked for, and creates the (S,G) route immediately, before any traffic exists. There is no WC RP entry at any point:

[root@VM3]~# pimctl -p show igmp groups
_______________________________________________________________________________
IGMP Group Membership Table
===============================================================================
Interface         Group            Source           Last Reported    Timeout
===============================================================================
vtnet2            232.1.1.1        10.0.12.1        10.0.34.4            385

[root@VM3]~# pimctl -p show mrt
_______________________________________________________________________________
Multicast Routing Table
===============================================================================
Source            Group            RP Address       Flags
===============================================================================
10.0.12.1         232.1.1.1        SSM              KAT SG

Number of Groups        : 1
Number of Cache MIRRORs : 0

The RP Address column reads SSM instead of an address: the tree is rooted at the source, not at a rendezvous point.

3. Start the multicast generator on VM1

-B on the client selects which of the two VM1 addresses is used as source:

[root@VM1]~# iperf -c 232.1.1.1 -u -T 32 -t 600 -i 1 -B 10.0.12.1
------------------------------------------------------------
Client connecting to 232.1.1.1, UDP port 5001
Sending 1470 byte datagrams, IPG target: 0.00 us (kalman adjust)
UDP buffer size: 9.00 KByte (default)
------------------------------------------------------------
[  1] local 10.0.12.1 port 58083 connected with 232.1.1.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  1] 0.00-1.00 sec   129 KBytes  1.06 Mbits/sec
[  1] 1.00-2.00 sec   128 KBytes  1.05 Mbits/sec

The receiver gets the flow without loss:

[  1] local 232.1.1.1 port 5001 connected with 10.0.12.1 port 58083
[ ID] Interval       Transfer     Bandwidth        Jitter   Lost/Total Datagrams
[  1] 0.00-1.00 sec   129 KBytes  1.06 Mbits/sec   0.051 ms 0/90 (0%)
[  1] 1.00-2.00 sec   128 KBytes  1.05 Mbits/sec   0.067 ms 0/89 (0%)
[  1] 2.00-3.00 sec   128 KBytes  1.05 Mbits/sec   0.064 ms 0/89 (0%)
[  1] 3.00-4.00 sec   128 KBytes  1.05 Mbits/sec   0.033 ms 0/89 (0%)

Both routers forward on the shortest-path tree:

[root@VM2]~# pimctl -p show mrt
_______________________________________________________________________________
Multicast Routing Table
===============================================================================
Source            Group            RP Address       Flags
===============================================================================
10.0.12.1         232.1.1.1        SSM              SPT CACHE ASSERTED SG

Number of Groups        : 1
Number of Cache MIRRORs : 1

[root@VM3]~# netstat -g

IPv4 Virtual Interface Table
 Vif   Thresh   Local-Address   Remote-Address    Pkts-In   Pkts-Out
  0         1   10.0.23.3                               0          0
  1         1   10.0.23.3                            1368          0
  2         1   10.0.34.3                               0       1368

IPv4 Multicast Forwarding Table
 Origin          Group             Packets In-Vif  Out-Vifs:Ttls
 10.0.12.1       232.1.1.1            1368    1    2:1

Compared to the PIM-SM lab, there is no registration step and no switchover: the traffic takes the source tree from the first packet.

4. An any-source join in the SSM range receives nothing

Start the same listener without -H, so it sends a plain any-source report, and start the source. Nothing arrives:

[root@VM4]~# iperf -s -u -B 232.1.1.1%vtnet2 -i 1
------------------------------------------------------------
Server listening on UDP port 5001
Joining multicast (*,G)=*,232.1.1.1 w/iface vtnet2
Server set to single client traffic mode (per multicast receive)
UDP buffer size: 41.1 KByte (default)
------------------------------------------------------------

VM3 ignores the report, as required by RFC 4604, so no route is created and nothing is pulled from the source:

[root@VM3]~# pimctl -p show igmp groups
_______________________________________________________________________________
IGMP Group Membership Table
===============================================================================
Interface         Group            Source           Last Reported    Timeout
===============================================================================

[root@VM3]~# pimctl -p show mrt
_______________________________________________________________________________
Multicast Routing Table
===============================================================================
Source            Group            RP Address       Flags
===============================================================================

Number of Groups        : 0
Number of Cache MIRRORs : 0

Check this test from a clean state

An (S,G) entry created by a previous source-specific join survives the receiver leaving, for the duration of its keepalive timer (the KAT flag), and keeps the traffic flowing to the receiver LAN in the meantime. Restart pimd on both routers, or wait for the entry to disappear from pimctl show mrt, before concluding that the any-source join is what delivered traffic.

5. Only the requested source is forwarded

With the receiver joined to (10.0.12.1, 232.1.1.1), start a second generator on VM1 using the decoy address, toward the same group:

[root@VM1]~# iperf -c 232.1.1.1 -u -T 32 -t 600 -i 1 -B 10.0.12.11
[  1] local 10.0.12.11 port 34059 connected with 232.1.1.1 port 5001

VM2, which is on the source LAN, sees both sources but only forwards the one a receiver asked for. The decoy has no outgoing interface (In-Vif 65535, zero packets forwarded):

[root@VM2]~# pimctl -p show mrt
_______________________________________________________________________________
Multicast Routing Table
===============================================================================
Source            Group            RP Address       Flags
===============================================================================
10.0.12.1         232.1.1.1        SSM              SPT CACHE ASSERTED SG
10.0.12.11        232.1.1.1        SSM              SG

Number of Groups        : 1
Number of Cache MIRRORs : 1

[root@VM2]~# netstat -g
IPv4 Virtual Interface Table
 Vif   Thresh   Local-Address   Remote-Address    Pkts-In   Pkts-Out
  0         1   10.0.12.2                               0          0
  1         1   10.0.12.2                            3969          0
  2         1   10.0.23.2                               0       3969

IPv4 Multicast Forwarding Table
 Origin          Group             Packets In-Vif  Out-Vifs:Ttls
 10.0.12.11      232.1.1.1               0  65535
 10.0.12.1       232.1.1.1            3969    1    2:1

On the receiver LAN, only the subscribed source is seen on the wire:

[root@VM4]~# tcpdump -pni vtnet2 -c 4 host 232.1.1.1
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on vtnet2, link-type EN10MB (Ethernet), snapshot length 262144 bytes
01:11:05.205695 IP 10.0.12.1.16264 > 232.1.1.1.5001: UDP, length 1470
01:11:05.216791 IP 10.0.12.1.16264 > 232.1.1.1.5001: UDP, length 1470
01:11:05.227738 IP 10.0.12.1.16264 > 232.1.1.1.5001: UDP, length 1470
01:11:05.238751 IP 10.0.12.1.16264 > 232.1.1.1.5001: UDP, length 1470
4 packets captured
10 packets received by filter
0 packets dropped by kernel

The decoy traffic never leaves the source LAN: with SSM, a source that nobody subscribed to costs the rest of the network nothing.