aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrik Hrnciar <phrnciar@cisco.com>2016-07-15 08:48:43 +0200
committerDave Wallace <dwallacelf@gmail.com>2016-08-01 14:46:43 +0000
commitec3512ff7f1405f2e7bc74bbfadf5691afc352c4 (patch)
tree79260ea2899e2b02430bdc6b2ed8b02f7cd9c1c5
parent8fa8590c30540f53edb02c223a6de616cbe149da (diff)
CSIT-25 DHCPv4 proxy tests
Change-Id: I5aa449d7289fe8d015a32b21c0a368e9693ba757 Signed-off-by: Patrik Hrnciar <phrnciar@cisco.com>
-rw-r--r--resources/libraries/python/Dhcp.py23
-rw-r--r--resources/libraries/python/TrafficScriptExecutor.py3
-rw-r--r--resources/libraries/robot/dhcp_client.robot99
-rw-r--r--resources/templates/vat/dhcp_proxy_config.vat1
-rwxr-xr-xresources/traffic_scripts/dhcp/send_dhcp_discover.py74
-rwxr-xr-xresources/traffic_scripts/dhcp/send_dhcp_messages.py307
-rw-r--r--tests/func/dhcp/dhcp_v4_proxy.robot95
7 files changed, 601 insertions, 1 deletions
diff --git a/resources/libraries/python/Dhcp.py b/resources/libraries/python/Dhcp.py
index 8b78f457c0..ebdfeb3b80 100644
--- a/resources/libraries/python/Dhcp.py
+++ b/resources/libraries/python/Dhcp.py
@@ -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))
diff --git a/resources/libraries/python/TrafficScriptExecutor.py b/resources/libraries/python/TrafficScriptExecutor.py
index 8d899044fc..e5fb2589ef 100644
--- a/resources/libraries/python/TrafficScriptExecutor.py
+++ b/resources/libraries/python/TrafficScriptExecutor.py
@@ -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:
diff --git a/resources/libraries/robot/dhcp_client.robot b/resources/libraries/robot/dhcp_client.robot
index c3c4645d3d..c11e01c67a 100644
--- a/resources/libraries/robot/dhcp_client.robot
+++ b/resources/libraries/robot/dhcp_client.robot
@@ -137,5 +137,102 @@
| | ... | --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
index 0000000000..365b8d7de1
--- /dev/null
+++ b/resources/templates/vat/dhcp_proxy_config.vat
@@ -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
index 0000000000..1715bb1d01
--- /dev/null
+++ b/resources/traffic_scripts/dhcp/send_dhcp_discover.py
@@ -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
index 0000000000..7881c24afd
--- /dev/null
+++ b/resources/traffic_scripts/dhcp/send_dhcp_messages.py
@@ -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
index 0000000000..62532d3077
--- /dev/null
+++ b/tests/func/dhcp/dhcp_v4_proxy.robot
@@ -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}