From patchwork Wed Nov 18 16:27:39 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 546113 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (unknown [IPv6:2600:3c00::f03c:91ff:fe6e:bdf7]) by ozlabs.org (Postfix) with ESMTP id D2257141466 for ; Thu, 19 Nov 2015 03:27:51 +1100 (AEDT) Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id 03B3A10312; Wed, 18 Nov 2015 08:27:51 -0800 (PST) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx1e3.cudamail.com (mx1.cudamail.com [69.90.118.67]) by archives.nicira.com (Postfix) with ESMTPS id 2B95D10300 for ; Wed, 18 Nov 2015 08:27:50 -0800 (PST) Received: from bar5.cudamail.com (localhost [127.0.0.1]) by mx1e3.cudamail.com (Postfix) with ESMTPS id 27BD7420310 for ; Wed, 18 Nov 2015 09:27:49 -0700 (MST) X-ASG-Debug-ID: 1447864067-09eadd479c81c60001-byXFYA Received: from mx1-pf1.cudamail.com ([192.168.24.1]) by bar5.cudamail.com with ESMTP id qISzDZdEmHleAIIm (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 18 Nov 2015 09:27:47 -0700 (MST) X-Barracuda-Envelope-From: nusiddiq@redhat.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.24.1 Received: from unknown (HELO mx1.redhat.com) (209.132.183.28) by mx1-pf1.cudamail.com with ESMTPS (DHE-RSA-AES256-SHA encrypted); 18 Nov 2015 16:27:46 -0000 Received-SPF: pass (mx1-pf1.cudamail.com: SPF record at _spf1.redhat.com designates 209.132.183.28 as permitted sender) X-Barracuda-Apparent-Source-IP: 209.132.183.28 X-Barracuda-RBL-IP: 209.132.183.28 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (Postfix) with ESMTPS id 4F19AC0BB281 for ; Wed, 18 Nov 2015 16:27:45 +0000 (UTC) Received: from nusiddiq.blr.redhat.com (dhcp-0-24.blr.redhat.com [10.70.1.24]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id tAIGRe7S022348 for ; Wed, 18 Nov 2015 11:27:42 -0500 X-CudaMail-Envelope-Sender: nusiddiq@redhat.com From: Numan Siddique X-CudaMail-MID: CM-E1-1117039646 X-CudaMail-DTE: 111815 X-CudaMail-Originating-IP: 209.132.183.28 To: dev@openvswitch.org X-ASG-Orig-Subj: [##CM-E1-1117039646##][ovs-dev][PATCH v2 4/4] ovn: Add tests for ovn dhcp Organization: Red Hat Message-ID: <564CA6FB.9040503@redhat.com> Date: Wed, 18 Nov 2015 21:57:39 +0530 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-GBUdb-Analysis: 0, 209.132.183.28, Ugly c=0.343385 p=-0.0714286 Source Normal X-MessageSniffer-Rules: 0-0-0-25539-c X-Barracuda-Connect: UNKNOWN[192.168.24.1] X-Barracuda-Start-Time: 1447864067 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-BRTS-Status: 1 X-Barracuda-Spam-Score: 0.60 X-Barracuda-Spam-Status: No, SCORE=0.60 using per-user scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC5_MJ1963, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.24509 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Subject: [ovs-dev] [PATCH v2 4/4] ovn: Add tests for ovn dhcp X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@openvswitch.org Sender: "dev" Signed-off-by: Numan Siddique --- tests/automake.mk | 1 + tests/ovn.at | 184 +++++++++++++++++++++++++++++++++++++++++++ tests/test-ovn-dhcp.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+) create mode 100644 tests/test-ovn-dhcp.c diff --git a/tests/automake.mk b/tests/automake.mk index 5267be1..9c19b1e 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -305,6 +305,7 @@ tests_ovstest_SOURCES = \ tests/test-odp.c \ tests/test-ofpbuf.c \ tests/test-ovn.c \ + tests/test-ovn-dhcp.c \ tests/test-packets.c \ tests/test-random.c \ tests/test-reconnect.c \ diff --git a/tests/ovn.at b/tests/ovn.at index 68fcc9a..34b9913 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1104,3 +1104,187 @@ for i in 1 2 3; do done done AT_CLEANUP + +AT_SETUP([ovn dhcp -- 3 HVs, 3 LS, 3 lports/LS, 1 LR]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +# Logical network: +# +# Three logical switches ls1, ls2, ls3. +# One logical router lr0 connected to ls[123], +# with nine subnets, three per logical switch: +# +# lrp11 on ls1 for subnet 192.168.11.0/24 +# lrp12 on ls1 for subnet 192.168.12.0/24 +# lrp13 on ls1 for subnet 192.168.13.0/24 +# ... +# lrp33 on ls3 for subnet 192.168.33.0/24 +# +# 27 VIFs, 9 per LS, 3 per subnet: lp[123][123][123], where the first two +# digits are the subnet and the last digit distinguishes the VIF. +for i in 1 2 3; do + ovn-nbctl lswitch-add ls$i + for j in 1 2 3; do + for k in 1 2 3; do + ovn-nbctl \ + -- lport-add ls$i lp$i$j$k \ + -- lport-set-addresses lp$i$j$k "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k" + done + done +done + +# Physical network: +# +# Three hypervisors hv[123]. +# lp?1[123] spread across hv[123]: lp?11 on hv1, lp?12 on hv2, lp?13 on hv3. +# lp?2[123] spread across hv[23]: lp?21 and lp?22 on hv2, lp?23 on hv3. +# lp?3[123] all on hv3. + + +# Given the name of a logical port, prints the name of the hypervisor +# on which it is located. +vif_to_hv() { + case $1 in dnl ( + ?11) echo 1 ;; dnl ( + ?12 | ?21 | ?22) echo 2 ;; dnl ( + ?13 | ?23 | ?3?) echo 3 ;; + esac +} + +# Given the name of a logical port, prints the name of its logical router +# port, e.g. "vif_to_lrp 123" yields 12. +vif_to_lrp() { + echo ${1%?} +} + +# Given the name of a logical port, prints the name of its logical +# switch, e.g. "vif_to_ls 123" yields 1. +vif_to_ls() { + echo ${1%??} +} + +net_add n1 +for i in 1 2 3; do + sim_add hv$i + as hv$i + ovs-vsctl add-br br-phys + ovn_attach n1 br-phys 192.168.0.$i +done +for i in 1 2 3; do + for j in 1 2 3; do + for k in 1 2 3; do + hv=`vif_to_hv $i$j$k` + as hv$hv ovs-vsctl \ + -- add-port br-int vif$i$j$k \ + -- set Interface vif$i$j$k \ + external-ids:iface-id=lp$i$j$k \ + options:tx_pcap=hv$hv/vif$i$j$k-tx.pcap \ + options:rxq_pcap=hv$hv/vif$i$j$k-rx.pcap \ + ofport-request=$i$j$k + done + done +done + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +ovn_populate_arp + +# Allow some time for ovn-northd and ovn-controller to catch up. +# XXX This should be more systematic. +sleep 1 + +send_dhcp_packet() { + local inport=$1 src_mac=$2 dhcp_type=$3 + local request=ffffffffffff${src_mac}080045100110000000008011000000000000ffffffff + # udp header and dhcp header + request+=0044004300FC0000 + request+=010106006359aa760000000000000000000000000000000000000000${src_mac} + # client hardware padding + request+=00000000000000000000 + # server hostname + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + # boot file name + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + # dhcp magic cookie + request+=63825363 + # dhcp message type + request+=3501${dhcp_type}ff + shift; shift; shift; shift; shift + hv=`vif_to_hv $inport` + as hv$hv ovs-appctl netdev-dummy/receive vif$inport $request +} + +ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" +} + +run_dhcp_test() { + local i=$1 j=$2 k=$3 set_option=$4 dhcp_type=$5 + netmask=255.255.255.0 + gw_ip=0.0.0.0 + if $set_option = 'true'; then + netmask=255.255.252.0 + gw_ip=192.168.$i$j.254 + ovn-nbctl \ + -- lport-set-options lp$i$j$k \ + "dhcp-opt-1=$netmask" "dhcp-opt-3=$gw_ip" + sleep 1 + fi + + echo $gw_ip + echo $netmask + hv=`vif_to_hv $i$j$k` + as hv$hv ovs-ofctl dump-flows br-int + inport=$i$j$k + send_dhcp_packet $inport f00000000$i$j$k $dhcp_type + sleep 2 + if $set_option = 'true'; then + AT_CHECK([ovstest test-ovn-dhcp hv$hv/vif$i$j$k-tx.pcap \ + 192.168.$i$j.$k $netmask $gw_ip $dhcp_type], [0], [ignore]) + else + #dhcp options are not set. So the dhcp packet should be recieved + # by other ports attached to the br-int since its flooded. + # verify if the dhcp packet was flooded or not. + for a in 1 2 3; do + for b in 1 2 3; do + for c in 1 2 3; do + if test $a$b$c = $inport; then + continue + fi + thv=`vif_to_hv $a$b$c` + if test $thv = $hv; then + AT_CHECK([ovstest test-ovn-dhcp hv$hv/vif$a$b$c-tx.pcap], + [0], [ignore]) + fi + done + done + done + + fi +} + +#send DHCP DISCOVER +run_dhcp_test 1 1 1 true 01 + +#send DHCP OFFER +run_dhcp_test 2 1 1 true 03 + +# don not set the dhcp options +# the dhcp packet should be flooded. +run_dhcp_test 1 2 1 false 01 + +# remove the ip address from the port +# the dhcp packet should be flooded. +ovn-nbctl \ + -- lport-set-addresses lp231 "f0:00:00:00:02:31" +sleep 1 + +run_dhcp_test 2 3 1 false 01 + +AT_CLEANUP diff --git a/tests/test-ovn-dhcp.c b/tests/test-ovn-dhcp.c new file mode 100644 index 0000000..3a63b93 --- /dev/null +++ b/tests/test-ovn-dhcp.c @@ -0,0 +1,211 @@ +/* Copyright (c) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "command-line.h" +#include "flow.h" +#include +#include +#include +#include +#include "classifier.h" +#include "dhcp.h" +#include "ofpbuf.h" +#include "ofp-print.h" +#include "ofp-util.h" +#include "openflow/openflow.h" +#include "ovstest.h" +#include "dp-packet.h" +#include "pcap-file.h" +#include "timeval.h" +#include "util.h" +#include "openvswitch/vlog.h" +#include "lib/packets.h" + + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +#define DHCP_MAGIC_COOKIE (uint32_t)0x63825363 +#define DHCP_OPT_MSG_TYPE ((uint8_t)53) + +/* Verify the dhcp option type */ +struct dhcp_option_header { + uint8_t option; + uint8_t len; +}; + +#define OPTION_PAYLOAD(opt) ((char *)opt + sizeof(struct dhcp_option_header)) + +static void +test_ovn_dhcp_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + int retval = 1; + FILE *pcap; + bool verify_response = true; + + if (argc == 2) { + verify_response = false; + } + else if (argc < 6) { + printf("Usage : test_ovn_dhcp_main pcap-file expected ip" + " expected-netmask expected-gw-ip dhcp-reply-type\n"); + exit(1); + } + + + set_program_name(argv[0]); + + pcap = fopen(argv[1], "rb"); + if (!pcap) { + ovs_fatal(errno, "failed to open %s", argv[1]); + } + + retval = ovs_pcap_read_header(pcap); + if (retval) { + ovs_fatal(retval > 0 ? retval : 0, "reading pcap header failed"); + } + + struct dp_packet *packet; + retval = ovs_pcap_read(pcap, &packet, NULL); + if (retval == EOF) { + ovs_fatal(0, "unexpected end of file reading pcap file : [%s]\n", + argv[1]); + } else if (retval) { + ovs_fatal(retval, "error reading pcap file"); + } + + struct flow flow; + flow_extract(packet, &flow); + + if (verify_response) { + if (flow.tp_src != htons(DHCP_SERVER_PORT) && + flow.tp_dst != htons(DHCP_CLIENT_PORT)) { + printf("Error. Not a dhcp response packet \n"); + exit(1); + } + } + else { + if (flow.dl_type == htons(ETH_TYPE_IP) && \ + flow.nw_proto == IPPROTO_UDP && \ + flow.nw_src == INADDR_ANY && \ + flow.nw_dst == INADDR_BROADCAST && \ + flow.tp_src == htons(DHCP_CLIENT_PORT) && \ + flow.tp_dst == htons(DHCP_SERVER_PORT)) { + exit(0); + } + else { + printf("Error.. Not a dhcp discover/request packet \n"); + exit(1); + } + } + /* verify if the dst ip is as expected */ + ovs_be32 expected_offer_ip; + if (!ovs_scan(argv[2], IP_SCAN_FMT, IP_SCAN_ARGS(&expected_offer_ip))) { + ovs_fatal(1, "invalid expected offer ip"); + } + + ovs_be32 expected_netmask; + if (!ovs_scan(argv[3], IP_SCAN_FMT, IP_SCAN_ARGS(&expected_netmask))) { + ovs_fatal(1, "invalid expected netmask"); + } + + ovs_be32 expected_gw_ip; + if (!ovs_scan(argv[4], IP_SCAN_FMT, IP_SCAN_ARGS(&expected_gw_ip))) { + ovs_fatal(1, "invalid expected gw ip"); + } + + if (flow.nw_dst != expected_offer_ip) { + printf("Error. Offered ip : "IP_FMT " : Expected ip : %s\n", + IP_ARGS(flow.nw_dst), argv[2]); + exit(1); + } + + /* verify the dhcp reply type */ + struct dhcp_header const *dhcp_data = dp_packet_get_udp_payload(packet); + if (dhcp_data->op != (uint8_t)2) { + printf("Invalid dhcp op reply code : %d\n", dhcp_data->op); + exit(1); + } + + if(dhcp_data->yiaddr != expected_offer_ip) { + printf("Error. Offered yiaddr : "IP_FMT " : Expected ip : %s\n", + IP_ARGS(dhcp_data->yiaddr), argv[2]); + exit(1); + } + + /* Verify the dhcp option cookie */ + char const *footer = (char *)dhcp_data + sizeof(*dhcp_data); + uint32_t cookie = *(uint32_t *)footer; + if (cookie != htonl(DHCP_MAGIC_COOKIE)) { + printf("Error. Invalid dhcp magic cookie\n"); + exit(1); + } + + footer += sizeof(uint32_t); + struct dhcp_option_header const *opt; + uint8_t dhcp_msg_type = 0; + ovs_be32 netmask = 0; + ovs_be32 gw_ip = 0xffffffff; + + size_t dhcp_data_size = dp_packet_l4_size(packet); + for (opt = (struct dhcp_option_header *)footer; + footer < (char *)dhcp_data + dhcp_data_size; + footer += (sizeof(*opt) + opt->len)) { + opt = (struct dhcp_option_header *)footer; + switch(opt->option) { + case 53: /* DHCP OPT MESSAGE TYPE */ + dhcp_msg_type = *(uint8_t *)OPTION_PAYLOAD(opt); + break; + case 1: /* DHCP OPT NETMASK */ + netmask = *(ovs_be32 *)OPTION_PAYLOAD(opt); + break; + case 3: /* DHCP OPT ROUTER */ + gw_ip = *(ovs_be32 *)OPTION_PAYLOAD(opt); + break; + } + } + + uint8_t expected_msg_type = (uint8_t)atoi(argv[5]); + if (expected_msg_type == 1) { + expected_msg_type = 2; + } + else { + expected_msg_type = 5; + } + + + if (dhcp_msg_type != expected_msg_type) { + printf("Error. dhcp message type = [%d] : " + "Expected dhcp message type = [%d]\n", + dhcp_msg_type, expected_msg_type); + exit(1); + } + + if (netmask != expected_netmask) { + printf("Error. Offered netmask : "IP_FMT " : Expected netmask : %s\n", + IP_ARGS(netmask), argv[3]); + exit(1); + } + if (gw_ip != expected_gw_ip) { + printf("Error. Offered gateway ip : "IP_FMT " : Expected gateway ip : %s\n", + IP_ARGS(gw_ip), argv[4]); + exit(1); + } + + exit(0); +} + +OVSTEST_REGISTER("test-ovn-dhcp", test_ovn_dhcp_main);