CSIT-25 DHCPv4 proxy tests 67/2067/6
authorPatrik Hrnciar <phrnciar@cisco.com>
Fri, 15 Jul 2016 06:48:43 +0000 (08:48 +0200)
committerDave Wallace <dwallacelf@gmail.com>
Mon, 1 Aug 2016 14:46:43 +0000 (14:46 +0000)
Change-Id: I5aa449d7289fe8d015a32b21c0a368e9693ba757
Signed-off-by: Patrik Hrnciar <phrnciar@cisco.com>
resources/libraries/python/Dhcp.py
resources/libraries/python/TrafficScriptExecutor.py
resources/libraries/robot/dhcp_client.robot
resources/templates/vat/dhcp_proxy_config.vat [new file with mode: 0644]
resources/traffic_scripts/dhcp/send_dhcp_discover.py [new file with mode: 0755]
resources/traffic_scripts/dhcp/send_dhcp_messages.py [new file with mode: 0755]
tests/func/dhcp/dhcp_v4_proxy.robot [new file with mode: 0644]

index 8b78f45..ebdfeb3 100644 (file)
@@ -46,3 +46,26 @@ class DhcpClient(object):
             raise RuntimeError('Unable to set DHCP client on node {} and'
                                ' interface {}.'
                                .format(vpp_node, interface))
+
+    @staticmethod
+    def dhcp_proxy_config(vpp_node, server_address, source_address):
+        """Set DHCP proxy.
+
+        :param vpp_node: VPP node to set DHCP proxy.
+        :param server_address: DHCP server IP address.
+        :param source_address: DHCP proxy address.
+        :type vpp_node: dict
+        :type server_address: str
+        :type source_address: str
+        :raises RuntimeError: If unable to set DHCP proxy.
+        """
+
+        output = VatExecutor.cmd_from_template(vpp_node,
+                                               "dhcp_proxy_config.vat",
+                                               server_address=server_address,
+                                               source_address=source_address)
+        output = output[0]
+
+        if output["retval"] != 0:
+            raise RuntimeError('Unable to set DHCP proxy on node {}'
+                               .format(vpp_node))
index 8d89904..e5fb258 100644 (file)
@@ -50,6 +50,7 @@ class TrafficScriptExecutor(object):
         :type timeout: int
         :raises RuntimeError: ICMP echo Rx timeout.
         :raises RuntimeError: DHCP REQUEST Rx timeout.
+        :raises RuntimeError: DHCP DISCOVER timeout.
         :raises RuntimeError: TCP/UDP Rx timeout.
         :raises RuntimeError: ARP reply timeout.
         :raises RuntimeError: Traffic script execution failed.
@@ -74,6 +75,8 @@ class TrafficScriptExecutor(object):
                 raise RuntimeError("ICMP echo Rx timeout")
             elif "RuntimeError: DHCP REQUEST Rx timeout" in stderr:
                 raise RuntimeError("DHCP REQUEST Rx timeout")
+            elif "RuntimeError('DHCP DISCOVER timeout')" in stderr:
+                raise RuntimeError("DHCP DISCOVER timeout")
             elif "RuntimeError: TCP/UDP Rx timeout" in stderr:
                 raise RuntimeError("TCP/UDP Rx timeout")
             elif "Error occurred: ARP reply timeout" in stdout:
index c3c4645..c11e01c 100644 (file)
 | | ... | --server_mac | ${server_mac} | --server_ip | ${server_ip}
 | | ... | --client_ip | ${client_ip} | --client_mask | ${client_mask}
 | | ... | --lease_time | ${lease_time}
-| | Run Traffic Script On Node  | dhcp/check_dhcp_request_ack.py
+| | Run Traffic Script On Node | dhcp/check_dhcp_request_ack.py
+| | ... | ${tg_node} | ${args}
+
+| Send DHCP Messages
+| | [Documentation] | Send and receive DHCP messages between client
+| | ...             | and server through DHCP proxy.
+| | ...
+| | ... | *Arguments:*
+| | ... | - tg_node - TG node. Type: dictionary
+| | ... | - tg_interface1 - TG interface. Type: string
+| | ... | - tg_interface2 - TG interface. Type: string
+| | ... | - server_ip - DHCP server IP address. Type: string
+| | ... | - server_mac - DHCP server MAC address. Type: string
+| | ... | - client_ip - Client IP address. Type: string
+| | ... | - client_mac - Client MAC address. Type: string
+| | ... | - proxy_ip - DHCP proxy IP address. Type: string
+| | ...
+| | ... | *Return:*
+| | ... | - No value returned.
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Send DHCP Messages \| ${nodes['TG']} \
+| | ... | \| eth3 \| eth4 \| 192.168.0.100 \| 08:00:27:cc:4f:54 \
+| | ... | \| 172.16.0.2 \| 08:00:27:64:18:d2 \| 172.16.0.1 \|
+| | ...
+| | [Arguments] | ${tg_node} | ${tg_interface1} | ${tg_interface2}
+| | ... | ${server_ip} | ${server_mac} | ${client_ip} | ${client_mac}
+| | ... | ${proxy_ip} |
+| | ${tg_interface_name1}= | Get interface name | ${tg_node} | ${tg_interface1}
+| | ${tg_interface_name2}= | Get interface name | ${tg_node} | ${tg_interface2}
+| | ${args}= | Catenate | --tx_if | ${tg_interface_name1}
+| | ...                 | --rx_if | ${tg_interface_name2}
+| | ...                 | --server_ip | ${server_ip}
+| | ...                 | --server_mac | ${server_mac}
+| | ...                 | --client_ip | ${client_ip}
+| | ...                 | --client_mac | ${client_mac}
+| | ...                 | --proxy_ip | ${proxy_ip}
+| | Run Traffic Script On Node | dhcp/send_dhcp_messages.py
+| | ... | ${tg_node} | ${args}
+
+| Send DHCP DISCOVER
+| | [Documentation] | Send and receive DHCP DISCOVER.
+| | ...
+| | ... | *Arguments:*
+| | ... | - tg_node - TG node. Type: dictionary
+| | ... | - tg_interface1 - TG interface. Type: string
+| | ... | - tg_interface2 - TG interface. Type: string
+| | ... | - tx_src_ip - Source address of DHCP DISCOVER packet. Type: string
+| | ... | - tx_dst_ip - Destination address of DHCP DISCOVER packet. Type: string
+| | ...
+| | ... | *Return:*
+| | ... | - No value returned.
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Send DHCP DISCOVER \| ${nodes['TG']} \
+| | ... | \| eth3 \| eth4 \| 0.0.0.0 \| 255.255.255.255 \|
+| | ...
+| | [Arguments] | ${tg_node} | ${tg_interface1} | ${tg_interface2}
+| | ... | ${tx_src_ip} | ${tx_dst_ip} |
+| | ${tg_interface_name1}= | Get interface name | ${tg_node} | ${tg_interface1}
+| | ${tg_interface_name2}= | Get interface name | ${tg_node} | ${tg_interface2}
+| | ${args}= | Catenate | --tx_if | ${tg_interface_name1}
+| | ...                 | --rx_if | ${tg_interface_name2}
+| | ...                 | --tx_src_ip | ${tx_src_ip}
+| | ...                 | --tx_dst_ip | ${tx_dst_ip}
+| | Run Traffic Script On Node | dhcp/send_dhcp_discover.py
+| | ... | ${tg_node} | ${args}
+
+| Send DHCP DISCOVER should fail
+| | [Documentation] | Send and receive DHCP DISCOVER should fail.
+| | ...
+| | ... | *Arguments:*
+| | ... | - tg_node - TG node. Type: dictionary
+| | ... | - tg_interface1 - TG interface. Type: string
+| | ... | - tg_interface2 - TG interface. Type: string
+| | ... | - tx_src_ip - Source address of DHCP DISCOVER packet. Type: string
+| | ... | - tx_dst_ip - Destination address of DHCP DISCOVER packet. Type: string
+| | ...
+| | ... | *Return:*
+| | ... | - No value returned.
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Send DHCP DISCOVER should fail \| ${nodes['TG']} \
+| | ... | \| eth3 \| eth4 \| 0.0.0.0 \| 255.255.255.255 \|
+| | ...
+| | [Arguments] | ${tg_node} | ${tg_interface1} | ${tg_interface2}
+| | ... | ${tx_src_ip} | ${tx_dst_ip} |
+| | ${tg_interface_name1}= | Get interface name | ${tg_node} | ${tg_interface1}
+| | ${tg_interface_name2}= | Get interface name | ${tg_node} | ${tg_interface2}
+| | ${args}= | Catenate | --tx_if | ${tg_interface_name1}
+| | ...                 | --rx_if | ${tg_interface_name2}
+| | ...                 | --tx_src_ip | ${tx_src_ip}
+| | ...                 | --tx_dst_ip | ${tx_dst_ip}
+| | Run Keyword And Expect Error | DHCP DISCOVER timeout
+| | ... | Run Traffic Script On Node | dhcp/send_dhcp_discover.py
 | | ... | ${tg_node} | ${args}
