summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2017-03-11 05:55:21 -0800
committerFlorin Coras <florin.coras@gmail.com>2017-03-17 15:49:39 +0000
commit4b919a56642ccd0a44920feace872aeb5b7a62cf (patch)
tree17750f4efc80d7863b68f9cd08ab381cd00dd534
parentc60f557590f79b8817382bdd982825b66c4e0a73 (diff)
Attached hosts
allow this config to function: set int ip address loop0 169.254.1.1/32 (the default GW address for attached hosts) set int unnumbered af_packet0 use loop0 ('enable' IP on the host interface) ip route add 192.168.1.1/32 via af_packet0 (where to find the host) repeat for each host and host interface. Inter-host communication is throught the /32 routes. To allow this: 1 - attached host routes have the ATTACHED flag set, so the ARP code accepts then as legitimate sources 2 - unnumbered interfaces inherit the source address from the IP interface Change-Id: Ib66c5f0e848c528f79372813adc3a0c11b50717f Signed-off-by: Neale Ranns <nranns@cisco.com>
-rw-r--r--src/vnet/ethernet/arp.c72
-rw-r--r--src/vnet/fib/fib_entry_src.c4
-rw-r--r--src/vnet/fib/fib_path.c7
-rw-r--r--src/vnet/fib/fib_path.h6
-rw-r--r--src/vnet/fib/fib_table.c1
-rw-r--r--src/vnet/fib/fib_types.h4
-rw-r--r--src/vnet/interface_api.c15
-rw-r--r--src/vnet/interface_cli.c12
-rw-r--r--test/test_neighbor.py176
-rw-r--r--test/vpp_interface.py2
10 files changed, 269 insertions, 30 deletions
diff --git a/src/vnet/ethernet/arp.c b/src/vnet/ethernet/arp.c
index d8ae84433e7..75c7e20372a 100644
--- a/src/vnet/ethernet/arp.c
+++ b/src/vnet/ethernet/arp.c
@@ -1016,7 +1016,6 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
vnet_hw_interface_t *hw_if0;
ethernet_arp_header_t *arp0;
ethernet_header_t *eth0;
- ip_adjacency_t *adj0;
ip4_address_t *if_addr0, proxy_src;
u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
u8 is_request0, dst_is_local0, is_unnum0;
@@ -1073,6 +1072,11 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
32);
dst_flags = fib_entry_get_flags (dst_fei);
+ src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
+ &arp0->ip4_over_ethernet[0].ip4,
+ 32);
+ src_flags = fib_entry_get_flags (src_fei);
+
conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
@@ -1085,11 +1089,6 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
is_unnum0 = sw_if_index0 != conn_sw_if_index0;
/* Source must also be local to subnet of matching interface address. */
- src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
- &arp0->ip4_over_ethernet[0].ip4,
- 32);
- src_flags = fib_entry_get_flags (src_fei);
-
if (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
(FIB_ENTRY_FLAG_CONNECTED & src_flags)))
{
@@ -1187,25 +1186,62 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
/* get the adj from the destination's covering connected */
if (NULL == pa)
{
- adj0 =
- adj_get (fib_entry_get_adj_for_source
- (ip4_fib_table_lookup
- (ip4_fib_get (fib_index0),
- &arp0->ip4_over_ethernet[1].ip4, 31),
- FIB_SOURCE_INTERFACE));
- if (adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
- {
- error0 = ETHERNET_ARP_ERROR_missing_interface_address;
- goto drop2;
- }
if (is_unnum0)
{
if (!arp_unnumbered (p0, pi0, eth0, conn_sw_if_index0))
goto drop2;
}
else
- vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
+ {
+ ip_adjacency_t *adj0 = NULL;
+ adj_index_t ai;
+
+ if (FIB_ENTRY_FLAG_ATTACHED & src_flags)
+ {
+ /*
+ * If the source is attached use the adj from that source.
+ */
+ ai = fib_entry_get_adj (src_fei);
+ if (ADJ_INDEX_INVALID != ai)
+ {
+ adj0 = adj_get (ai);
+ }
+ }
+ else
+ {
+ /*
+ * Get the glean adj from the cover. This is presumably interface
+ * sourced, and therefre needs to be a glean adj.
+ */
+ ai = fib_entry_get_adj_for_source
+ (ip4_fib_table_lookup
+ (ip4_fib_get (fib_index0),
+ &arp0->ip4_over_ethernet[1].ip4, 31),
+ FIB_SOURCE_INTERFACE);
+
+ if (ADJ_INDEX_INVALID != ai)
+ {
+ adj0 = adj_get (ai);
+
+ if (adj0->lookup_next_index == IP_LOOKUP_NEXT_GLEAN)
+ {
+ adj0 = NULL;
+ }
+ }
+ }
+ if (NULL != adj0)
+ {
+ vlib_buffer_advance (p0,
+ -adj0->rewrite_header.data_bytes);
+ }
+ else
+ {
+ error0 = ETHERNET_ARP_ERROR_missing_interface_address;
+ goto drop2;
+ }
+ }
}
+
vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
n_left_to_next, pi0, next0);
diff --git a/src/vnet/fib/fib_entry_src.c b/src/vnet/fib/fib_entry_src.c
index feb232df7f1..aa1d5a24a0f 100644
--- a/src/vnet/fib/fib_entry_src.c
+++ b/src/vnet/fib/fib_entry_src.c
@@ -946,6 +946,10 @@ fib_path_is_attached (const fib_route_path_t *rpath)
{
return (!0);
}
+ else if (rpath->frp_flags & FIB_ROUTE_PATH_ATTACHED)
+ {
+ return (!0);
+ }
return (0);
}
diff --git a/src/vnet/fib/fib_path.c b/src/vnet/fib/fib_path.c
index aa545b5ed45..3ed309f328b 100644
--- a/src/vnet/fib/fib_path.c
+++ b/src/vnet/fib/fib_path.c
@@ -110,6 +110,10 @@ typedef enum fib_path_oper_attribute_t_ {
*/
FIB_PATH_OPER_ATTRIBUTE_RESOLVED,
/**
+ * The path is attached, despite what the next-hop may say.
+ */
+ FIB_PATH_OPER_ATTRIBUTE_ATTACHED,
+ /**
* The path has become a permanent drop.
*/
FIB_PATH_OPER_ATTRIBUTE_DROP,
@@ -143,6 +147,7 @@ typedef enum fib_path_oper_flags_t_ {
FIB_PATH_OPER_FLAG_RECURSIVE_LOOP = (1 << FIB_PATH_OPER_ATTRIBUTE_RECURSIVE_LOOP),
FIB_PATH_OPER_FLAG_DROP = (1 << FIB_PATH_OPER_ATTRIBUTE_DROP),
FIB_PATH_OPER_FLAG_RESOLVED = (1 << FIB_PATH_OPER_ATTRIBUTE_RESOLVED),
+ FIB_PATH_OPER_FLAG_ATTACHED = (1 << FIB_PATH_OPER_ATTRIBUTE_ATTACHED),
} __attribute__ ((packed)) fib_path_oper_flags_t;
/**
@@ -963,6 +968,8 @@ fib_path_route_flags_to_cfg_flags (const fib_route_path_t *rpath)
cfg_flags |= FIB_PATH_CFG_FLAG_RESOLVE_ATTACHED;
if (rpath->frp_flags & FIB_ROUTE_PATH_LOCAL)
cfg_flags |= FIB_PATH_CFG_FLAG_LOCAL;
+ if (rpath->frp_flags & FIB_ROUTE_PATH_ATTACHED)
+ cfg_flags |= FIB_PATH_CFG_FLAG_ATTACHED;
return (cfg_flags);
}
diff --git a/src/vnet/fib/fib_path.h b/src/vnet/fib/fib_path.h
index 91f49d09234..14efc1ab842 100644
--- a/src/vnet/fib/fib_path.h
+++ b/src/vnet/fib/fib_path.h
@@ -63,6 +63,10 @@ typedef enum fib_path_cfg_attribute_t_ {
*/
FIB_PATH_CFG_ATTRIBUTE_RESOLVE_ATTACHED,
/**
+ * The path is attached
+ */
+ FIB_PATH_CFG_ATTRIBUTE_ATTACHED,
+ /**
* The path is a for-us path
*/
FIB_PATH_CFG_ATTRIBUTE_LOCAL,
@@ -83,6 +87,7 @@ typedef enum fib_path_cfg_attribute_t_ {
[FIB_PATH_CFG_ATTRIBUTE_RESOLVE_HOST] = "resolve-host", \
[FIB_PATH_CFG_ATTRIBUTE_RESOLVE_ATTACHED] = "resolve-attached", \
[FIB_PATH_CFG_ATTRIBUTE_LOCAL] = "local", \
+ [FIB_PATH_CFG_ATTRIBUTE_ATTACHED] = "attached", \
}
#define FOR_EACH_FIB_PATH_CFG_ATTRIBUTE(_item) \
@@ -100,6 +105,7 @@ typedef enum fib_path_cfg_flags_t_ {
FIB_PATH_CFG_FLAG_RESOLVE_HOST = (1 << FIB_PATH_CFG_ATTRIBUTE_RESOLVE_HOST),
FIB_PATH_CFG_FLAG_RESOLVE_ATTACHED = (1 << FIB_PATH_CFG_ATTRIBUTE_RESOLVE_ATTACHED),
FIB_PATH_CFG_FLAG_LOCAL = (1 << FIB_PATH_CFG_ATTRIBUTE_LOCAL),
+ FIB_PATH_CFG_FLAG_ATTACHED = (1 << FIB_PATH_CFG_ATTRIBUTE_ATTACHED),
} __attribute__ ((packed)) fib_path_cfg_flags_t;
diff --git a/src/vnet/fib/fib_table.c b/src/vnet/fib/fib_table.c
index a0ce0bbb4de..7818d02e05d 100644
--- a/src/vnet/fib/fib_table.c
+++ b/src/vnet/fib/fib_table.c
@@ -480,6 +480,7 @@ fib_table_route_path_fixup (const fib_prefix_t *prefix,
path->frp_sw_if_index != ~0)
{
path->frp_addr = prefix->fp_addr;
+ path->frp_flags |= FIB_ROUTE_PATH_ATTACHED;
}
}
diff --git a/src/vnet/fib/fib_types.h b/src/vnet/fib/fib_types.h
index 05e0e0af4c9..1c5299a9214 100644
--- a/src/vnet/fib/fib_types.h
+++ b/src/vnet/fib/fib_types.h
@@ -282,6 +282,10 @@ typedef enum fib_route_path_flags_t_
* A for-us/local path
*/
FIB_ROUTE_PATH_LOCAL = (1 << 2),
+ /**
+ * Attached path
+ */
+ FIB_ROUTE_PATH_ATTACHED = (1 << 3),
} fib_route_path_flags_t;
/**
diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c
index 28b09b55598..44798c8b33b 100644
--- a/src/vnet/interface_api.c
+++ b/src/vnet/interface_api.c
@@ -459,11 +459,26 @@ static void vl_api_sw_interface_set_unnumbered_t_handler
{
si->flags |= VNET_SW_INTERFACE_FLAG_UNNUMBERED;
si->unnumbered_sw_if_index = sw_if_index;
+
+ ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] =
+ ip4_main.
+ lookup_main.if_address_pool_index_by_sw_if_index[sw_if_index];
+ ip6_main.
+ lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] =
+ ip6_main.
+ lookup_main.if_address_pool_index_by_sw_if_index[sw_if_index];
}
else
{
si->flags &= ~(VNET_SW_INTERFACE_FLAG_UNNUMBERED);
si->unnumbered_sw_if_index = (u32) ~ 0;
+
+ ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] = ~0;
+ ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] = ~0;
}
ip4_sw_interface_enable_disable (unnumbered_sw_if_index, mp->is_add);
ip6_sw_interface_enable_disable (unnumbered_sw_if_index, mp->is_add);
diff --git a/src/vnet/interface_cli.c b/src/vnet/interface_cli.c
index bd715e4ea4b..ec8530da299 100644
--- a/src/vnet/interface_cli.c
+++ b/src/vnet/interface_cli.c
@@ -864,6 +864,10 @@ set_unnumbered (vlib_main_t * vm,
si->unnumbered_sw_if_index = (u32) ~ 0;
ip4_sw_interface_enable_disable (unnumbered_sw_if_index, 0);
ip6_sw_interface_enable_disable (unnumbered_sw_if_index, 0);
+ ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] = ~0;
+ ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] = ~0;
}
else if (is_set)
{
@@ -871,6 +875,14 @@ set_unnumbered (vlib_main_t * vm,
si->unnumbered_sw_if_index = inherit_from_sw_if_index;
ip4_sw_interface_enable_disable (unnumbered_sw_if_index, 1);
ip6_sw_interface_enable_disable (unnumbered_sw_if_index, 1);
+ ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] =
+ ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [inherit_from_sw_if_index];
+ ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [unnumbered_sw_if_index] =
+ ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+ [inherit_from_sw_if_index];
}
return 0;
diff --git a/test/test_neighbor.py b/test/test_neighbor.py
index a97a63fc7a6..f2b1cfa47ee 100644
--- a/test/test_neighbor.py
+++ b/test/test_neighbor.py
@@ -44,9 +44,15 @@ class ARPTestCase(VppTestCase):
def tearDown(self):
super(ARPTestCase, self).tearDown()
+ self.pg0.unconfig_ip4()
+ self.pg0.unconfig_ip6()
+
+ self.pg1.unconfig_ip4()
+ self.pg1.unconfig_ip6()
+
+ self.pg3.unconfig_ip4()
+
for i in self.pg_interfaces:
- i.unconfig_ip4()
- i.unconfig_ip6()
i.admin_down()
def verify_arp_req(self, rx, smac, sip, dip):
@@ -115,7 +121,7 @@ class ARPTestCase(VppTestCase):
#
# Generate some hosts on the LAN
#
- self.pg1.generate_remote_hosts(6)
+ self.pg1.generate_remote_hosts(9)
#
# Send IP traffic to one of these unresolved hosts.
@@ -249,6 +255,47 @@ class ARPTestCase(VppTestCase):
self.assertTrue(find_nbr(self,
self.pg1.sw_if_index,
self.pg1._remote_hosts[3].ip4))
+ #
+ # Fire in an ARP request before the interface becomes IP enabled
+ #
+ self.pg2.generate_remote_hosts(4)
+
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst=self.pg1.local_ip4,
+ psrc=self.pg2.remote_hosts[3].ip4))
+ self.send_and_assert_no_replies(self.pg2, p,
+ "interface not IP enabled")
+
+ #
+ # Make pg2 un-numbered to pg1
+ #
+ self.pg2.set_unnumbered(self.pg1.sw_if_index)
+
+ #
+ # We should respond to ARP requests for the unnumbered to address
+ # once an attached route to the source is known
+ #
+ self.send_and_assert_no_replies(
+ self.pg2, p,
+ "ARP req for unnumbered address - no source")
+
+ attached_host = VppIpRoute(self, self.pg2.remote_hosts[3].ip4, 32,
+ [VppRoutePath("0.0.0.0",
+ self.pg2.sw_if_index)])
+ attached_host.add_vpp_config()
+
+ self.pg2.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg2.local_mac,
+ self.pg2.remote_mac,
+ self.pg1.local_ip4,
+ self.pg2.remote_hosts[3].ip4)
#
# A neighbor entry that has no associated FIB-entry
@@ -270,12 +317,8 @@ class ARPTestCase(VppTestCase):
self.pg1._remote_hosts[4].ip4,
32))
#
- # Unnumbered pg2 to pg1
- #
- self.pg2.set_unnumbered(self.pg1.sw_if_index)
-
- #
- # now we can form adjacencies out of pg2 from within pg1's subnet
+ # pg2 is unnumbered to pg1, so we can form adjacencies out of pg2
+ # from within pg1's subnet
#
arp_unnum = VppNeighbor(self,
self.pg2.sw_if_index,
@@ -302,10 +345,100 @@ class ARPTestCase(VppTestCase):
self.pg1._remote_hosts[5].ip4)
#
+ # ARP requests from hosts in pg1's subnet sent on pg2 are replied to
+ # with the unnumbered interface's address as the source
+ #
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst=self.pg1.local_ip4,
+ psrc=self.pg1.remote_hosts[6].ip4))
+
+ self.pg2.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg2.local_mac,
+ self.pg2.remote_mac,
+ self.pg1.local_ip4,
+ self.pg1.remote_hosts[6].ip4)
+
+ #
+ # An attached host route out of pg2 for an undiscovered hosts generates
+ # an ARP request with the unnumbered address as the source
+ #
+ att_unnum = VppIpRoute(self, self.pg1.remote_hosts[7].ip4, 32,
+ [VppRoutePath("0.0.0.0",
+ self.pg2.sw_if_index)])
+ att_unnum.add_vpp_config()
+
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=self.pg0.remote_ip4,
+ dst=self.pg1._remote_hosts[7].ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw())
+
+ self.pg0.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(1)
+
+ self.verify_arp_req(rx[0],
+ self.pg2.local_mac,
+ self.pg1.local_ip4,
+ self.pg1._remote_hosts[7].ip4)
+
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst=self.pg1.local_ip4,
+ psrc=self.pg1.remote_hosts[7].ip4))
+
+ self.pg2.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg2.local_mac,
+ self.pg2.remote_mac,
+ self.pg1.local_ip4,
+ self.pg1.remote_hosts[7].ip4)
+
+ #
+ # An attached host route as yet unresolved out of pg2 for an
+ # undiscovered host, an ARP requests begets a response.
+ #
+ att_unnum1 = VppIpRoute(self, self.pg1.remote_hosts[8].ip4, 32,
+ [VppRoutePath("0.0.0.0",
+ self.pg2.sw_if_index)])
+ att_unnum1.add_vpp_config()
+
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst=self.pg1.local_ip4,
+ psrc=self.pg1.remote_hosts[8].ip4))
+
+ self.pg2.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg2.local_mac,
+ self.pg2.remote_mac,
+ self.pg1.local_ip4,
+ self.pg1.remote_hosts[8].ip4)
+
+ #
# ERROR Cases
# 1 - don't respond to ARP request for address not within the
# interface's sub-net
- #
+ # 1a - nor within the unnumbered subnet
p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
ARP(op="who-has",
hwsrc=self.pg0.remote_mac,
@@ -313,6 +446,14 @@ class ARPTestCase(VppTestCase):
psrc=self.pg0.remote_ip4))
self.send_and_assert_no_replies(self.pg0, p,
"ARP req for non-local destination")
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg1.remote_hosts[7].ip4))
+ self.send_and_assert_no_replies(
+ self.pg0, p,
+ "ARP req for non-local destination - unnum")
#
# 2 - don't respond to ARP request from an address not within the
@@ -325,6 +466,14 @@ class ARPTestCase(VppTestCase):
pdst=self.pg0.local_ip4))
self.send_and_assert_no_replies(self.pg0, p,
"ARP req for non-local source")
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ psrc="10.10.10.3",
+ pdst=self.pg0.local_ip4))
+ self.send_and_assert_no_replies(
+ self.pg0, p,
+ "ARP req for non-local source - unnum")
#
# 3 - don't respond to ARP request from an address that belongs to
@@ -358,6 +507,10 @@ class ARPTestCase(VppTestCase):
static_arp.remove_vpp_config()
self.pg2.unset_unnumbered(self.pg1.sw_if_index)
+ # need this to flush the adj-fibs
+ self.pg2.unset_unnumbered(self.pg1.sw_if_index)
+ self.pg2.admin_down()
+
def test_proxy_arp(self):
""" Proxy ARP """
@@ -500,7 +653,7 @@ class ARPTestCase(VppTestCase):
#
# clean up on interface 2
#
- self.pg2.set_unnumbered(self.pg1.sw_if_index)
+ self.pg2.unset_unnumbered(self.pg1.sw_if_index)
def test_mpls(self):
""" MPLS """
@@ -560,6 +713,7 @@ class ARPTestCase(VppTestCase):
55,
self.pg0.remote_ip4,
"10.0.0.1")
+ self.pg2.unconfig_ip4()
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_interface.py b/test/vpp_interface.py
index 8135bc84d0e..aeaf27a898e 100644
--- a/test/vpp_interface.py
+++ b/test/vpp_interface.py
@@ -336,7 +336,7 @@ class VppInterface(object):
ip_sw_if_index)
def unset_unnumbered(self, ip_sw_if_index):
- """ Unaet the interface to unnumbered via ip_sw_if_index """
+ """ Unset the interface to unnumbered via ip_sw_if_index """
self.test.vapi.sw_interface_set_unnumbered(
self.sw_if_index,
ip_sw_if_index,