def add_geneve_tunnel(node, local_address, remote_address, vni, multicast_if=None, encap_vrf=0, l3_mode=False, next_index=None): """Add GENEVE tunnel on the specified VPP node. :param node: Topology node. :param local_address: Local IP address. :param remote_address: Remote IP address. :param vni: Virtual network ID. :param multicast_if: Interface key of multicast interface; used only if remote is multicast. (Default value = None) :param encap_vrf: The FIB ID for sending unicast GENEVE encap packets or receiving multicast packets. (Default value = 0) :param l3_mode: Use geneve tunnel in L3 mode (ip routing) if Tue else in L2 mode (L2 switching). (Default value = False) :param next_index: The index of the next node. :type node: dict :type local_address: str :type remote_address: str :type vni: int :type multicast_if: str :type encap_vrf: int :type l3_mode: bool :type next_index: int :returns: SW interface index of created geneve tunnel. :rtype: int """ cmd = u"geneve_add_del_tunnel2" args = dict(is_add=True, local_address=IPAddress.create_ip_address_object( ip_address(local_address)), remote_address=IPAddress.create_ip_address_object( ip_address(remote_address)), mcast_sw_if_index=Topology.get_interface_sw_index( node, multicast_if) if multicast_if else Constants.BITWISE_NON_ZERO, encap_vrf_id=int(encap_vrf), decap_next_index=next_index if l3_mode else Constants.BITWISE_NON_ZERO, vni=int(vni), l3_mode=l3_mode) err_msg = f"Failed to configure GENEVE tunnel on host {node[u'host']}!" with PapiSocketExecutor(node) as papi_exec: sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg) if_key = Topology.add_new_port(node, u"geneve_tunnel") Topology.update_interface_sw_if_index(node, if_key, sw_if_index) ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index) Topology.update_interface_name(node, if_key, ifc_name) ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index) Topology.update_interface_mac_address(node, if_key, ifc_mac) return sw_if_index
def gbp_endpoint_add(node, sw_if_index, ip_addr, mac_addr, sclass): """Add GBP endpoint. :param node: Node to add GBP endpoint on. :param sw_if_index: SW index of interface. :param ip_addr: GBP route domain ID. :param mac_addr: MAC address. :param sclass: Source CLASS. :type node: dict :type sw_if_index: int :type ip_addr: str :type mac_addr: str :type sclass: int """ cmd = u"gbp_endpoint_add" err_msg = f"Failed to add GBP endpoint on {node[u'host']}!" ips = list() ips.append(IPAddress.create_ip_address_object(ip_address(ip_addr))) tun_src = IPAddress.create_ip_address_object(ip_address(u"0.0.0.0")) tun_dst = IPAddress.create_ip_address_object(ip_address(u"0.0.0.0")) args_in = dict(endpoint=dict( sw_if_index=sw_if_index, ips=ips, n_ips=len(ips), mac=L2Util.mac_to_bin(mac_addr), sclass=sclass, flags=getattr(GBPEndpointFlags, u"GBP_API_ENDPOINT_FLAG_EXTERNAL").value, tun=dict(src=tun_src, dst=tun_dst))) with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args_in).get_reply(err_msg)
def create_prefix_object(ip_addr, addr_len): """Create prefix object. :param ip_addr: IPv4 or IPv6 address. :param addr_len: Length of IP address. :type ip_addr: IPv4Address or IPv6Address :type addr_len: int :returns: Prefix object. :rtype: dict """ addr = IPAddress.create_ip_address_object(ip_addr) return dict(len=int(addr_len), address=addr)
def gbp_subnet_add_del(node, address, subnet_length, sclass, rd_id=1, sw_if_index=0xffffffff): """Add external interface to GBP. :param node: Node to add GBP subnet on. :param address: IPv4 adddress. :param subnet_length: IPv4 address subnet. :param sclass: Source CLASS. :param rd_id: GBP route domain ID. :param sw_if_index: Interface index. :type node: dict :type address: int :type subnet_length: int :type sclass: int :type rd_id: int :type sw_if_index: int """ cmd = u"gbp_subnet_add_del" err_msg = f"Failed to add GBP subnet on {node[u'host']}!" args_in = dict( is_add=True, subnet=dict(type=getattr(GBPSubnetType, u"GBP_API_SUBNET_L3_OUT").value, sw_if_index=sw_if_index, sclass=sclass, prefix=dict(address=IPAddress.create_ip_address_object( ip_address(address)), len=int(subnet_length)), rd_id=rd_id)) with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args_in).get_reply(err_msg)
def vpp_create_vxlan_and_vlan_interfaces( node, node_vxlan_if, node_vlan_if, vxlan_count, vni_start, src_ip_start, dst_ip_start, ip_step): """ Configure IPs, create VXLAN interfaces and VLAN sub-interfaces on VPP node. :param node: VPP node. :param node_vxlan_if: VPP node interface key to create VXLAN tunnel interfaces. :param node_vlan_if: VPP node interface key to create VLAN sub-interface. :param vxlan_count: Number of tunnel interfaces to create. :param vni_start: VNI start ID. :param src_ip_start: VXLAN tunnel source IP address start. :param dst_ip_start: VXLAN tunnel destination IP address start. :param ip_step: IP address incremental step. :type node: dict :type node_vxlan_if: str :type node_vlan_if: str :type vxlan_count: int :type vni_start: int :type src_ip_start: str :type dst_ip_start: str :type ip_step: int :returns: Number of created VXLAN interfaces. :rtype: int """ src_ip_start = ip_address(src_ip_start) dst_ip_start = ip_address(dst_ip_start) if vxlan_count > 10: commands = list() for i in range(0, vxlan_count): try: src_ip = src_ip_start + i * ip_step dst_ip = dst_ip_start + i * ip_step except AddressValueError: logger.warn( u"Can't do more iterations - IP address limit " u"has been reached." ) vxlan_count = i break commands.append( f"sw_interface_add_del_address sw_if_index " f"{Topology.get_interface_sw_index(node, node_vxlan_if)} " f"{src_ip}/{128 if src_ip.version == 6 else 32}\n" ) commands.append( f"vxlan_add_del_tunnel src {src_ip} dst {dst_ip} " f"vni {vni_start + i}\n" ) commands.append( f"create_vlan_subif sw_if_index " f"{Topology.get_interface_sw_index(node, node_vlan_if)} " f"vlan {i + 1}\n" ) VatExecutor().write_and_execute_script( node, u"/tmp/create_vxlan_interfaces.config", commands ) return vxlan_count cmd1 = u"sw_interface_add_del_address" args1 = dict( sw_if_index=InterfaceUtil.get_interface_index(node, node_vxlan_if), is_add=True, del_all=False, prefix=None ) cmd2 = u"vxlan_add_del_tunnel_v3" args2 = dict( is_add=True, instance=Constants.BITWISE_NON_ZERO, src_address=None, dst_address=None, mcast_sw_if_index=Constants.BITWISE_NON_ZERO, encap_vrf_id=0, decap_next_index=Constants.BITWISE_NON_ZERO, vni=None ) cmd3 = u"create_vlan_subif" args3 = dict( sw_if_index=InterfaceUtil.get_interface_index( node, node_vlan_if), vlan_id=None ) with PapiSocketExecutor(node) as papi_exec: for i in range(0, vxlan_count): try: src_ip = src_ip_start + i * ip_step dst_ip = dst_ip_start + i * ip_step except AddressValueError: logger.warn( u"Can't do more iterations - IP address limit " u"has been reached." ) vxlan_count = i break args1[u"prefix"] = IPUtil.create_prefix_object( src_ip, 128 if src_ip_start.version == 6 else 32 ) args2[u"src_address"] = IPAddress.create_ip_address_object( src_ip ) args2[u"dst_address"] = IPAddress.create_ip_address_object( dst_ip ) args2[u"vni"] = int(vni_start) + i args3[u"vlan_id"] = i + 1 history = bool(not 1 < i < vxlan_count - 1) papi_exec.add(cmd1, history=history, **args1).\ add(cmd2, history=history, **args2).\ add(cmd3, history=history, **args3) papi_exec.get_replies() return vxlan_count
def vpp_put_vxlan_and_vlan_interfaces_to_bridge_domain( node, node_vxlan_if, vxlan_count, op_node, op_node_if, dst_ip_start, ip_step, bd_id_start): """ Configure ARPs and routes for VXLAN interfaces and put each pair of VXLAN tunnel interface and VLAN sub-interface to separate bridge-domain. :param node: VPP node. :param node_vxlan_if: VPP node interface key where VXLAN tunnel interfaces have been created. :param vxlan_count: Number of tunnel interfaces. :param op_node: Opposite VPP node for VXLAN tunnel interfaces. :param op_node_if: Opposite VPP node interface key for VXLAN tunnel interfaces. :param dst_ip_start: VXLAN tunnel destination IP address start. :param ip_step: IP address incremental step. :param bd_id_start: Bridge-domain ID start. :type node: dict :type node_vxlan_if: str :type vxlan_count: int :type op_node: dict :type op_node_if: :type dst_ip_start: str :type ip_step: int :type bd_id_start: int """ dst_ip_start = ip_address(dst_ip_start) if vxlan_count > 1: idx_vxlan_if = Topology.get_interface_sw_index(node, node_vxlan_if) commands = list() for i in range(0, vxlan_count): dst_ip = dst_ip_start + i * ip_step commands.append( f"exec ip neighbor " f"{Topology.get_interface_name(node, node_vxlan_if)} " f"{dst_ip} " f"{Topology.get_interface_mac(op_node, op_node_if)} static " f"\n" ) commands.append( f"ip_route_add_del " f"{dst_ip}/{128 if dst_ip.version == 6 else 32} count 1 " f"via {dst_ip} sw_if_index {idx_vxlan_if}\n" ) sw_idx_vxlan = Topology.get_interface_sw_index( node, f"vxlan_tunnel{i + 1}" ) commands.append( f"sw_interface_set_l2_bridge sw_if_index {sw_idx_vxlan} " f"bd_id {bd_id_start + i} shg 0 enable\n" ) sw_idx_vlan = Topology.get_interface_sw_index( node, f"vlan_subif{i + 1}" ) commands.append( f"sw_interface_set_l2_bridge sw_if_index {sw_idx_vlan} " f"bd_id {bd_id_start + i} shg 0 enable\n" ) VatExecutor().write_and_execute_script( node, u"/tmp/configure_routes_and_bridge_domains.config", commands ) return cmd1 = u"ip_neighbor_add_del" neighbor = dict( sw_if_index=Topology.get_interface_sw_index(node, node_vxlan_if), flags=0, mac_address=Topology.get_interface_mac(op_node, op_node_if), ip_address=u"" ) args1 = dict( is_add=1, neighbor=neighbor ) cmd2 = u"ip_route_add_del" kwargs = dict( interface=node_vxlan_if, gateway=str(dst_ip_start) ) route = IPUtil.compose_vpp_route_structure( node, str(dst_ip_start), 128 if dst_ip_start.version == 6 else 32, **kwargs ) args2 = dict( is_add=1, is_multipath=0, route=route ) cmd3 = u"sw_interface_set_l2_bridge" args3 = dict( rx_sw_if_index=None, bd_id=None, shg=0, port_type=0, enable=1 ) args4 = dict( rx_sw_if_index=None, bd_id=None, shg=0, port_type=0, enable=1 ) with PapiSocketExecutor(node) as papi_exec: for i in range(0, vxlan_count): args1[u"neighbor"][u"ip_address"] = \ str(dst_ip_start + i * ip_step) args2[u"route"][u"prefix"][u"address"][u"un"] = \ IPAddress.union_addr(dst_ip_start + i * ip_step) args2[u"route"][u"paths"][0][u"nh"][u"address"] = \ IPAddress.union_addr(dst_ip_start + i * ip_step) args3[u"rx_sw_if_index"] = Topology.get_interface_sw_index( node, f"vxlan_tunnel{i+1}" ) args3[u"bd_id"] = int(bd_id_start+i) args4[u"rx_sw_if_index"] = Topology.get_interface_sw_index( node, f"vlan_subif{i+1}" ) args4[u"bd_id"] = int(bd_id_start+i) history = bool(not 1 < i < vxlan_count - 1) papi_exec.add(cmd1, history=history, **args1). \ add(cmd2, history=history, **args2). \ add(cmd3, history=history, **args3). \ add(cmd3, history=history, **args4) papi_exec.get_replies()
def compose_vpp_route_structure(node, network, prefix_len, **kwargs): """Create route object for ip_route_add_del api call. :param node: VPP node. :param network: Route destination network address. :param prefix_len: Route destination network prefix length. :param kwargs: Optional key-value arguments: gateway: Route gateway address. (str) interface: Route interface. (str) vrf: VRF table ID. (int) count: number of IP addresses to add starting from network IP (int) local: The route is local with same prefix (increment is 1). If None, then is not used. (bool) lookup_vrf: VRF table ID for lookup. (int) multipath: Enable multipath routing. (bool) weight: Weight value for unequal cost multipath routing. (int) :type node: dict :type network: str :type prefix_len: int :type kwargs: dict :returns: route parameter basic structure :rtype: dict """ interface = kwargs.get(u"interface", u"") gateway = kwargs.get(u"gateway", u"") net_addr = ip_address(network) prefix = IPUtil.create_prefix_object(net_addr, prefix_len) paths = list() n_hop = dict(address=IPAddress.union_addr(ip_address(gateway)) if gateway else 0, via_label=MPLS_LABEL_INVALID, obj_id=Constants.BITWISE_NON_ZERO) path = dict(sw_if_index=InterfaceUtil.get_interface_index( node, interface) if interface else Constants.BITWISE_NON_ZERO, table_id=int(kwargs.get(u"lookup_vrf", 0)), rpf_id=Constants.BITWISE_NON_ZERO, weight=int(kwargs.get(u"weight", 1)), preference=1, type=getattr( FibPathType, u"FIB_PATH_TYPE_LOCAL" if kwargs.get(u"local", False) else u"FIB_PATH_TYPE_NORMAL").value, flags=getattr(FibPathFlags, u"FIB_PATH_FLAG_NONE").value, proto=getattr( FibPathNhProto, u"FIB_PATH_NH_PROTO_IP6" if net_addr.version == 6 else u"FIB_PATH_NH_PROTO_IP4").value, nh=n_hop, n_labels=0, label_stack=list(0 for _ in range(16))) paths.append(path) route = dict(table_id=int(kwargs.get(u"vrf", 0)), prefix=prefix, n_paths=len(paths), paths=paths) return route
def configure_sr_localsid(node, local_sid, behavior, interface=None, next_hop=None, fib_table=None, out_if=None, in_if=None, src_addr=None, sid_list=None): """Create SRv6 LocalSID and binds it to a particular behaviour on the given node. :param node: Given node to create localSID on. :param local_sid: LocalSID IPv6 address. :param behavior: SRv6 LocalSID function. :param interface: Interface name (Optional, required for L2/L3 xconnects). :param next_hop: Next hop IPv4/IPv6 address (Optional, required for L3 xconnects). :param fib_table: FIB table for IPv4/IPv6 lookup (Optional, required for L3 routing). :param out_if: Interface name of local interface for sending traffic towards the Service Function (Optional, required for SRv6 endpoint to SR-unaware appliance). :param in_if: Interface name of local interface receiving the traffic coming back from the Service Function (Optional, required for SRv6 endpoint to SR-unaware appliance). :param src_addr: Source address on the packets coming back on in_if interface (Optional, required for SRv6 endpoint to SR-unaware appliance via static proxy). :param sid_list: SID list (Optional, required for SRv6 endpoint to SR-unaware appliance via static proxy). :type node: dict :type local_sid: str :type behavior: str :type interface: str :type next_hop: str :type fib_table: str :type out_if: str :type in_if: str :type src_addr: str :type sid_list: list :raises ValueError: If required parameter is missing. """ beh = behavior.replace(u".", u"_").upper() # There is no SRv6Behaviour enum defined for functions from SRv6 plugins # so we need to use CLI command to configure it. if beh in (getattr(SRv6Behavior, u"END_AD").name, getattr(SRv6Behavior, u"END_AS").name, getattr(SRv6Behavior, u"END_AM").name): if beh == getattr(SRv6Behavior, u"END_AS").name: if next_hop is None or out_if is None or in_if is None or \ src_addr is None or sid_list is None: raise ValueError(f"Required parameter(s) missing.\n" f"next_hop:{next_hop}\n " f"out_if:{out_if}\n" f"in_if:{in_if}\n" f"src_addr:{src_addr}\n" f"sid_list:{sid_list}") sid_conf = f"next {u' next '.join(sid_list)}" params = f"nh {next_hop} oif {out_if} iif {in_if} " \ f"src {src_addr} {sid_conf}" else: if next_hop is None or out_if is None or in_if is None: raise ValueError(f"Required parameter(s) missing.\n" f"next_hop:{next_hop}\n" f"out_if:{out_if}\n" f"in_if:{in_if}") params = f"nh {next_hop} oif {out_if} iif {in_if}" cli_cmd = f"sr localsid address {local_sid} behavior {behavior} " \ f"{params}" PapiSocketExecutor.run_cli_cmd(node, cli_cmd) return cmd = u"sr_localsid_add_del" args = dict(is_del=False, localsid=IPv6Address(local_sid).packed, end_psp=False, behavior=getattr(SRv6Behavior, beh).value, sw_if_index=Constants.BITWISE_NON_ZERO, vlan_index=0, fib_table=0, nh_addr=0) err_msg = f"Failed to add SR localSID {local_sid} " \ f"host {node[u'host']}" if beh in (getattr(SRv6Behavior, u"END_X").name, getattr(SRv6Behavior, u"END_DX4").name, getattr(SRv6Behavior, u"END_DX6").name): if interface is None or next_hop is None: raise ValueError(f"Required parameter(s) missing.\n" f"interface:{interface}\n" f"next_hop:{next_hop}") args[u"sw_if_index"] = InterfaceUtil.get_interface_index( node, interface) args[u"nh_addr"] = IPAddress.create_ip_address_object( ip_address(next_hop)) elif beh == getattr(SRv6Behavior, u"END_DX2").name: if interface is None: raise ValueError( f"Required parameter missing.\ninterface: {interface}") args[u"sw_if_index"] = InterfaceUtil.get_interface_index( node, interface) elif beh in (getattr(SRv6Behavior, u"END_DT4").name, getattr(SRv6Behavior, u"END_DT6").name): if fib_table is None: raise ValueError(f"Required parameter missing.\n" f"fib_table: {fib_table}") args[u"fib_table"] = fib_table with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args).get_reply(err_msg)