Skip to content

Instantly share code, notes, and snippets.

@sassyn
Forked from kntyskw/ec2_multicast.sh
Created June 8, 2021 14:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sassyn/9d1ef5811d7672e9138ebdd8fb46d428 to your computer and use it in GitHub Desktop.
Save sassyn/9d1ef5811d7672e9138ebdd8fb46d428 to your computer and use it in GitHub Desktop.
Script to enable IP multicast without using Ethernet broadcast. It uses tc mirred and pedit actions to copy and edit an IP multicast packet to send over multiple Ethernet unicast frames. It requires two network interfaces to work. One is the interface to grab original multicast packets from and the other is to send out modified packets. This is …
#!/bin/sh
[[ -n "$1" && -n "$2" ]] || { echo "Usage: $0 <interface to grab multicast packets from> <interface to send modified packets to> [target MAC address 1] [target MAC address 2] ..."; exit 0 ; }
IIF=$1
OIF=$2
shift
shift
SRC_MACADDR=`ip link show $OIF | awk '/ether/ {print $2}' | tr -d :`
HANDLE=10
TC=tc
BASE_PRIO=100
# File which stores the priority of the tc filter that is to be registered
PRIO_FILE=/var/run/ec2_broadcastd_tc_prio
# Load the priority that the previous filter is registered with
if [ -r $PRIO_FILE ]; then
BASE_PRIO=`cat $PRIO_FILE`
fi
# Determine the priority that the new filter will be registered with
# (Any number which is different from the previous one is fine unless
# other filters exist.)
if [ `expr $BASE_PRIO % 2` -eq 1 ]; then
PRIO=`expr $BASE_PRIO + 1`
else
PRIO=`expr $BASE_PRIO - 1`
fi
# Create queue discipline for the origin network interface
$TC qdisc add dev $IIF root handle ${HANDLE}: prio >& /dev/null
# Calculate bit mask for modifying packet ID. (We will send duplicate IP packets.
# In order not to break underlying packet fragmentation and reconstruction,
# each IP packet needs to have different ID)
RETAIN_MASK=0xfff0
# Create the list of actions to apply to outgoing multicast packets
i=0
for DST_MACADDR in $*
do
DST_MACADDR=`echo $DST_MACADDR | tr -d :`
if [ "${actions}X" != "X" ]; then
# Action to mirror the filtered packet to the outgoing interface
actions="$actions action mirred egress mirror dev $OIF "
fi
# Actions to modify Ethernet header
# Offset 0 is the beginning of the IP header
# Offset has to be factor of 4
# Last a few bits of the packet ID are modified to make each packet unique
actions="$actions action pedit
munge offset -16 u32 set 0x0000`echo $DST_MACADDR | cut -c 1-4`
munge offset -12 u32 set 0x`echo $DST_MACADDR | cut -c 5-12`
munge offset -8 u32 set 0x`echo $SRC_MACADDR | cut -c 1-8`
munge offset 4 u16 set 0x`printf "%04x" $i` retain $RETAIN_MASK pipe "
# Recalculate IP header checksum because we have modified the packet ID field
actions="$actions action csum ip4h pipe "
i=`expr $i + 1`
done
# Action to redirect the filtered packet to the outgoing interface
# (Note that this suppresses to send out the original frame with
# the broadcast address.)
actions="$actions action mirred egress redirect dev $OIF"
# Register the filter that matches ethernet broadcast frames which
# carry IP packets addressed to class D (IP multicast)
$TC filter add dev $IIF parent ${HANDLE}: protocol ip prio $PRIO u32 \
match u8 0x01 0x01 at 0 match u8 0xe0 0xe0 at 16 $actions
# Delete the old filter if exists
if [ -r $PRIO_FILE ]; then
$TC filter del dev $IIF parent ${HANDLE}: prio $BASE_PRIO
fi
# Store the priority used for the filter for future reference
echo $PRIO > $PRIO_FILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment