ip: Replace Sematics for Interface IP addresses
[vpp.git] / test / vpp_ip_route.py
index 18c27ff..d871f7a 100644 (file)
@@ -4,14 +4,21 @@
   object abstractions for representing IP routes in VPP
 """
 
-from vpp_object import *
+from vpp_object import VppObject
 from socket import inet_pton, inet_ntop, AF_INET, AF_INET6
-from vpp_ip import *
+from vpp_ip import DpoProto, INVALID_INDEX, VppIpAddressUnion, \
+    VppIpMPrefix
+from ipaddress import ip_network, ip_address, IPv4Network, IPv6Network
 
 # from vnet/vnet/mpls/mpls_types.h
 MPLS_IETF_MAX_LABEL = 0xfffff
 MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1
 
+try:
+    text_type = unicode
+except NameError:
+    text_type = str
+
 
 class MRouteItfFlags:
     MFIB_ITF_FLAG_NONE = 0
@@ -30,43 +37,148 @@ class MRouteEntryFlags:
     MFIB_ENTRY_FLAG_INHERIT_ACCEPT = 8
 
 
+class FibPathProto:
+    FIB_PATH_NH_PROTO_IP4 = 0
+    FIB_PATH_NH_PROTO_IP6 = 1
+    FIB_PATH_NH_PROTO_MPLS = 2
+    FIB_PATH_NH_PROTO_ETHERNET = 3
+    FIB_PATH_NH_PROTO_BIER = 4
+    FIB_PATH_NH_PROTO_NSH = 5
+
+
+class FibPathType:
+    FIB_PATH_TYPE_NORMAL = 0
+    FIB_PATH_TYPE_LOCAL = 1
+    FIB_PATH_TYPE_DROP = 2
+    FIB_PATH_TYPE_UDP_ENCAP = 3
+    FIB_PATH_TYPE_BIER_IMP = 4
+    FIB_PATH_TYPE_ICMP_UNREACH = 5
+    FIB_PATH_TYPE_ICMP_PROHIBIT = 6
+    FIB_PATH_TYPE_SOURCE_LOOKUP = 7
+    FIB_PATH_TYPE_DVR = 8
+    FIB_PATH_TYPE_INTERFACE_RX = 9
+    FIB_PATH_TYPE_CLASSIFY = 10
+
+
+class FibPathFlags:
+    FIB_PATH_FLAG_NONE = 0
+    FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED = 1
+    FIB_PATH_FLAG_RESOLVE_VIA_HOST = 2
+    FIB_PATH_FLAG_POP_PW_CW = 4
+
+
 class MplsLspMode:
     PIPE = 0
     UNIFORM = 1
 
 
-def find_route(test, ip_addr, len, table_id=0, inet=AF_INET):
-    if inet == AF_INET:
-        s = 4
-        routes = test.vapi.ip_fib_dump()
+def mk_network(addr, len):
+    if ip_address(text_type(addr)).version == 4:
+        return IPv4Network("%s/%d" % (addr, len), strict=False)
+    else:
+        return IPv6Network("%s/%d" % (addr, len), strict=False)
+
+
+def ip_to_dpo_proto(addr):
+    if addr.version == 6:
+        return DpoProto.DPO_PROTO_IP6
     else:
-        s = 16
-        routes = test.vapi.ip6_fib_dump()
+        return DpoProto.DPO_PROTO_IP4
+
+
+def address_proto(ip_addr):
+    if ip_addr.ip_addr.version == 4:
+        return FibPathProto.FIB_PATH_NH_PROTO_IP4
+    else:
+        return FibPathProto.FIB_PATH_NH_PROTO_IP6
+
+
+def find_route(test, addr, len, table_id=0, sw_if_index=None):
+    prefix = mk_network(addr, len)
+
+    if 4 == prefix.version:
+        routes = test.vapi.ip_route_dump(table_id, False)
+    else:
+        routes = test.vapi.ip_route_dump(table_id, True)
 
-    route_addr = inet_pton(inet, ip_addr)
     for e in routes:
-        if route_addr == e.address[:s] \
-           and len == e.address_length \
-           and table_id == e.table_id:
+        if table_id == e.route.table_id \
+           and str(e.route.prefix) == str(prefix):
+            if not sw_if_index:
+                return True
+            else:
+                # should be only one path if the user is looking
+                # for the interface the route is reachable through
+                if e.route.n_paths != 1:
+                    return False
+                else:
+                    return (e.route.paths[0].sw_if_index == sw_if_index)
+
+    return False
+
+
+def find_route_in_dump(dump, route, table):
+    for r in dump:
+        if table.table_id == r.route.table_id \
+           and route.prefix == r.route.prefix:
+            if len(route.paths) == r.route.n_paths:
+                return True
+    return False
+
+
+def find_mroute_in_dump(dump, route, table):
+    for r in dump:
+        if table.table_id == r.route.table_id \
+           and route.prefix == r.route.prefix:
             return True
     return False
 
 
 def find_mroute(test, grp_addr, src_addr, grp_addr_len,
-                table_id=0, inet=AF_INET):
-    if inet == AF_INET:
-        s = 4
-        routes = test.vapi.ip_mfib_dump()
+                table_id=0):
+    ip_mprefix = VppIpMPrefix(text_type(src_addr),
+                              text_type(grp_addr),
+                              grp_addr_len)
+
+    if 4 == ip_mprefix.version:
+        routes = test.vapi.ip_mroute_dump(table_id, False)
     else:
-        s = 16
-        routes = test.vapi.ip6_mfib_dump()
-    gaddr = inet_pton(inet, grp_addr)
-    saddr = inet_pton(inet, src_addr)
+        routes = test.vapi.ip_mroute_dump(table_id, True)
+
     for e in routes:
-        if gaddr == e.grp_address[:s] \
-           and grp_addr_len == e.address_length \
-           and saddr == e.src_address[:s] \
-           and table_id == e.table_id:
+        if table_id == e.route.table_id and ip_mprefix == e.route.prefix:
+            return True
+    return False
+
+
+def find_mpls_route(test, table_id, label, eos_bit, paths=None):
+    dump = test.vapi.mpls_route_dump(table_id)
+    for e in dump:
+        if label == e.mr_route.mr_label \
+           and eos_bit == e.mr_route.mr_eos \
+           and table_id == e.mr_route.mr_table_id:
+            if not paths:
+                return True
+            else:
+                if (len(paths) != len(e.mr_route.mr_paths)):
+                    return False
+                for i in range(len(paths)):
+                    if (paths[i] != e.mr_route.mr_paths[i]):
+                        return False
+                return True
+    return False
+
+
+def fib_interface_ip_prefix(test, addr, len, sw_if_index):
+    # can't use python net here since we need the host bits in the prefix
+    prefix = "%s/%d" % (addr, len)
+    addrs = test.vapi.ip_address_dump(
+        sw_if_index,
+        is_ipv6=(6 == ip_address(addr).version))
+
+    for a in addrs:
+        if a.sw_if_index == sw_if_index and \
+           str(a.prefix) == prefix:
             return True
     return False
 
@@ -82,28 +194,46 @@ class VppIpTable(VppObject):
         self.is_ip6 = is_ip6
 
     def add_vpp_config(self):
-        self._test.vapi.ip_table_add_del(
-            self.table_id,
-            is_ipv6=self.is_ip6,
-            is_add=1)
+        self._test.vapi.ip_table_add_del(is_add=1,
+                                         table={'is_ip6': self.is_ip6,
+                                                'table_id': self.table_id})
         self._test.registry.register(self, self._test.logger)
+        return self
 
     def remove_vpp_config(self):
-        self._test.vapi.ip_table_add_del(
-            self.table_id,
-            is_ipv6=self.is_ip6,
-            is_add=0)
+        self._test.vapi.ip_table_add_del(is_add=0,
+                                         table={'is_ip6': self.is_ip6,
+                                                'table_id': self.table_id})
+
+    def replace_begin(self):
+        self._test.vapi.ip_table_replace_begin(
+            table={'is_ip6': self.is_ip6,
+                   'table_id': self.table_id})
+
+    def replace_end(self):
+        self._test.vapi.ip_table_replace_end(
+            table={'is_ip6': self.is_ip6,
+                   'table_id': self.table_id})
+
+    def flush(self):
+        self._test.vapi.ip_table_flush(table={'is_ip6': self.is_ip6,
+                                              'table_id': self.table_id})
+
+    def dump(self):
+        return self._test.vapi.ip_route_dump(self.table_id, self.is_ip6)
+
+    def mdump(self):
+        return self._test.vapi.ip_mroute_dump(self.table_id, self.is_ip6)
 
     def query_vpp_config(self):
+        if self.table_id == 0:
+            # the default table always exists
+            return False
         # find the default route
         return find_route(self._test,
                           "::" if self.is_ip6 else "0.0.0.0",
                           0,
-                          self.table_id,
-                          inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
-
-    def __str__(self):
-        return self.object_id()
+                          self.table_id)
 
     def object_id(self):
         return ("table-%s-%d" %
@@ -111,6 +241,102 @@ class VppIpTable(VppObject):
                  self.table_id))
 
 
+class VppIpInterfaceAddress(VppObject):
+
+    def __init__(self, test, intf, addr, len, bind=None):
+        self._test = test
+        self.intf = intf
+        self.addr = addr
+        self.len = len
+        self.prefix = "%s/%d" % (addr, len)
+        self.host_len = ip_network(self.prefix, strict=False).max_prefixlen
+        self.table_id = 0
+        if bind:
+            self.table_id = bind.table.table_id
+
+    def add_vpp_config(self):
+        self._test.vapi.sw_interface_add_del_address(
+            sw_if_index=self.intf.sw_if_index, prefix=self.prefix,
+            is_add=1)
+        self._test.registry.register(self, self._test.logger)
+        return self
+
+    def remove_vpp_config(self):
+        self._test.vapi.sw_interface_add_del_address(
+            sw_if_index=self.intf.sw_if_index, prefix=self.prefix,
+            is_add=0)
+
+    def query_vpp_config(self):
+        # search for the IP address mapping and the two expected
+        # FIB entries
+        v = ip_address(self.addr).version
+
+        if ((v == 4 and self.len < 31) or (v == 6 and self.len < 127)):
+            return (fib_interface_ip_prefix(self._test,
+                                            self.addr,
+                                            self.len,
+                                            self.intf.sw_if_index) &
+                    find_route(self._test,
+                               self.addr,
+                               self.len,
+                               table_id=self.table_id,
+                               sw_if_index=self.intf.sw_if_index) &
+                    find_route(self._test,
+                               self.addr,
+                               self.host_len,
+                               table_id=self.table_id,
+                               sw_if_index=self.intf.sw_if_index))
+        else:
+            return (fib_interface_ip_prefix(self._test,
+                                            self.addr,
+                                            self.len,
+                                            self.intf.sw_if_index) &
+                    find_route(self._test,
+                               self.addr,
+                               self.host_len,
+                               table_id=self.table_id,
+                               sw_if_index=self.intf.sw_if_index))
+
+    def object_id(self):
+        return "interface-ip-%s-%d-%s" % (self.intf,
+                                          self.table_id,
+                                          self.prefix)
+
+
+class VppIpInterfaceBind(VppObject):
+
+    def __init__(self, test, intf, table):
+        self._test = test
+        self.intf = intf
+        self.table = table
+
+    def add_vpp_config(self):
+        if self.table.is_ip6:
+            self.intf.set_table_ip6(self.table.table_id)
+        else:
+            self.intf.set_table_ip4(self.table.table_id)
+        self._test.registry.register(self, self._test.logger)
+        return self
+
+    def remove_vpp_config(self):
+        if 0 == self.table.table_id:
+            return
+        if self.table.is_ip6:
+            self.intf.set_table_ip6(0)
+        else:
+            self.intf.set_table_ip4(0)
+
+    def query_vpp_config(self):
+        if 0 == self.table.table_id:
+            return False
+        return self._test.vapi.sw_interface_get_table(
+            self.intf.sw_if_index,
+            self.table.is_ip6).vrf_id == self.table.table_id
+
+    def object_id(self):
+        return "interface-bind-%s-%s" % (self.intf, self.table)
+
+
 class VppMplsLabel(object):
     def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
         self.value = value
@@ -125,6 +351,54 @@ class VppMplsLabel(object):
                 'exp': self.exp,
                 'is_uniform': is_uniform}
 
+    def __eq__(self, other):
+        if isinstance(other, self.__class__):
+            return (self.value == other.value and
+                    self.ttl == other.ttl and
+                    self.exp == other.exp and
+                    self.mode == other.mode)
+        elif hasattr(other, 'label'):
+            return (self.value == other.label and
+                    self.ttl == other.ttl and
+                    self.exp == other.exp and
+                    (self.mode == MplsLspMode.UNIFORM) == other.is_uniform)
+        else:
+            return False
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class VppFibPathNextHop(object):
+    def __init__(self, addr,
+                 via_label=MPLS_LABEL_INVALID,
+                 next_hop_id=INVALID_INDEX):
+        self.addr = VppIpAddressUnion(addr)
+        self.via_label = via_label
+        self.obj_id = next_hop_id
+
+    def encode(self):
+        if self.via_label is not MPLS_LABEL_INVALID:
+            return {'via_label': self.via_label}
+        if self.obj_id is not INVALID_INDEX:
+            return {'obj_id': self.obj_id}
+        else:
+            return {'address': self.addr.encode()}
+
+    def proto(self):
+        if self.via_label is MPLS_LABEL_INVALID:
+            return address_proto(self.addr)
+        else:
+            return FibPathProto.FIB_PATH_NH_PROTO_MPLS
+
+    def __eq__(self, other):
+        if not isinstance(other, self.__class__):
+            # try the other instance's __eq__.
+            return NotImplemented
+        return (self.addr == other.addr and
+                self.via_label == other.via_label and
+                self.obj_id == other.obj_id)
+
 
 class VppRoutePath(object):
 
@@ -136,38 +410,24 @@ class VppRoutePath(object):
             labels=[],
             nh_via_label=MPLS_LABEL_INVALID,
             rpf_id=0,
-            is_interface_rx=0,
-            is_resolve_host=0,
-            is_resolve_attached=0,
-            is_source_lookup=0,
-            is_udp_encap=0,
-            is_dvr=0,
-            next_hop_id=0xffffffff,
-            proto=DpoProto.DPO_PROTO_IP4):
+            next_hop_id=INVALID_INDEX,
+            proto=None,
+            flags=FibPathFlags.FIB_PATH_FLAG_NONE,
+            type=FibPathType.FIB_PATH_TYPE_NORMAL):
         self.nh_itf = nh_sw_if_index
         self.nh_table_id = nh_table_id
-        self.nh_via_label = nh_via_label
         self.nh_labels = labels
         self.weight = 1
         self.rpf_id = rpf_id
         self.proto = proto
-        if self.proto is DpoProto.DPO_PROTO_IP6:
-            self.nh_addr = inet_pton(AF_INET6, nh_addr)
-        elif self.proto is DpoProto.DPO_PROTO_IP4:
-            self.nh_addr = inet_pton(AF_INET, nh_addr)
+        self.flags = flags
+        self.type = type
+        self.nh = VppFibPathNextHop(nh_addr, nh_via_label, next_hop_id)
+        if proto is None:
+            self.proto = self.nh.proto()
         else:
-            self.nh_addr = inet_pton(AF_INET6, "::")
-        self.is_resolve_host = is_resolve_host
-        self.is_resolve_attached = is_resolve_attached
-        self.is_interface_rx = is_interface_rx
-        self.is_source_lookup = is_source_lookup
-        self.is_rpf_id = 0
-        if rpf_id != 0:
-            self.is_rpf_id = 1
-            self.nh_itf = rpf_id
-        self.is_udp_encap = is_udp_encap
+            self.proto = proto
         self.next_hop_id = next_hop_id
-        self.is_dvr = is_dvr
 
     def encode_labels(self):
         lstack = []
@@ -177,39 +437,65 @@ class VppRoutePath(object):
             else:
                 lstack.append({'label': l,
                                'ttl': 255})
+        while (len(lstack) < 16):
+            lstack.append({})
+
         return lstack
 
     def encode(self):
-        return {'next_hop': self.nh_addr,
-                'weight': 1,
-                'afi': 0,
+        return {'weight': 1,
                 'preference': 0,
                 'table_id': self.nh_table_id,
+                'nh': self.nh.encode(),
                 'next_hop_id': self.next_hop_id,
                 'sw_if_index': self.nh_itf,
-                'afi': self.proto,
-                'is_udp_encap': self.is_udp_encap,
+                'rpf_id': self.rpf_id,
+                'proto': self.proto,
+                'type': self.type,
+                'flags': self.flags,
                 'n_labels': len(self.nh_labels),
                 'label_stack': self.encode_labels()}
 
     def __eq__(self, other):
-        return self.nh_addr == other.nh_addr
+        if isinstance(other, self.__class__):
+            return self.nh == other.nh
+        elif hasattr(other, 'sw_if_index'):
+            # vl_api_fib_path_t
+            if (len(self.nh_labels) != other.n_labels):
+                return False
+            for i in range(len(self.nh_labels)):
+                if (self.nh_labels[i] != other.label_stack[i]):
+                    return False
+            return self.nh_itf == other.sw_if_index
+        else:
+            return False
+
+    def __ne__(self, other):
+        return not (self == other)
 
 
 class VppMRoutePath(VppRoutePath):
 
     def __init__(self, nh_sw_if_index, flags,
                  nh=None,
-                 proto=DpoProto.DPO_PROTO_IP4,
-                 bier_imp=0):
+                 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
+                 type=FibPathType.FIB_PATH_TYPE_NORMAL,
+                 bier_imp=INVALID_INDEX):
         if not nh:
-            nh = "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0"
+            nh = "::" if proto is FibPathProto.FIB_PATH_NH_PROTO_IP6 \
+                 else "0.0.0.0"
         super(VppMRoutePath, self).__init__(nh,
                                             nh_sw_if_index,
-                                            proto=proto)
+                                            proto=proto,
+                                            type=type,
+                                            next_hop_id=bier_imp)
         self.nh_i_flags = flags
         self.bier_imp = bier_imp
 
+    def encode(self):
+        return {'path': super(VppMRoutePath, self).encode(),
+                'itf_flags': self.nh_i_flags}
+
 
 class VppIpRoute(VppObject):
     """