diff --git a/resources/templates/vat/dhcp_proxy_config.vat b/resources/templates/vat/dhcp_proxy_config.vat
new file mode 100644 (file)
index 0000000..365b8d7
--- /dev/null
@@ -0,0 +1 @@
+dhcp_proxy_config svr {server_address} src {source_address}
\ No newline at end of file
diff --git a/resources/traffic_scripts/dhcp/send_dhcp_discover.py b/resources/traffic_scripts/dhcp/send_dhcp_discover.py
new file mode 100755 (executable)
index 0000000..1715bb1
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# 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.
+
+"""Traffic script that sends DHCP DISCOVER packets."""
+
+import sys
+
+from scapy.all import Ether
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet import UDP_SERVICES
+from scapy.layers.dhcp import DHCP, BOOTP
+
+from resources.libraries.python.PacketVerifier import RxQueue, TxQueue
+from resources.libraries.python.TrafficScriptArg import TrafficScriptArg
+
+
+def main():
+    """Send DHCP DISCOVER packet."""
+
+    args = TrafficScriptArg(['tx_src_ip', 'tx_dst_ip'])
+
+    tx_if = args.get_arg('tx_if')
+    rx_if = args.get_arg('rx_if')
+
+    rxq = RxQueue(rx_if)
+    txq = TxQueue(tx_if)
+
+    tx_src_ip = args.get_arg('tx_src_ip')
+    tx_dst_ip = args.get_arg('tx_dst_ip')
+
+    sent_packets = []
+
+    dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff") / \
+                    IP(src=tx_src_ip, dst=tx_dst_ip) / \
+                    UDP(sport=68, dport=67) / \
+                    BOOTP(op=1,) / \
+                    DHCP(options=[("message-type", "discover"),
+                                  "end"])
+
+    sent_packets.append(dhcp_discover)
+    txq.send(dhcp_discover)
+
+    ether = rxq.recv(2)
+
+    if ether is None:
+        raise RuntimeError('DHCP DISCOVER timeout')
+
+    if ether[UDP].dport != UDP_SERVICES.bootps:
+        raise RuntimeError("UDP destination port error.")
+    print "UDP destination port: OK."
+
+    if ether[UDP].sport != UDP_SERVICES.bootpc:
+        raise RuntimeError("UDP source port error.")
+    print "UDP source port: OK."
+
+    if ether[DHCP].options[0][1] != 1:  # 1 - DISCOVER message
+        raise RuntimeError("DHCP DISCOVER message error.")
+    print "DHCP DISCOVER message OK."
+
+    sys.exit(0)
+
+if __name__ == "__main__":
+    main()
diff --git a/resources/traffic_scripts/dhcp/send_dhcp_messages.py b/resources/traffic_scripts/dhcp/send_dhcp_messages.py
new file mode 100755 (executable)
index 0000000..7881c24
--- /dev/null
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# 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.
+
+"""Traffic script that sends DHCP packets."""
+
+import sys
+
+from scapy.all import Ether
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet import UDP_SERVICES
+from scapy.layers.dhcp import DHCP, BOOTP
+
+from resources.libraries.python.PacketVerifier import RxQueue, TxQueue
+from resources.libraries.python.TrafficScriptArg import TrafficScriptArg
+
+
+def dhcp_discover(args):
+    """Send DHCP DISCOVER packet."""
+
+    tx_if = args.get_arg('tx_if')
+    rx_if = args.get_arg('rx_if')
+
+    rxq = RxQueue(rx_if)
+    txq = TxQueue(tx_if)
+
+    tx_src_ip = "0.0.0.0"
+    tx_dst_ip = "255.255.255.255"
+
+    server_ip = args.get_arg('server_ip')
+    proxy_ip = args.get_arg('proxy_ip')
+    client_mac = args.get_arg('client_mac')
+
+    sent_packets = []
+
+    dhcp_discover = Ether(src=client_mac, dst="ff:ff:ff:ff:ff:ff") / \
+                    IP(src=tx_src_ip, dst=tx_dst_ip) / \
+                    UDP(sport=68, dport=67) / \
+                    BOOTP(op=1,) / \
+                    DHCP(options=[("message-type", "discover"),
+                                  "end"])
+
+    sent_packets.append(dhcp_discover)
+    txq.send(dhcp_discover)
+
+    ether = rxq.recv(2)
+
+    if ether is None:
+        raise RuntimeError('DHCP DISCOVER timeout')
+
+    if ether[IP].src != proxy_ip:
+        raise RuntimeError("Source IP address error.")
+    print "Source IP address: OK."
+
+    if ether[IP].dst != server_ip:
+        raise RuntimeError("Destination IP address error.")
+    print "Destination IP address: OK."
+
+    if ether[UDP].dport != UDP_SERVICES.bootps:
+        raise RuntimeError("UDP destination port error.")
+    print "UDP destination port: OK."
+
+    if ether[UDP].sport != UDP_SERVICES.bootpc:
+        raise RuntimeError("UDP source port error.")
+    print "UDP source port: OK."
+
+    if ether[DHCP].options[1][0] != 'relay_agent_Information':  # option 82
+        raise RuntimeError("Relay agent information error.")
+    option_82 = ether[DHCP].options[1][1]
+
+    if ether[DHCP].options[0][1] != 1:  # 1 - DISCOVER message
+        raise RuntimeError("DHCP DISCOVER message error.")
+    print "DHCP DISCOVER message OK."
+    dhcp_offer(args, option_82)
+
+
+def dhcp_offer(args, option_82):
+    """Send DHCP OFFER packet."""
+
+    rx_if = args.get_arg('tx_if')
+    tx_if = args.get_arg('rx_if')
+
+    rxq = RxQueue(rx_if)
+    txq = TxQueue(tx_if)
+
+    tx_dst_ip = "255.255.255.255"
+    server_ip = args.get_arg('server_ip')
+    server_mac = args.get_arg('server_mac')
+    client_ip = args.get_arg('client_ip')
+    proxy_ip = args.get_arg('proxy_ip')
+
+    sent_packets = []
+
+    dhcp_offer = Ether(src=server_mac, dst="ff:ff:ff:ff:ff:ff") / \
+                 IP(src=server_ip, dst=tx_dst_ip) / \
+                 UDP(sport=67, dport=68) / \
+                 BOOTP(op=2,
+                       yiaddr=client_ip,
+                       siaddr=server_ip) / \
+                 DHCP(options=
+                      [("message-type", "offer"),
+                       ("server_id", server_ip),
+                       ("relay_agent_Information", option_82),
+                       "end"])
+
+    txq.send(dhcp_offer)
+    sent_packets.append(dhcp_offer)
+
+    ether = rxq.recv(2)
+
+    if ether is None:
+        raise RuntimeError('DHCP OFFER timeout')
+
+    if ether[IP].dst != tx_dst_ip:
+        raise RuntimeError("Destination IP address error.")
+    print "Destination IP address: OK."
+
+    if ether[IP].src != proxy_ip:
+        raise RuntimeError("Source IP address error.")
+    print "Source IP address: OK."
+
+    if ether[UDP].dport != UDP_SERVICES.bootpc:
+        raise RuntimeError("UDP destination port error.")
+    print "UDP destination port: OK."
+
+    if ether[UDP].sport != UDP_SERVICES.bootps:
+        raise RuntimeError("UDP source port error.")
+    print "UDP source port: OK."
+
+    if ether[BOOTP].yiaddr != client_ip:
+        raise RuntimeError("Client IP address error.")
+    print "Client IP address: OK."
+
+    if ether[BOOTP].siaddr != server_ip:
+        raise RuntimeError("DHCP server IP address error.")
+    print "DHCP server IP address: OK."
+
+    if ether[DHCP].options[0][1] != 2:  # 2 - OFFER message
+        raise RuntimeError("DHCP OFFER message error.")
+    print "DHCP OFFER message OK."
+    dhcp_request(args)
+
+
+def dhcp_request(args):
+    """Send DHCP REQUEST packet."""
+
+    tx_if = args.get_arg('tx_if')
+    rx_if = args.get_arg('rx_if')
+
+    rxq = RxQueue(rx_if)
+    txq = TxQueue(tx_if)
+
+    tx_dst_ip = "255.255.255.255"
+    server_ip = args.get_arg('server_ip')
+    client_ip = args.get_arg('client_ip')
+    client_mac = args.get_arg('client_mac')
+    proxy_ip = args.get_arg('proxy_ip')
+
+    sent_packets = []
+
+    dhcp_request = Ether(src=client_mac, dst="ff:ff:ff:ff:ff:ff") / \
+                   IP(src="0.0.0.0", dst=tx_dst_ip) / \
+                   UDP(sport=68, dport=67) / \
+                   BOOTP(op=1,
+                         giaddr=proxy_ip,
+                         siaddr=server_ip) / \
+                   DHCP(options=[("message-type", "request"),
+                                 ("server_id", server_ip),
+                                 ("requested_addr", client_ip),
+                                 "end"])
+
+    sent_packets.append(dhcp_request)
+    txq.send(dhcp_request)
+
+    ether = rxq.recv(2)
+
+    if ether is None:
+        raise RuntimeError('DHCP REQUEST timeout')
+
+    if ether[IP].dst != server_ip:
+        raise RuntimeError("Destination IP address error.")
+    print "Destination IP address: OK."
+
+    if ether[IP].src != proxy_ip:
+        raise RuntimeError("Source IP address error.")
+    print "Source IP address: OK."
+
+    if ether[UDP].dport != UDP_SERVICES.bootps:
+        raise RuntimeError("UDP destination port error.")
+    print "UDP destination port: OK."
+
+    if ether[UDP].sport != UDP_SERVICES.bootpc:
+        raise RuntimeError("UDP source port error.")
+    print "UDP source port: OK."
+
+    if ether[BOOTP].siaddr != server_ip:
+        raise RuntimeError("DHCP server IP address error.")
+    print "DHCP server IP address: OK."
+
+    if ether[DHCP].options[2][1] != client_ip:
+        raise RuntimeError("Requested IP address error.")
+    print "Requested IP address: OK."
+
+    if ether[DHCP].options[3][0] != 'relay_agent_Information':  # option 82
+        raise RuntimeError("Relay agent information error.")
+    option_82 = ether[DHCP].options[3][1]
+
+    if ether[DHCP].options[0][1] != 3:  # 2 - REQUEST message
+        raise RuntimeError("DHCP REQUEST message error.")
+    print "DHCP REQUEST message: OK."
+    dhcp_ack(args, option_82)
+
+
+def dhcp_ack(args, option_82):
+    """Send DHCP ACK packet."""
+
+    rx_if = args.get_arg('tx_if')
+    tx_if = args.get_arg('rx_if')
+
+    rxq = RxQueue(rx_if)
+    txq = TxQueue(tx_if)
+
+    tx_dst_ip = "255.255.255.255"
+    server_ip = args.get_arg('server_ip')
+    server_mac = args.get_arg('server_mac')
+    client_ip = args.get_arg('client_ip')
+    proxy_ip = args.get_arg('proxy_ip')
+    lease_time = 43200  # 12 hours
+
+    sent_packets = []
+
+    dhcp_ack = Ether(src=server_mac, dst="ff:ff:ff:ff:ff:ff") / \
+               IP(src=server_ip, dst=tx_dst_ip) / \
+               UDP(sport=67, dport=68) / \
+               BOOTP(op=2,
+                     yiaddr=client_ip,
+                     siaddr=server_ip) / \
+               DHCP(options=
+                    [("message-type", "ack"),
+                     ("server_id", server_ip),
+                     ("lease_time", lease_time),
+                     ("relay_agent_Information", option_82),
+                     "end"])
+
+    txq.send(dhcp_ack)
+    sent_packets.append(dhcp_ack)
+
+    ether = rxq.recv(2)
+
+    if ether is None:
+        raise RuntimeError('DHCP ACK timeout')
+
+    if ether[IP].dst != tx_dst_ip:
+        raise RuntimeError("Destination IP address error.")
+    print "Destination IP address: OK."
+
+    if ether[IP].src != proxy_ip:
+        raise RuntimeError("Source IP address error.")
+    print "Source IP address: OK."
+
+    if ether[UDP].dport != UDP_SERVICES.bootpc:
+        raise RuntimeError("UDP destination port error.")
+    print "UDP destination port: OK."
+
+    if ether[UDP].sport != UDP_SERVICES.bootps:
+        raise RuntimeError("UDP source port error.")
+    print "UDP source port: OK."
+
+    if ether[BOOTP].yiaddr != client_ip:
+        raise RuntimeError("Client IP address error.")
+    print "Client IP address: OK."
+
+    if ether[BOOTP].siaddr != server_ip:
+        raise RuntimeError("DHCP server IP address error.")
+    print "DHCP server IP address: OK."
+
+    if ether[DHCP].options[2][1] != lease_time:
+        raise RuntimeError("DHCP lease time error.")
+    print "DHCP lease time OK."
+
+    if ether[DHCP].options[0][1] != 5:  # 5 - ACK message
+        raise RuntimeError("DHCP ACK message error.")
+    print "DHCP ACK message OK."
+
+
+def main():
+    """Send DHCP messages."""
+
+    args = TrafficScriptArg(['server_ip', 'server_mac', 'client_ip',
+                             'client_mac', 'proxy_ip'])
+
+    dhcp_discover(args)
+
+    sys.exit(0)
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/func/dhcp/dhcp_v4_proxy.robot b/tests/func/dhcp/dhcp_v4_proxy.robot
new file mode 100644 (file)
index 0000000..62532d3
--- /dev/null
@@ -0,0 +1,95 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# 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.
+
+*** Settings ***
+| Resource | resources/libraries/robot/default.robot
+| Resource | resources/libraries/robot/testing_path.robot
+| Resource | resources/libraries/robot/dhcp_client.robot
+| Resource | resources/libraries/robot/ipv4.robot
+| Library | resources.libraries.python.Trace
+| Force Tags | HW_ENV | VM_ENV | 3_NODE_DOUBLE_LINK_TOPO
+| Test Setup | Run Keywords | Setup all DUTs before test
+| ...        | AND          | Setup all TGs before traffic script
+| Test Teardown | Run Keywords | Show Packet Trace on All DUTs | ${nodes}
+| ...           | AND          | Show vpp trace dump on all DUTs
+| Documentation | *DHCP proxy test cases*
+| ...
+| ... | *[Top] Network Topologies:* TG = DUT
+| ... |        with two links between the nodes.
+| ... | *[Enc] Packet Encapsulations:* Eth-IPv4-UDP-BOOTP-DHCP
+| ... | *[Cfg] DUT configuration:* DUT is configured with DHCP proxy.
+| ... | *[Ver] TG verification:* Test DHCP packets are sent
+| ... |        on TG on first link to DUT and received on TG on second link.
+| ... |        On receive TG verifies if DHCP packets are valid.
+
+*** Variables ***
+| ${dut_to_tg_if1_ip}= | 172.16.0.1
+| ${dut_to_tg_if2_ip}= | 192.168.0.1
+| ${dhcp_server_ip}= | 192.168.0.100
+| ${client_ip}= | 172.16.0.2
+| ${prefix_length}= | 24
+
+| ${discover_src_ip}= | 0.0.0.0
+| ${valid_discover_dst_ip}= | 255.255.255.255
+| ${invalid_discover_dst_ip}= | 255.255.255.1
+
+*** Test Cases ***
+| TC01: VPP proxies valid DHCPv4 request to DHCPv4 server
+| | [Documentation] |
+| | ... | [Top] TG=DUT \
+| | ... | [Enc] Eth-IPv4-UDP-BOOTP-DHCP
+| | ... | [Cfg] On DUT setup DHCP proxy.
+| | ... | [Ver] Make TG verify matching DHCP packets between client and DHCP
+| | ... | server through DHCP proxy.
+| | ...
+| | Given Path for 2-node testing is set
+| | ... | ${nodes['TG']} | ${nodes['DUT1']} | ${nodes['TG']}
+| | And Interfaces in 2-node path are up
+| | And VPP Route Add | ${dut_node} | 255.255.255.255 | 32 | ${NONE} | local
+| | ... | ${FALSE} | ${NONE}
+| | And Set Interface Address | ${dut_node}
+| | ... | ${dut_to_tg_if1} | ${dut_to_tg_if1_ip} | ${prefix_length}
+| | And Set Interface Address | ${dut_node}
+| | ... | ${dut_to_tg_if2} | ${dut_to_tg_if2_ip} | ${prefix_length}
+| | And Add Arp On Dut | ${dut_node} | ${dut_to_tg_if2} | ${dhcp_server_ip}
+| | ... | ${tg_to_dut_if2_mac}
+| | When DHCP Proxy Config | ${dut_node} | ${dhcp_server_ip}
+| | ... | ${dut_to_tg_if1_ip}
+| | Then Send DHCP Messages | ${tg_node} | ${tg_to_dut_if1} | ${tg_to_dut_if2}
+| | ... | ${dhcp_server_ip} | ${tg_to_dut_if2_mac} | ${client_ip}
+| | ... | ${tg_to_dut_if1_mac} | ${dut_to_tg_if1_ip}
+
+| TC02: VPP proxy ignores invalid DHCPv4 request
+| | [Documentation] |
+| | ... | [Top] TG=DUT \
+| | ... | [Enc] Eth-IPv4-UDP-BOOTP-DHCP
+| | ... | [Cfg] On DUT setup DHCP proxy.
+| | ... | [Ver] Make TG verify matching invalid DHCP packets are dropped.
+| | ...
+| | Given Path for 2-node testing is set
+| | ... | ${nodes['TG']} | ${nodes['DUT1']} | ${nodes['TG']}
+| | And Interfaces in 2-node path are up
+| | And VPP Route Add | ${dut_node} | 255.255.255.255 | 32 | ${NONE} | local
+| | ... | ${FALSE} | ${NONE}
+| | And Set Interface Address | ${dut_node}
+| | ... | ${dut_to_tg_if1} | ${dut_to_tg_if1_ip} | ${prefix_length}
+| | And Set Interface Address | ${dut_node}
+| | ... | ${dut_to_tg_if2} | ${dut_to_tg_if2_ip} | ${prefix_length}
+| | And Add Arp On Dut | ${dut_node} | ${dut_to_tg_if2} | ${dhcp_server_ip}
+| | ... | ${tg_to_dut_if2_mac}
+| | When DHCP Proxy Config | ${dut_node} | ${dhcp_server_ip}
+| | ... | ${dut_to_tg_if1_ip}
+| | Then Send DHCP DISCOVER | ${tg_node} | ${tg_to_dut_if1}
+| | ... | ${tg_to_dut_if2} | ${discover_src_ip} | ${valid_discover_dst_ip}
+| | And Send DHCP DISCOVER should fail | ${tg_node} | ${tg_to_dut_if1}
+| | ... | ${tg_to_dut_if2} | ${discover_src_ip} | ${invalid_discover_dst_ip}