diff options
author | Dumitru Ceara <dceara@redhat.com> | 2019-07-15 22:25:03 +0200 |
---|---|---|
committer | Ben Pfaff <blp@ovn.org> | 2019-07-16 14:46:28 -0700 |
commit | 605535f9adf2e881aa6242b19bcec0c90ba8ad96 (patch) | |
tree | bc28182d0c36e51effc2018bc76313a727669f14 /tests | |
parent | c89f5f171a0a189137af46c9cf7f132e27a48ca9 (diff) | |
download | openvswitch-605535f9adf2e881aa6242b19bcec0c90ba8ad96.tar.gz |
OVN: Add ovn-northd IGMP support
New IP Multicast Snooping Options are added to the Northbound DB
Logical_Switch:other_config column. These allow enabling IGMP snooping and
querier on the logical switch and get translated by ovn-northd to rows in
the IP_Multicast Southbound DB table.
ovn-northd monitors for changes done by ovn-controllers in the Southbound DB
IGMP_Group table. Based on the entries in IGMP_Group ovn-northd creates
Multicast_Group entries in the Southbound DB, one per IGMP_Group address X,
containing the list of logical switch ports (aggregated from all controllers)
that have IGMP_Group entries for that datapath and address X. ovn-northd
also creates a logical flow that matches on IP multicast traffic destined
to address X and outputs it on the tunnel key of the corresponding
Multicast_Group entry.
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
Acked-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/ovn.at | 270 | ||||
-rw-r--r-- | tests/system-ovn.at | 119 |
2 files changed, 389 insertions, 0 deletions
diff --git a/tests/ovn.at b/tests/ovn.at index fb05f8ba4..cb380d275 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -14430,3 +14430,273 @@ AT_CHECK([ovn-sbctl get controller_event $uuid seq_num], [0], [dnl OVN_CLEANUP([hv1], [hv2]) AT_CLEANUP + +AT_SETUP([ovn -- IGMP snoop/querier]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +# Logical network: +# Two independent logical switches (sw1 and sw2). +# sw1: +# - subnet 10.0.0.0/8 +# - 2 ports bound on hv1 (sw1-p11, sw1-p12) +# - 2 ports bound on hv2 (sw1-p21, sw1-p22) +# sw2: +# - subnet 20.0.0.0/8 +# - 1 port bound on hv1 (sw2-p1) +# - 1 port bound on hv2 (sw2-p2) +# - IGMP Querier from 20.0.0.254 + +reset_pcap_file() { + local iface=$1 + local pcap_file=$2 + ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ +options:rxq_pcap=dummy-rx.pcap + rm -f ${pcap_file}*.pcap + ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ +options:rxq_pcap=${pcap_file}-rx.pcap +} + +ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" +} + +# +# send_igmp_v3_report INPORT HV ETH_SRC IP_SRC IP_CSUM GROUP REC_TYPE +# IGMP_CSUM OUTFILE +# +# This shell function causes an IGMPv3 report to be received on INPORT of HV. +# The packet's content has Ethernet destination 01:00:5E:00:00:22 and source +# ETH_SRC (exactly 12 hex digits). Ethernet type is set to IP. +# GROUP is the IP multicast group to be joined/to leave (based on REC_TYPE). +# REC_TYPE == 04: join GROUP +# REC_TYPE == 03: leave GROUP +# The packet hexdump is also stored in OUTFILE. +# +send_igmp_v3_report() { + local inport=$1 hv=$2 eth_src=$3 ip_src=$4 ip_chksum=$5 group=$6 + local rec_type=$7 igmp_chksum=$8 outfile=$9 + + local eth_dst=01005e000016 + local ip_dst=$(ip_to_hex 224 0 0 22) + local ip_ttl=01 + local ip_ra_opt=94040000 + + local igmp_type=2200 + local num_rec=00000001 + local aux_dlen=00 + local num_src=0000 + + local eth=${eth_dst}${eth_src}0800 + local ip=46c0002800004000${ip_ttl}02${ip_chksum}${ip_src}${ip_dst}${ip_ra_opt} + local igmp=${igmp_type}${igmp_chksum}${num_rec}${rec_type}${aux_dlen}${num_src}${group} + local packet=${eth}${ip}${igmp} + + echo ${packet} >> ${outfile} + as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet} +} + +# +# store_igmp_v3_query ETH_SRC IP_SRC IP_CSUM OUTFILE +# +# This shell function builds an IGMPv3 general query from ETH_SRC and IP_SRC +# and stores the hexdump of the packet in OUTFILE. +# +store_igmp_v3_query() { + local eth_src=$1 ip_src=$2 ip_chksum=$3 outfile=$4 + + local eth_dst=01005e000001 + local ip_dst=$(ip_to_hex 224 0 0 1) + local ip_ttl=01 + local igmp_type=11 + local max_resp=0a + local igmp_chksum=eeeb + local addr=00000000 + + local eth=${eth_dst}${eth_src}0800 + local ip=4500002000004000${ip_ttl}02${ip_chksum}${ip_src}${ip_dst} + local igmp=${igmp_type}${max_resp}${igmp_chksum}${addr}000a0000 + local packet=${eth}${ip}${igmp} + + echo ${packet} >> ${outfile} +} + +# +# send_ip_multicast_pkt INPORT HV ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN +# IP_PROTO DATA OUTFILE +# +# This shell function causes an IP multicast packet to be received on INPORT +# of HV. +# The hexdump of the packet is stored in OUTFILE. +# +send_ip_multicast_pkt() { + local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ip_src=$5 ip_dst=$6 + local ip_len=$7 ip_chksum=$8 proto=$9 data=${10} outfile=${11} + + local ip_ttl=20 + + local eth=${eth_dst}${eth_src}0800 + local ip=450000${ip_len}95f14000${ip_ttl}${proto}${ip_chksum}${ip_src}${ip_dst} + local packet=${eth}${ip}${data} + + as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet} + echo ${packet} >> ${outfile} +} + +ovn-nbctl ls-add sw1 +ovn-nbctl ls-add sw2 + +ovn-nbctl lsp-add sw1 sw1-p11 +ovn-nbctl lsp-add sw1 sw1-p12 +ovn-nbctl lsp-add sw1 sw1-p21 +ovn-nbctl lsp-add sw1 sw1-p22 +ovn-nbctl lsp-add sw2 sw2-p1 +ovn-nbctl lsp-add sw2 sw2-p2 + +net_add n1 +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=sw1-p11 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 +ovs-vsctl -- add-port br-int hv1-vif2 -- \ + set interface hv1-vif2 external-ids:iface-id=sw1-p12 \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap \ + ofport-request=1 +ovs-vsctl -- add-port br-int hv1-vif3 -- \ + set interface hv1-vif3 external-ids:iface-id=sw2-p1 \ + options:tx_pcap=hv1/vif3-tx.pcap \ + options:rxq_pcap=hv1/vif3-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=sw1-p21 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 +ovs-vsctl -- add-port br-int hv2-vif2 -- \ + set interface hv2-vif2 external-ids:iface-id=sw1-p22 \ + options:tx_pcap=hv2/vif2-tx.pcap \ + options:rxq_pcap=hv2/vif2-rx.pcap \ + ofport-request=1 +ovs-vsctl -- add-port br-int hv2-vif3 -- \ + set interface hv2-vif3 external-ids:iface-id=sw2-p2 \ + options:tx_pcap=hv2/vif3-tx.pcap \ + options:rxq_pcap=hv2/vif3-rx.pcap \ + ofport-request=1 + +OVN_POPULATE_ARP + +# Enable IGMP snooping on sw1. +ovn-nbctl set Logical_Switch sw1 other_config:mcast_querier="false" +ovn-nbctl set Logical_Switch sw1 other_config:mcast_snoop="true" + +# No IGMP query should be generated by sw1 (mcast_querier="false"). +truncate -s 0 expected +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected]) +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) +OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected]) + +ovn-nbctl --wait=hv sync + +# Inject IGMP Join for 239.0.1.68 on sw1-p11. +send_igmp_v3_report hv1-vif1 hv1 \ + 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ + $(ip_to_hex 239 0 1 68) 04 e9b9 \ + /dev/null +# Inject IGMP Join for 239.0.1.68 on sw1-p21. +send_igmp_v3_report hv2-vif1 hv2 000000000002 $(ip_to_hex 10 0 0 2) f9f9 \ + $(ip_to_hex 239 0 1 68) 04 e9b9 \ + /dev/null + +# Check that the IGMP Group is learned on both hv. +OVS_WAIT_UNTIL([ + total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` + test "${total_entries}" = "2" +]) + +# Send traffic and make sure it gets forwarded only on the two ports that +# joined. +truncate -s 0 expected +truncate -s 0 expected_empty +send_ip_multicast_pkt hv1-vif2 hv1 \ + 000000000001 01005e000144 \ + $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \ + e518e518000a3b3a0000 \ + expected + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected]) +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty]) +OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) +OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty]) +OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) + +# Inject IGMP Leave for 239.0.1.68 on sw1-p11. +send_igmp_v3_report hv1-vif1 hv1 \ + 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ + $(ip_to_hex 239 0 1 68) 03 eab9 \ + /dev/null + +# Check IGMP_Group table on both HV. +OVS_WAIT_UNTIL([ + total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` + test "${total_entries}" = "1" +]) + +# Send traffic traffic and make sure it gets forwarded only on the port that +# joined. +as hv1 reset_pcap_file hv1-vif1 hv1/vif1 +as hv2 reset_pcap_file hv2-vif1 hv2/vif1 +truncate -s 0 expected +truncate -s 0 expected_empty +send_ip_multicast_pkt hv1-vif2 hv1 \ + 000000000001 01005e000144 \ + $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \ + e518e518000a3b3a0000 \ + expected + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty]) +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty]) +OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) +OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty]) +OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) + +# Flush IGMP groups. +ovn-sbctl ip-multicast-flush sw1 +ovn-nbctl --wait=hv -t 3 sync +OVS_WAIT_UNTIL([ + total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` + test "${total_entries}" = "0" +]) + +# Enable IGMP snooping and querier on sw2 and set query interval to minimum. +ovn-nbctl set Logical_Switch sw2 \ + other_config:mcast_snoop="true" \ + other_config:mcast_querier="true" \ + other_config:mcast_query_interval=1 \ + other_config:mcast_eth_src="00:00:00:00:02:fe" \ + other_config:mcast_ip4_src="20.0.0.254" + +# Wait for 1 query interval (1 sec) and check that two queries are generated. +truncate -s 0 expected +store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected +store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected + +sleep 1 +OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected]) +OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected]) + +OVN_CLEANUP([hv1], [hv2]) +AT_CLEANUP diff --git a/tests/system-ovn.at b/tests/system-ovn.at index b7e2d779d..10fbd2649 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -1542,3 +1542,122 @@ as OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP + +AT_SETUP([ovn -- 2 LSs IGMP]) +AT_KEYWORDS([ovnigmp]) + +ovn_start + +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +# Set external-ids in br-int needed for ovn-controller +ovs-vsctl \ + -- set Open_vSwitch . external-ids:system-id=hv1 \ + -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true + +# Start ovn-controller +start_daemon ovn-controller + +# Logical network: +# Two independent logical switches (sw1 and sw2). +# sw1: +# - subnet 10.0.0.0/8 +# - 2 ports (sw1-p1 - sw1-p2) +# sw2: +# - subnet 20.0.0.0/8 +# - 2 port (sw2-p1 - sw2-p2) +# - IGMP Querier from 20.0.0.254 + +ovn-nbctl ls-add sw1 +ovn-nbctl ls-add sw2 + +for i in `seq 1 2` +do + ADD_NAMESPACES(sw1-p$i) + ADD_VETH(sw1-p$i, sw1-p$i, br-int, "10.0.0.$i/24", "00:00:00:00:01:0$i", \ + "10.0.0.254") + ovn-nbctl lsp-add sw1 sw1-p$i \ + -- lsp-set-addresses sw1-p$i "00:00:00:00:01:0$i 10.0.0.$i" +done + +for i in `seq 1 2` +do + ADD_NAMESPACES(sw2-p$i) + ADD_VETH(sw2-p$i, sw2-p$i, br-int, "20.0.0.$i/24", "00:00:00:00:02:0$i", \ + "20.0.0.254") + ovn-nbctl lsp-add sw2 sw2-p$i \ + -- lsp-set-addresses sw2-p$i "00:00:00:00:02:0$i 20.0.0.$i" +done + +# Enable IGMP snooping on sw1. +ovn-nbctl set Logical_Switch sw1 other_config:mcast_querier="false" +ovn-nbctl set Logical_Switch sw1 other_config:mcast_snoop="true" + +# Inject IGMP Join for 239.0.1.68 on sw1-p1. +NS_CHECK_EXEC([sw1-p1], [ip addr add dev sw1-p1 239.0.1.68/32 autojoin], [0]) + +# Inject IGMP Join for 239.0.1.68 on sw1-p2 +NS_CHECK_EXEC([sw1-p2], [ip addr add dev sw1-p2 239.0.1.68/32 autojoin], [0]) + +# Check that the IGMP Group is learned. +OVS_WAIT_UNTIL([ + total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` + ports=`ovn-sbctl find IGMP_Group | grep ports | cut -f 2 -d ":" | wc -w` + test "${total_entries}" = "1" + test "${ports}" = "2" +]) + +# Inject IGMP Leave for 239.0.1.68 on sw1-p2. +NS_CHECK_EXEC([sw1-p2], [ip addr del dev sw1-p2 239.0.1.68/32], [0]) + +# Check that only one port is left in the group. +OVS_WAIT_UNTIL([ + total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` + ports=`ovn-sbctl find IGMP_Group | grep ports | cut -f 2 -d ":" | wc -w` + test "${total_entries}" = "1" + test "${ports}" = "1" +]) + +# Flush IGMP groups. +ovn-sbctl ip-multicast-flush sw1 +ovn-nbctl --wait=hv -t 3 sync +OVS_WAIT_UNTIL([ + total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` + test "${total_entries}" = "0" +]) + +# Enable IGMP snooping and querier on sw2 and set query interval to minimum. +ovn-nbctl set Logical_Switch sw2 \ + other_config:mcast_snoop="true" \ + other_config:mcast_querier="true" \ + other_config:mcast_query_interval=1 \ + other_config:mcast_eth_src="00:00:00:00:02:fe" \ + other_config:mcast_ip4_src="20.0.0.254" + +# Check that queries are generated. +NS_CHECK_EXEC([sw2-p1], [tcpdump -n -c 2 -i sw2-p1 igmp > sw2-p1.pcap &]) + +OVS_WAIT_UNTIL([ + total_queries=`cat sw2-p1.pcap | grep "igmp query" | wc -l` + test "${total_queries}" = "2" +]) + +OVS_APP_EXIT_AND_WAIT([ovn-controller]) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d +/connection dropped.*/d"]) +AT_CLEANUP |