@@ -217,113 +503,96 @@ class VppIpRoute(VppObject):
     """
 
     def __init__(self, test, dest_addr,
-                 dest_addr_len, paths, table_id=0, is_ip6=0, is_local=0,
-                 is_unreach=0, is_prohibit=0, is_drop=0):
+                 dest_addr_len, paths, table_id=0, register=True):
         self._test = test
         self.paths = paths
-        self.dest_addr_len = dest_addr_len
         self.table_id = table_id
-        self.is_ip6 = is_ip6
-        self.is_local = is_local
-        self.is_unreach = is_unreach
-        self.is_prohibit = is_prohibit
-        self.is_drop = is_drop
-        self.dest_addr_p = dest_addr
-        if is_ip6:
-            self.dest_addr = inet_pton(AF_INET6, dest_addr)
-        else:
-            self.dest_addr = inet_pton(AF_INET, dest_addr)
+        self.prefix = mk_network(dest_addr, dest_addr_len)
+        self.register = register
+        self.stats_index = None
+        self.modified = False
+
+        self.encoded_paths = []
+        for path in self.paths:
+            self.encoded_paths.append(path.encode())
 
-    def modify(self, paths, is_local=0,
-               is_unreach=0, is_prohibit=0):
+    def __eq__(self, other):
+        if self.table_id == other.table_id and \
+           self.prefix == other.prefix:
+            return True
+        return False
+
+    def modify(self, paths):
         self.paths = paths
-        self.is_local = is_local
-        self.is_unreach = is_unreach
-        self.is_prohibit = is_prohibit
+        self.encoded_paths = []
+        for path in self.paths:
+            self.encoded_paths.append(path.encode())
+        self.modified = True
+
+        self._test.vapi.ip_route_add_del(route={'table_id': self.table_id,
+                                                'prefix': self.prefix,
+                                                'n_paths': len(
+                                                    self.encoded_paths),
+                                                'paths': self.encoded_paths,
+                                                },
+                                         is_add=1,
+                                         is_multipath=0)
 
     def add_vpp_config(self):
-        if self.is_local or self.is_unreach or \
-           self.is_prohibit or self.is_drop:
-            self._test.vapi.ip_add_del_route(
-                self.dest_addr,
-                self.dest_addr_len,
-                inet_pton(AF_INET6, "::"),
-                0xffffffff,
-                is_local=self.is_local,
-                is_unreach=self.is_unreach,
-                is_prohibit=self.is_prohibit,
-                is_drop=self.is_drop,
-                table_id=self.table_id,
-                is_ipv6=self.is_ip6)
-        else:
-            for path in self.paths:
-                lstack = path.encode_labels()
-
-                self._test.vapi.ip_add_del_route(
-                    self.dest_addr,
-                    self.dest_addr_len,
-                    path.nh_addr,
-                    path.nh_itf,
-                    table_id=self.table_id,
-                    next_hop_out_label_stack=lstack,
-                    next_hop_n_out_labels=len(lstack),
-                    next_hop_via_label=path.nh_via_label,
-                    next_hop_table_id=path.nh_table_id,
-                    next_hop_id=path.next_hop_id,
-                    is_ipv6=self.is_ip6,
-                    is_dvr=path.is_dvr,
-                    is_resolve_host=path.is_resolve_host,
-                    is_resolve_attached=path.is_resolve_attached,
-                    is_source_lookup=path.is_source_lookup,
-                    is_udp_encap=path.is_udp_encap,
-                    is_multipath=1 if len(self.paths) > 1 else 0)
-        self._test.registry.register(self, self._test.logger)
+        r = self._test.vapi.ip_route_add_del(
+            route={'table_id': self.table_id,
+                   'prefix': self.prefix,
+                   'n_paths': len(self.encoded_paths),
+                   'paths': self.encoded_paths,
+                   },
+            is_add=1,
+            is_multipath=0)
+        self.stats_index = r.stats_index
+        if self.register:
+            self._test.registry.register(self, self._test.logger)
+        return self
 
     def remove_vpp_config(self):
-        if self.is_local or self.is_unreach or \
-           self.is_prohibit or self.is_drop:
-            self._test.vapi.ip_add_del_route(
-                self.dest_addr,
-                self.dest_addr_len,
-                inet_pton(AF_INET6, "::"),
-                0xffffffff,
-                is_local=self.is_local,
-                is_unreach=self.is_unreach,
-                is_prohibit=self.is_prohibit,
+        # there's no need to issue different deletes for modified routes
+        # we do this only to test the two different ways to delete routes
+        # eiter by passing all the paths to remove and mutlipath=1 or
+        # passing no paths and multipath=0
+        if self.modified:
+            self._test.vapi.ip_route_add_del(
+                route={'table_id': self.table_id,
+                       'prefix': self.prefix,
+                       'n_paths': len(
+                           self.encoded_paths),
+                       'paths': self.encoded_paths},
                 is_add=0,
-                table_id=self.table_id,
-                is_ipv6=self.is_ip6)
+                is_multipath=1)
         else:
-            for path in self.paths:
-                self._test.vapi.ip_add_del_route(
-                    self.dest_addr,
-                    self.dest_addr_len,
-                    path.nh_addr,
-                    path.nh_itf,
-                    table_id=self.table_id,
-                    next_hop_table_id=path.nh_table_id,
-                    next_hop_via_label=path.nh_via_label,
-                    next_hop_id=path.next_hop_id,
-                    is_add=0,
-                    is_udp_encap=path.is_udp_encap,
-                    is_ipv6=self.is_ip6,
-                    is_dvr=path.is_dvr)
+            self._test.vapi.ip_route_add_del(
+                route={'table_id': self.table_id,
+                       'prefix': self.prefix,
+                       'n_paths': 0},
+                is_add=0,
+                is_multipath=0)
 
     def query_vpp_config(self):
         return find_route(self._test,
-                          self.dest_addr_p,
-                          self.dest_addr_len,
-                          self.table_id,
-                          inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
-
-    def __str__(self):
-        return self.object_id()
+                          self.prefix.network_address,
+                          self.prefix.prefixlen,
+                          self.table_id)
 
     def object_id(self):
-        return ("%d:%s/%d"
-                % (self.table_id,
-                   self.dest_addr_p,
-                   self.dest_addr_len))
+        return ("%s:table-%d-%s" % (
+            'ip6-route' if self.prefix.version == 6 else 'ip-route',
+                self.table_id,
+                self.prefix))
+
+    def get_stats_to(self):
+        c = self._test.statistics.get_counter("/net/route/to")
+        return c[0][self.stats_index]
+
+    def get_stats_via(self):
+        c = self._test.statistics.get_counter("/net/route/via")
+        return c[0][self.stats_index]
 
 
 class VppIpMRoute(VppObject):
@@ -333,122 +602,86 @@ class VppIpMRoute(VppObject):
 
     def __init__(self, test, src_addr, grp_addr,
                  grp_addr_len, e_flags, paths, table_id=0,
-                 rpf_id=0, is_ip6=0):
+                 rpf_id=0):
         self._test = test
         self.paths = paths
-        self.grp_addr_len = grp_addr_len
         self.table_id = table_id
         self.e_flags = e_flags
-        self.is_ip6 = is_ip6
         self.rpf_id = rpf_id
 
-        self.grp_addr_p = grp_addr
-        self.src_addr_p = src_addr
-        if is_ip6:
-            self.grp_addr = inet_pton(AF_INET6, grp_addr)
-            self.src_addr = inet_pton(AF_INET6, src_addr)
-        else:
-            self.grp_addr = inet_pton(AF_INET, grp_addr)
-            self.src_addr = inet_pton(AF_INET, src_addr)
+        self.prefix = VppIpMPrefix(src_addr, grp_addr, grp_addr_len)
+        self.encoded_paths = []
+        for path in self.paths:
+            self.encoded_paths.append(path.encode())
 
     def add_vpp_config(self):
-        for path in self.paths:
-            self._test.vapi.ip_mroute_add_del(self.src_addr,
-                                              self.grp_addr,
-                                              self.grp_addr_len,
+        r = self._test.vapi.ip_mroute_add_del(self.table_id,
+                                              self.prefix.encode(),
                                               self.e_flags,
-                                              path.proto,
-                                              path.nh_itf,
-                                              path.nh_addr,
-                                              path.nh_i_flags,
-                                              bier_imp=path.bier_imp,
-                                              rpf_id=self.rpf_id,
-                                              table_id=self.table_id,
-                                              is_ipv6=self.is_ip6)
+                                              self.rpf_id,
+                                              self.encoded_paths,
+                                              is_add=1)
+        self.stats_index = r.stats_index
         self._test.registry.register(self, self._test.logger)
+        return self
 
     def remove_vpp_config(self):
-        for path in self.paths:
-            self._test.vapi.ip_mroute_add_del(self.src_addr,
-                                              self.grp_addr,
-                                              self.grp_addr_len,
-                                              self.e_flags,
-                                              path.proto,
-                                              path.nh_itf,
-                                              path.nh_addr,
-                                              path.nh_i_flags,
-                                              table_id=self.table_id,
-                                              bier_imp=path.bier_imp,
-                                              is_add=0,
-                                              is_ipv6=self.is_ip6)
+        self._test.vapi.ip_mroute_add_del(self.table_id,
+                                          self.prefix.encode(),
+                                          self.e_flags,
+                                          self.rpf_id,
+                                          self.encoded_paths,
+                                          is_add=0)
 
     def update_entry_flags(self, flags):
         self.e_flags = flags
-        self._test.vapi.ip_mroute_add_del(self.src_addr,
-                                          self.grp_addr,
-                                          self.grp_addr_len,
+        self._test.vapi.ip_mroute_add_del(self.table_id,
+                                          self.prefix.encode(),
                                           self.e_flags,
-                                          0,
-                                          0xffffffff,
-                                          "",
-                                          0,
-                                          table_id=self.table_id,
-                                          is_ipv6=self.is_ip6)
+                                          self.rpf_id,
+                                          [],
+                                          is_add=1)
 
     def update_rpf_id(self, rpf_id):
         self.rpf_id = rpf_id
-        self._test.vapi.ip_mroute_add_del(self.src_addr,
-                                          self.grp_addr,
-                                          self.grp_addr_len,
+        self._test.vapi.ip_mroute_add_del(self.table_id,
+                                          self.prefix.encode(),
                                           self.e_flags,
-                                          0,
-                                          0xffffffff,
-                                          "",
-                                          0,
-                                          rpf_id=self.rpf_id,
-                                          table_id=self.table_id,
-                                          is_ipv6=self.is_ip6)
+                                          self.rpf_id,
+                                          [],
+                                          is_add=1)
 
     def update_path_flags(self, itf, flags):
-        for path in self.paths:
-            if path.nh_itf == itf:
-                path.nh_i_flags = flags
-                break
-        self._test.vapi.ip_mroute_add_del(self.src_addr,
-                                          self.grp_addr,
-                                          self.grp_addr_len,
+        for p in range(len(self.paths)):
+            if self.paths[p].nh_itf == itf:
+                self.paths[p].nh_i_flags = flags
+            self.encoded_paths[p] = self.paths[p].encode()
+            break
+
+        self._test.vapi.ip_mroute_add_del(self.table_id,
+                                          self.prefix.encode(),
                                           self.e_flags,
-                                          path.proto,
-                                          path.nh_itf,
-                                          path.nh_addr,
-                                          path.nh_i_flags,
-                                          table_id=self.table_id,
-                                          is_ipv6=self.is_ip6)
+                                          self.rpf_id,
+                                          [self.encoded_paths[p]],
+                                          is_add=1,
+                                          is_multipath=0)
 
     def query_vpp_config(self):
         return find_mroute(self._test,
-                           self.grp_addr_p,
-                           self.src_addr_p,
-                           self.grp_addr_len,
-                           self.table_id,
-                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
-
-    def __str__(self):
-        return self.object_id()
+                           self.prefix.gaddr,
+                           self.prefix.saddr,
+                           self.prefix.length,
+                           self.table_id)
 
     def object_id(self):
-        if self.is_ip6:
-            return ("%d:(%s,%s/%d)"
-                    % (self.table_id,
-                       inet_ntop(AF_INET6, self.src_addr),
-                       inet_ntop(AF_INET6, self.grp_addr),
-                       self.grp_addr_len))
-        else:
-            return ("%d:(%s,%s/%d)"
-                    % (self.table_id,
-                       inet_ntop(AF_INET, self.src_addr),
-                       inet_ntop(AF_INET, self.grp_addr),
-                       self.grp_addr_len))
+        return ("%d:(%s,%s/%d)" % (self.table_id,
+                                   self.prefix.saddr,
+                                   self.prefix.gaddr,
+                                   self.prefix.length))
+
+    def get_stats(self):
+        c = self._test.statistics.get_counter("/net/mroute")
+        return c[0][self.stats_index]
 
 
 class VppMFibSignal(object):
@@ -461,15 +694,7 @@ class VppMFibSignal(object):
     def compare(self, signal):
         self.test.assertEqual(self.interface, signal.sw_if_index)
         self.test.assertEqual(self.route.table_id, signal.table_id)
-        self.test.assertEqual(self.route.grp_addr_len,
-                              signal.grp_address_len)
-        for i in range(self.route.grp_addr_len / 8):
-            self.test.assertEqual(self.route.grp_addr[i],
-                                  signal.grp_address[i])
-        if (self.route.grp_addr_len > 32):
-            for i in range(4):
-                self.test.assertEqual(self.route.src_addr[i],
-                                      signal.src_address[i])
+        self.test.assertEqual(self.route.prefix, signal.prefix)
 
 
 class VppMplsIpBind(VppObject):
@@ -482,44 +707,34 @@ class VppMplsIpBind(VppObject):
         self._test = test
         self.dest_addr_len = dest_addr_len
         self.dest_addr = dest_addr
+        self.ip_addr = ip_address(text_type(dest_addr))
         self.local_label = local_label
         self.table_id = table_id
         self.ip_table_id = ip_table_id
-        self.is_ip6 = is_ip6
-        if is_ip6:
-            self.dest_addrn = inet_pton(AF_INET6, dest_addr)
-        else:
-            self.dest_addrn = inet_pton(AF_INET, dest_addr)
+        self.prefix = mk_network(dest_addr, dest_addr_len)
 
     def add_vpp_config(self):
         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
-                                            self.dest_addrn,
-                                            self.dest_addr_len,
+                                            self.prefix,
                                             table_id=self.table_id,
-                                            ip_table_id=self.ip_table_id,
-                                            is_ip4=(self.is_ip6 == 0))
+                                            ip_table_id=self.ip_table_id)
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
-                                            self.dest_addrn,
-                                            self.dest_addr_len,
+                                            self.prefix,
                                             table_id=self.table_id,
                                             ip_table_id=self.ip_table_id,
-                                            is_bind=0,
-                                            is_ip4=(self.is_ip6 == 0))
+                                            is_bind=0)
 
     def query_vpp_config(self):
-        dump = self._test.vapi.mpls_fib_dump()
+        dump = self._test.vapi.mpls_route_dump(self.table_id)
         for e in dump:
-            if self.local_label == e.label \
-               and self.table_id == e.table_id:
+            if self.local_label == e.mr_route.mr_label \
+               and self.table_id == e.mr_route.mr_table_id:
                 return True
         return False
 
-    def __str__(self):
-        return self.object_id()
-
     def object_id(self):
         return ("%d:%s binds %d:%s/%d"
                 % (self.table_id,
@@ -549,15 +764,12 @@ class VppMplsTable(VppObject):
             is_add=0)
 
     def query_vpp_config(self):
-        # find the default route
-        dump = self._test.vapi.mpls_fib_dump()
-        if len(dump):
-            return True
+        dump = self._test.vapi.mpls_table_dump()
+        for d in dump:
+            if d.mt_table.mt_table_id == self.table_id:
+                return True
         return False
 
-    def __str__(self):
-        return self.object_id()
-
     def object_id(self):
         return ("table-mpls-%d" % (self.table_id))
 
@@ -568,61 +780,56 @@ class VppMplsRoute(VppObject):
     """
 
     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
