From 57d143eb6f44bd941a448b1f498dc0decdfca8bf Mon Sep 17 00:00:00 2001 From: Han Zhou Date: Mon, 30 Nov 2015 11:42:46 -0800 Subject: [PATCH] ovn: support ARP response for known IPs For lswitch ports with known IPs, ARP is responded directly from local ovn-controller to avoid flooding. Signed-off-by: Han Zhou Signed-off-by: Ben Pfaff --- ovn/northd/ovn-northd.8.xml | 18 ++++++++++ ovn/northd/ovn-northd.c | 38 ++++++++++++++++++++ tests/ovn.at | 72 +++++++++++++++++++++++++++++++++---- 3 files changed, 122 insertions(+), 6 deletions(-) diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index 865c727d2..fa7675b60 100644 --- a/ovn/northd/ovn-northd.8.xml +++ b/ovn/northd/ovn-northd.8.xml @@ -203,6 +203,24 @@

    +
  • + Priority-150 flows that matches ARP requests to each known IP address + A of logical port P, and respond ARP replies + directly with corresponding Ethernet address E: +
    +eth.dst = eth.src;
    +eth.src = E;
    +arp.op = 2; /* ARP reply. */
    +arp.tha = arp.sha;
    +arp.sha = E;
    +arp.tpa = arp.spa;
    +arp.spa = A;
    +outport = P;
    +inport = ""; /* Allow sending out inport. */
    +output;
    +        
    +
  • +
  • A priority-100 flow that outputs all packets with an Ethernet broadcast or multicast eth.dst to the MC_FLOOD diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 7f3a92e35..270b116a7 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -1145,6 +1145,44 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, ds_destroy(&match); } + /* Ingress table 3: Destination lookup, ARP reply for known IPs. + * (priority 150). */ + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbs) { + continue; + } + + for (size_t i = 0; i < op->nbs->n_addresses; i++) { + struct eth_addr ea; + ovs_be32 ip; + + if (ovs_scan(op->nbs->addresses[i], + ETH_ADDR_SCAN_FMT" "IP_SCAN_FMT, + ETH_ADDR_SCAN_ARGS(ea), IP_SCAN_ARGS(&ip))) { + char *match = xasprintf( + "arp.tpa == "IP_FMT" && arp.op == 1", IP_ARGS(ip)); + char *actions = xasprintf( + "eth.dst = eth.src; " + "eth.src = "ETH_ADDR_FMT"; " + "arp.op = 2; /* ARP reply */ " + "arp.tha = arp.sha; " + "arp.sha = "ETH_ADDR_FMT"; " + "arp.tpa = arp.spa; " + "arp.spa = "IP_FMT"; " + "outport = inport; " + "inport = \"\"; /* Allow sending out inport. */ " + "output;", + ETH_ADDR_ARGS(ea), + ETH_ADDR_ARGS(ea), + IP_ARGS(ip)); + ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 150, + match, actions); + free(match); + free(actions); + } + } + } + /* Ingress table 3: Destination lookup, broadcast and multicast handling * (priority 100). */ HMAP_FOR_EACH (op, key_node, ports) { diff --git a/tests/ovn.at b/tests/ovn.at index c34d30d55..e00674de1 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -516,6 +516,7 @@ AT_BANNER([OVN end-to-end tests]) # 3 hypervisors, one logical switch, 3 logical ports per hypervisor AT_SETUP([ovn -- 3 HVs, 1 LS, 3 lports/HV]) +AT_KEYWORDS([ovnarp]) AT_SKIP_IF([test $HAVE_PYTHON = no]) ovn_start @@ -537,9 +538,9 @@ for i in 1 2 3; do ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j ovn-nbctl lport-add lsw0 lp$i$j if test $j = 1; then - ovn-nbctl lport-set-addresses lp$i$j f0:00:00:00:00:$i$j unknown + ovn-nbctl lport-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown else - ovn-nbctl lport-set-addresses lp$i$j f0:00:00:00:00:$i$j + ovn-nbctl lport-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" ovn-nbctl lport-set-port-security lp$i$j f0:00:00:00:00:$i$j fi done @@ -558,6 +559,12 @@ ovn_populate_arp sleep 1 ovn-sbctl dump-flows -- list multicast_group +# Given the name of a logical port, prints the name of the hypervisor +# on which it is located. +vif_to_hv() { + echo hv${1%?} +} + # test_packet INPORT DST SRC ETHTYPE OUTPORT... # # This shell function causes a packet to be received on INPORT. The packet's @@ -575,7 +582,7 @@ for i in 1 2 3; do done test_packet() { local inport=$1 packet=$2$3$4; shift; shift; shift; shift - hv=hv`echo $inport | sed 's/^\(.\).*/\1/'` + hv=`vif_to_hv $inport` vif=vif$inport as $hv ovs-appctl netdev-dummy/receive $vif $packet for outport; do @@ -583,6 +590,44 @@ test_packet() { done } +# test_arp INPORT SHA SPA TPA [REPLY_HA] +# +# Causes a packet to be received on INPORT. The packet is an ARP +# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then +# it should be the hardware address of the target to expect to receive in an +# ARP reply; otherwise no reply is expected. +# +# INPORT is an lport number, e.g. 11 for vif11. +# SHA and REPLY_HA are each 12 hex digits. +# SPA and TPA are each 8 hex digits. +test_arp() { + local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 + local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} + hv=`vif_to_hv $inport` + as $hv ovs-appctl netdev-dummy/receive vif$inport $request + + if test X$reply_ha == X; then + # Expect to receive the broadcast ARP on the other logical switch ports + # if no reply is expected. + local i j + for i in 1 2 3; do + for j in 1 2 3; do + if test $i$j != $inport; then + echo $request >> $i$j.expected + fi + done + done + else + # Expect to receive the reply, if any. + local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa} + echo $reply >> $inport.expected + fi +} + +ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" +} + # Send packets between all pairs of source and destination ports: # # 1. Unicast packets are delivered to exactly one lport (except that packets @@ -605,6 +650,10 @@ test_packet() { # 7. The lswitch drops unicast packets that violate an ACL. # # 8. The lswitch drops multicast and broadcast packets that violate an ACL. +# +# 9. ARP requests to known IPs are responded directly. +# +# 10. No response to ARP requests for unknown IPs. for is in 1 2 3; do for js in 1 2 3; do s=$is$js @@ -641,6 +690,12 @@ for is in 1 2 3; do bcast="$bcast $unicast" bacl2="$bacl2 $acl2" bacl3="$bacl3 $acl3" + + sip=`ip_to_hex 192 168 0 $i$j` + tip=`ip_to_hex 192 168 0 $id$jd` + tip_unknown=`ip_to_hex 11 11 11 11` + test_arp $s f000000000$s $sip $tip f000000000$d #9 + test_arp $s f000000000$s $sip $tip_unknown #10 done done @@ -883,6 +938,9 @@ for i in 1 2 3; do done done +ovn-nbctl set Logical_Port lrp33-attachment \ + addresses='"00:00:00:00:ff:33 192.168.33.254"' + # Physical network: # # Three hypervisors hv[123]. @@ -1041,13 +1099,15 @@ test_arp() { as $hv ovs-appctl netdev-dummy/receive vif$inport $request #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request - # Expect to receive the broadcast ARP on the other logical switch ports. - # (OVN should probably suppress these.) + # Expect to receive the broadcast ARP on the other logical switch ports if + # IP address is not configured to the lswitch patch port. local i=`vif_to_ls $inport` local j k for j in 1 2 3; do for k in 1 2 3; do - if test $i$j$k != $inport; then + # 192.168.33.254 is configured to the lswtich patch port for lrp33, + # so no ARP flooding expected for it. + if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then echo $request >> $i$j$k.expected fi done -- 2.20.1