-                 is_multicast=0):
+                 is_multicast=0,
+                 eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP4):
         self._test = test
         self.paths = paths
         self.local_label = local_label
         self.eos_bit = eos_bit
+        self.eos_proto = eos_proto
         self.table_id = table_id
         self.is_multicast = is_multicast
 
     def add_vpp_config(self):
-        is_multipath = len(self.paths) > 1
+        paths = []
         for path in self.paths:
-            lstack = path.encode_labels()
-
-            self._test.vapi.mpls_route_add_del(
-                self.local_label,
-                self.eos_bit,
-                path.proto,
-                path.nh_addr,
-                path.nh_itf,
-                is_multicast=self.is_multicast,
-                is_multipath=is_multipath,
-                table_id=self.table_id,
-                is_interface_rx=path.is_interface_rx,
-                is_rpf_id=path.is_rpf_id,
-                next_hop_out_label_stack=lstack,
-                next_hop_n_out_labels=len(lstack),
-                next_hop_via_label=path.nh_via_label,
-                next_hop_table_id=path.nh_table_id)
+            paths.append(path.encode())
+
+        r = self._test.vapi.mpls_route_add_del(self.table_id,
+                                               self.local_label,
+                                               self.eos_bit,
+                                               self.eos_proto,
+                                               self.is_multicast,
+                                               paths, 1, 0)
+        self.stats_index = r.stats_index
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
+        paths = []
         for path in self.paths:
-            self._test.vapi.mpls_route_add_del(self.local_label,
-                                               self.eos_bit,
-                                               path.proto,
-                                               path.nh_addr,
-                                               path.nh_itf,
-                                               is_rpf_id=path.is_rpf_id,
-                                               table_id=self.table_id,
-                                               is_add=0)
+            paths.append(path.encode())
 
-    def query_vpp_config(self):
-        dump = self._test.vapi.mpls_fib_dump()
-        for e in dump:
-            if self.local_label == e.label \
-               and self.eos_bit == e.eos_bit \
-               and self.table_id == e.table_id:
-                return True
-        return False
+        self._test.vapi.mpls_route_add_del(self.table_id,
+                                           self.local_label,
+                                           self.eos_bit,
+                                           self.eos_proto,
+                                           self.is_multicast,
+                                           paths, 0, 0)
 
-    def __str__(self):
-        return self.object_id()
+    def query_vpp_config(self):
+        return find_mpls_route(self._test, self.table_id,
+                               self.local_label, self.eos_bit)
 
     def object_id(self):
-        return ("%d:%s/%d"
+        return ("mpls-route-%d:%s/%d"
                 % (self.table_id,
                    self.local_label,
-                   20+self.eos_bit))
+                   20 + self.eos_bit))
+
+    def get_stats_to(self):
+        c = self._test.statistics.get_counter("/net/route/to")
+        return c[0][self.stats_index]
+
+    def get_stats_via(self):
+        c = self._test.statistics.get_counter("/net/route/via")
+        return c[0][self.stats_index]