def start_the_tcpdump_on_the_node(from_node, from_port, filter_ip): """ Start the tcpdump on the frome_node. :param from_node: Will execute the tcpdump on this node. :param from_port: Will capture the packets on this interface. :param filter_ip: filter the dest ip. :type from_node: dict :type from_port: str :type filter_ip: str :returns: none :raises RuntimeError: If the script "start_tcpdump.sh" fails. """ interface_name = Topology.get_interface_name(from_node, from_port) ssh = SSH() ssh.connect(from_node) cmd = 'cd {0}/nsh_sfc_tests/sfc_scripts/ && sudo ./start_tcpdump.sh ' \ '{1} {2}'.format(con.REMOTE_FW_DIR, interface_name, filter_ip) (ret_code, _, _) = ssh.exec_command(cmd, timeout=600) if ret_code != 0: raise RuntimeError( 'Failed to exec start_tcpdump.sh at node {0}'.format( from_node['host']))
def vpp_ip_probe(node, interface, addr, if_type="key"): """Run ip probe on VPP node. :param node: VPP node. :param interface: Interface key or name. :param addr: IPv4/IPv6 address. :param if_type: Interface type :type node: dict :type interface: str :type addr: str :type if_type: str :raises ValueError: If the if_type is unknown. :raises Exception: If vpp probe fails. """ ssh = SSH() ssh.connect(node) if if_type == "key": iface_name = Topology.get_interface_name(node, interface) elif if_type == "name": iface_name = interface else: raise ValueError("if_type unknown: {0}".format(if_type)) cmd = "{c}".format(c=Constants.VAT_BIN_NAME) cmd_input = 'exec ip probe {dev} {ip}'.format(dev=iface_name, ip=addr) (ret_code, _, _) = ssh.exec_command_sudo(cmd, cmd_input) if int(ret_code) != 0: raise Exception('VPP ip probe {dev} {ip} failed on {h}'.format( dev=iface_name, ip=addr, h=node['host']))
def get_node_port_ipv6_address(node, iface_key, nodes_addr): """Return IPv6 address of the node port. :param node: Node in the topology. :param iface_key: Interface key of the node. :param nodes_addr: Nodes IPv6 addresses. :type node: dict :type iface_key: str :type nodes_addr: dict :return: IPv6 address string. :rtype: str """ interface = Topology.get_interface_name(node, iface_key) for net in nodes_addr.values(): for port in net['ports'].values(): host = port.get('node') dev = port.get('if') if host == node['host'] and dev == interface: ip_addr = port.get('addr') if ip_addr is not None: return ip_addr else: raise Exception( 'Node {n} port {p} IPv6 address is not set'.format( n=node['host'], p=interface)) raise Exception('Node {n} port {p} IPv6 address not found.'.format( n=node['host'], p=interface))
def set_interface_ethernet_mtu(node, iface_key, mtu): """Set Ethernet MTU for specified interface. Function can be used only for TGs. :param node: Node where the interface is. :param iface_key: Interface key from topology file. :param mtu: MTU to set. :type node: dict :type iface_key: str :type mtu: int :returns: Nothing. :raises ValueError: If the node type is "DUT". :raises ValueError: If the node has an unknown node type. """ if node['type'] == NodeType.DUT: raise ValueError('Node {}: Setting Ethernet MTU for interface ' 'on DUT nodes not supported', node['host']) elif node['type'] == NodeType.TG: iface_name = Topology.get_interface_name(node, iface_key) cmd = 'ip link set {} mtu {}'.format(iface_name, mtu) exec_cmd_no_error(node, cmd, sudo=True) else: raise ValueError('Node {} has unknown NodeType: "{}"' .format(node['host'], node['type']))
def config_and_start_sfc_test(dut_node, dut_if1, dut_if2, if1_adj_mac, if2_adj_mac, testtype): """ Start the SFC functional on the dut_node. :param dut_node: Will execute the SFC on this node. :param dut_if1: The first ingress interface on the DUT. :param dut_if2: The last egress interface on the DUT. :param if1_adj_mac: The interface 1 adjacency MAC. :param if2_adj_mac: The interface 2 adjacency MAC. :param testtype: The SFC functional test type. (Classifier, Proxy Inbound, Proxy Outbound, SFF). :type dut_node: dict :type dut_if1: str :type dut_if2: str :type if1_adj_mac: str :type if2_adj_mac: str :type testtype: str :returns: none :raises RuntimeError: If the script execute fails. """ vpp_intf_name1 = Topology.get_interface_name(dut_node, dut_if1) vpp_intf_name2 = Topology.get_interface_name(dut_node, dut_if2) ssh = SSH() ssh.connect(dut_node) if testtype == "Classifier": exec_shell = "set_sfc_classifier.sh" elif testtype == "Proxy Inbound": exec_shell = "set_nsh_proxy_inbound.sh" elif testtype == "Proxy Outbound": exec_shell = "set_nsh_proxy_outbound.sh" else: exec_shell = "set_sfc_sff.sh" cmd = 'cd {0}/tests/nsh_sfc/sfc_scripts/ && sudo ./{1} {2} {3} {4} ' \ '{5}'.format(con.REMOTE_FW_DIR, exec_shell, vpp_intf_name1, vpp_intf_name2, if1_adj_mac, if2_adj_mac) (ret_code, _, _) = ssh.exec_command(cmd, timeout=600) if ret_code != 0: raise RuntimeError('Failed to execute SFC setup script ' \ '{0} at node {1}'.format(exec_shell, dut_node['host']))
def configure_sr_steer(node, mode, bsid, interface=None, ip_addr=None, prefix=None): """Create SRv6 steering policy on the given node. :param node: Given node to create steering policy on. :param mode: Mode of operation - L2 or L3. :param bsid: BindingSID - local SID IPv6 address. :param interface: Interface name (Optional, required in case of L2 mode). :param ip_addr: IPv4/IPv6 address (Optional, required in case of L3 mode). :param prefix: IP address prefix (Optional, required in case of L3 mode). :type node: dict :type mode: str :type bsid: str :type interface: str :type ip_addr: str :type prefix: int :raises ValueError: If unsupported mode used or required parameter is missing. """ if mode.lower() == 'l2': if interface is None: raise ValueError( 'Required data missing.\ninterface:{0}\n'.format( interface)) interface_name = Topology.get_interface_name(node, interface) params = 'l2 {0}'.format(interface_name) elif mode.lower() == 'l3': if ip_addr is None or prefix is None: raise ValueError('Required data missing.\nIP address:{0}\n' 'mask:{1}'.format(ip_addr, prefix)) params = 'l3 {0}/{1}'.format(ip_addr, prefix) else: raise ValueError('Unsupported mode: {0}'.format(mode)) with VatTerminal(node, json_param=False) as vat: vat.vat_terminal_exec_cmd_from_template( 'srv6/sr_steer_add_del.vat', params=params, bsid=bsid) sr_steer_errors = ("exec error: Misc", "sr steer: No SR policy specified") for err in sr_steer_errors: if err in vat.vat_stdout: raise RuntimeError('Create SRv6 steering policy for BindingSID' ' {0} failed on node {1}'.format( bsid, node['host']))
def _configure_vpp_cross_horiz(self, **kwargs): """Configure VPP in cross horizontal topology (single memif). :param kwargs: Named parameters. :param kwargs: dict """ if 'DUT1' in self.engine.container.name: if_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs['dut1_if']) if_name = Topology.get_interface_name( self.engine.container.node, kwargs['dut1_if']) if 'DUT2' in self.engine.container.name: if_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs['dut2_if']) if_name = Topology.get_interface_name( self.engine.container.node, kwargs['dut2_if']) self.engine.create_vpp_startup_config_dpdk_dev(if_pci) self.engine.create_vpp_exec_config( 'memif_create_cross_horizon.exec', mid1=kwargs['mid1'], sid1=kwargs['sid1'], if_name=if_name, socket1='{guest_dir}/memif-{c.name}-{sid1}'. format(c=self.engine.container, **kwargs))
def set_interface_state(node, interface, state, if_type="key"): """Set interface state on a node. Function can be used for DUTs as well as for TGs. :param node: Node where the interface is. :param interface: Interface key or sw_if_index or name. :param state: One of 'up' or 'down'. :param if_type: Interface type :type node: dict :type interface: str or int :type state: str :type if_type: str :returns: Nothing. :raises ValueError: If the interface type is unknown. :raises ValueError: If the state of interface is unexpected. :raises ValueError: If the node has an unknown node type. """ if if_type == "key": if isinstance(interface, basestring): sw_if_index = Topology.get_interface_sw_index(node, interface) iface_name = Topology.get_interface_name(node, interface) else: sw_if_index = interface elif if_type == "name": iface_key = Topology.get_interface_by_name(node, interface) if iface_key is not None: sw_if_index = Topology.get_interface_sw_index(node, iface_key) iface_name = interface else: raise ValueError("if_type unknown: {}".format(if_type)) if node['type'] == NodeType.DUT: if state == 'up': state = 'admin-up' elif state == 'down': state = 'admin-down' else: raise ValueError( 'Unexpected interface state: {}'.format(state)) VatExecutor.cmd_from_template(node, 'set_if_state.vat', sw_if_index=sw_if_index, state=state) elif node['type'] == NodeType.TG or node['type'] == NodeType.VM: cmd = 'ip link set {} {}'.format(iface_name, state) exec_cmd_no_error(node, cmd, sudo=True) else: raise ValueError('Node {} has unknown NodeType: "{}"'.format( node['host'], node['type']))
def _configure_vpp_cross_horiz(self, **kwargs): """Configure VPP in cross horizontal topology (single memif). :param kwargs: Named parameters. :type kwargs: dict """ if u"DUT1" in self.engine.container.name: if_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs[u"dut1_if"]) if_name = Topology.get_interface_name( self.engine.container.node, kwargs[u"dut1_if"]) if u"DUT2" in self.engine.container.name: if_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs[u"dut2_if"]) if_name = Topology.get_interface_name( self.engine.container.node, kwargs[u"dut2_if"]) self.engine.create_vpp_startup_config_dpdk_dev(if_pci) self.engine.create_vpp_exec_config( u"memif_create_cross_horizon.exec", mid1=kwargs[u"mid1"], sid1=kwargs[u"sid1"], if_name=if_name, socket1=f"{kwargs[u'guest_dir']}/memif-" f"{self.engine.container.name}-{kwargs[u'sid1']}" )
def delete_sr_steer(node, mode, bsid, interface=None, ip_addr=None, mask=None): """Delete SRv6 steering policy on the given node. :param node: Given node to delete steering policy on. :param mode: Mode of operation - L2 or L3. :param bsid: BindingSID - local SID IPv6 address. :param interface: Interface name (Optional, required in case of L2 mode). :param ip_addr: IPv4/IPv6 address (Optional, required in case of L3 mode). :param mask: IP address mask (Optional, required in case of L3 mode). :type node: dict :type mode: str :type bsid: str :type interface: str :type ip_addr: int :type mask: int :raises ValueError: If unsupported mode used or required parameter is missing. """ params = 'del' if mode == 'l2': if interface is None: raise ValueError( 'Required data missing.\ninterface:{0}\n'.format( interface)) interface_name = Topology.get_interface_name(node, interface) params += 'l2 {0}'.format(interface_name) elif mode == 'l3': if ip_addr is None or mask is None: raise ValueError('Required data missing.\nIP address:{0}\n' 'mask:{1}'.format(ip_addr, mask)) params += '{0}/{1}'.format(ip_addr, mask) else: raise ValueError('Unsupported mode: {0}'.format(mode)) with VatTerminal(node) as vat: resp = vat.vat_terminal_exec_cmd_from_template( 'srv6/sr_steer_add_del.vat', params=params, bsid=bsid) VatJsonUtil.verify_vat_retval( resp[0], err_msg='Delete SRv6 policy for bsid {0} failed on node {1}'. format(bsid, node['host']))
def get_variables(nodes, networks=IPV4_NETWORKS[:]): """Special robot framework method that returns dictionary nodes_ipv4_addr, mapping of node and interface name to IPv4 address. :param nodes: Nodes of the test topology. :param networks: List of available IPv4 networks. :type nodes: dict :type networks: list .. note:: Robot framework calls it automatically. """ topo = Topology() links = topo.get_links(nodes) if len(links) > len(networks): raise Exception('Not enough available IPv4 networks for topology.') ip4_n = IPv4NetworkGenerator(networks) nets = {} for link in links: ip4_net = ip4_n.next_network() net_hosts = ip4_net.hosts() port_idx = 0 ports = {} for node in nodes.values(): if_key = topo.get_interface_by_link_name(node, link) if_name = topo.get_interface_name(node, if_key) if if_name is not None: port = { 'addr': str(next(net_hosts)), 'node': node['host'], 'if': if_name } port_idx += 1 port_id = 'port{0}'.format(port_idx) ports.update({port_id: port}) nets.update({ link: { 'net_addr': str(ip4_net.network_address), 'prefix': ip4_net.prefixlen, 'ports': ports } }) return {'DICT__nodes_ipv4_addr': nets}
def vpp_ipsec_set_ip_route(node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface, raddr_range): """Set IP address and route on interface. :param node: VPP node to add config on. :param n_tunnels: Number of tunnels to create. :param tunnel_src: Tunnel header source IPv4 or IPv6 address. :param traffic_addr: Traffic destination IP address to route. :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. :param interface: Interface key on node 1. :param raddr_range: Mask specifying range of Policy selector Remote IPv4 addresses. Valid values are from 1 to 32. :type node: dict :type n_tunnels: int :type tunnel_src: str :type traffic_addr: str :type tunnel_dst: str :type interface: str :type raddr_range: int """ tmp_filename = '/tmp/ipsec_set_ip.script' addr_incr = 1 << (32 - raddr_range) with open(tmp_filename, 'w') as tmp_file: for i in range(0, n_tunnels): conf = ( 'exec set interface ip address {interface} {laddr}/24\n' 'exec ip route add {taddr}/32 via {raddr} {interface}\n'. format( interface=Topology.get_interface_name(node, interface), laddr=ip_address(unicode(tunnel_src)) + i * addr_incr, raddr=ip_address(unicode(tunnel_dst)) + i * addr_incr, taddr=ip_address(unicode(traffic_addr)) + i)) tmp_file.write(conf) vat = VatExecutor() vat.execute_script(tmp_filename, node, timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_filename)
def set_interface_irqs_affinity(node, interface, cpu_skip_cnt=0, cpu_cnt=1): """Set IRQs affinity for interface in linux. :param node: Topology node. :param interface: Topology interface. :param cpu_skip_cnt: Amount of CPU cores to skip. :param cpu_cnt: CPU threads count. (Optional, Default: 0) :param cpu_list: List of CPUs. (Optional, Default: 1) :type node: dict :type interface: str :type cpu_skip_cnt: int :type cpu_cnt: int """ cpu_list = CpuUtils.get_affinity_af_xdp( node, interface, cpu_skip_cnt=cpu_skip_cnt, cpu_cnt=cpu_cnt ) interface = Topology.get_interface_name(node, interface) irq_list = IrqUtil.get_interface_irqs(node, interface) for irq, cpu in zip(irq_list, cpu_list): if cpu < 32: mask = 1 << cpu mask = f"{mask:x}" else: groups = int(cpu/32) mask_fill = u"" for _ in range(groups): mask_fill = f"{mask_fill},00000000" mask = 1 << (cpu - (32 * groups)) mask = f"{mask:x}{mask_fill}" command = f"sh -c 'echo {mask} > /proc/irq/{irq}/smp_affinity'" message = f"Failed to set IRQ affinity for {irq} on {node['host']}!" exec_cmd_no_error( node, command, timeout=30, sudo=True, message=message )
def vpp_ipsec_create_tunnel_interfaces(node1, node2, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels, crypto_alg, crypto_key, integ_alg, integ_key, raddr_ip1, raddr_ip2, raddr_range): """Create multiple IPsec tunnel interfaces between two VPP nodes. :param node1: VPP node 1 to create tunnel interfaces. :param node2: VPP node 2 to create tunnel interfaces. :param if1_ip_addr: VPP node 1 interface IP4 address. :param if2_ip_addr: VPP node 2 interface IP4 address. :param if1_key: VPP node 1 interface key from topology file. :param if2_key: VPP node 2 interface key from topology file. :param n_tunnels: Number of tunnell interfaces to create. :param crypto_alg: The encryption algorithm name. :param crypto_key: The encryption key string. :param integ_alg: The integrity algorithm name. :param integ_key: The integrity key string. :param raddr_ip1: Policy selector remote IPv4 start address for the first tunnel in direction node1->node2. :param raddr_ip2: Policy selector remote IPv4 start address for the first tunnel in direction node2->node1. :param raddr_range: Mask specifying range of Policy selector Remote IPv4 addresses. Valid values are from 1 to 32. :type node1: dict :type node2: dict :type if1_ip_addr: str :type if2_ip_addr: str :type if1_key: str :type if2_key: str :type n_tunnels: int :type crypto_alg: CryptoAlg :type crypto_key: str :type integ_alg: IntegAlg :type integ_key: str :type raddr_ip1: string :type raddr_ip2: string :type raddr_range: int """ spi_1 = 10000 spi_2 = 20000 raddr_ip1_i = int(ip_address(unicode(raddr_ip1))) raddr_ip2_i = int(ip_address(unicode(raddr_ip2))) addr_incr = 1 << (32 - raddr_range) tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config' tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config' ckey = crypto_key.encode('hex') ikey = integ_key.encode('hex') vat = VatExecutor() with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2: for i in range(0, n_tunnels): integ = '' if crypto_alg.alg_name != 'aes-gcm-128': integ = 'integ_alg {integ_alg} '\ 'local_integ_key {local_integ_key} '\ 'remote_integ_key {remote_integ_key} '\ .format(integ_alg=integ_alg.alg_name, local_integ_key=ikey, remote_integ_key=ikey) dut1_tunnel = 'ipsec_tunnel_if_add_del '\ 'local_spi {local_spi} '\ 'remote_spi {remote_spi} '\ 'crypto_alg {crypto_alg} '\ 'local_crypto_key {local_crypto_key} '\ 'remote_crypto_key {remote_crypto_key} '\ '{integ} '\ 'local_ip {local_ip} '\ 'remote_ip {remote_ip}\n'\ .format(local_spi=spi_1+i, remote_spi=spi_2+i, crypto_alg=crypto_alg.alg_name, local_crypto_key=ckey, remote_crypto_key=ckey, integ=integ, local_ip=if1_ip_addr, remote_ip=if2_ip_addr) dut2_tunnel = 'ipsec_tunnel_if_add_del '\ 'local_spi {local_spi} '\ 'remote_spi {remote_spi} '\ 'crypto_alg {crypto_alg} '\ 'local_crypto_key {local_crypto_key} '\ 'remote_crypto_key {remote_crypto_key} '\ '{integ} '\ 'local_ip {local_ip} '\ 'remote_ip {remote_ip}\n'\ .format(local_spi=spi_2+i, remote_spi=spi_1+i, crypto_alg=crypto_alg.alg_name, local_crypto_key=ckey, remote_crypto_key=ckey, integ=integ, local_ip=if2_ip_addr, remote_ip=if1_ip_addr) tmp_f1.write(dut1_tunnel) tmp_f2.write(dut2_tunnel) vat.execute_script(tmp_fn1, node1, timeout=300, json_out=False, copy_on_execute=True) vat.execute_script(tmp_fn2, node2, timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_fn1) os.remove(tmp_fn2) with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2: for i in range(0, n_tunnels): raddr_ip1 = ip_address(raddr_ip1_i + addr_incr * i) raddr_ip2 = ip_address(raddr_ip2_i + addr_incr * i) dut1_if = Topology.get_interface_name(node1, if1_key) dut1 = 'ip_add_del_route {raddr}/{mask} via {addr} ipsec{i}\n'\ 'exec set interface unnumbered ipsec{i} use {uifc}\n'\ 'sw_interface_set_flags ipsec{i} admin-up\n'\ .format(raddr=raddr_ip2, mask=raddr_range, addr=if2_ip_addr, i=i, uifc=dut1_if) dut2_if = Topology.get_interface_name(node2, if2_key) dut2 = 'ip_add_del_route {raddr}/{mask} via {addr} ipsec{i}\n'\ 'exec set interface unnumbered ipsec{i} use {uifc}\n'\ 'sw_interface_set_flags ipsec{i} admin-up\n'\ .format(raddr=raddr_ip1, mask=raddr_range, addr=if1_ip_addr, i=i, uifc=dut2_if) tmp_f1.write(dut1) tmp_f2.write(dut2) vat.execute_script(tmp_fn1, node1, timeout=300, json_out=False, copy_on_execute=True) vat.execute_script(tmp_fn2, node2, timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_fn1) os.remove(tmp_fn2)
def configure_vpp_in_all_containers(self, chain_topology, dut1_if=None, dut2_if=None): """Configure VPP in all containers. :param chain_topology: Topology used for chaining containers can be chain or cross_horiz. Chain topology is using 1 memif pair per container. Cross_horiz topology is using 1 memif and 1 physical interface in container (only single container can be configured). :param dut1_if: Interface on DUT1 directly connected to DUT2. :param dut2_if: Interface on DUT2 directly connected to DUT1. :type container_topology: str :type dut1_if: str :type dut2_if: str """ # Count number of DUTs based on node's host information dut_cnt = len( Counter([ self.containers[container].node['host'] for container in self.containers ])) mod = len(self.containers) / dut_cnt container_vat_template = 'memif_create_{topology}.vat'.format( topology=chain_topology) if chain_topology == 'chain': for i, container in enumerate(self.containers): mid1 = i % mod + 1 mid2 = i % mod + 1 sid1 = i % mod * 2 + 1 sid2 = i % mod * 2 + 2 self.engine.container = self.containers[container] self.engine.create_vpp_startup_config() self.engine.create_vpp_exec_config(container_vat_template, \ mid1=mid1, mid2=mid2, sid1=sid1, sid2=sid2, \ socket1='memif-{c.name}-{sid}'. \ format(c=self.engine.container, sid=sid1), \ socket2='memif-{c.name}-{sid}'. \ format(c=self.engine.container, sid=sid2)) elif chain_topology == 'cross_horiz': if mod > 1: raise RuntimeError('Container chain topology {topology} ' 'supports only single container.'.format( topology=chain_topology)) for i, container in enumerate(self.containers): mid1 = i % mod + 1 sid1 = i % mod * 2 + 1 self.engine.container = self.containers[container] if 'DUT1' in self.engine.container.name: if_pci = Topology.get_interface_pci_addr( \ self.engine.container.node, dut1_if) if_name = Topology.get_interface_name( \ self.engine.container.node, dut1_if) if 'DUT2' in self.engine.container.name: if_pci = Topology.get_interface_pci_addr( \ self.engine.container.node, dut2_if) if_name = Topology.get_interface_name( \ self.engine.container.node, dut2_if) self.engine.create_vpp_startup_config_dpdk_dev(if_pci) self.engine.create_vpp_exec_config(container_vat_template, \ mid1=mid1, sid1=sid1, if_name=if_name, \ socket1='memif-{c.name}-{sid}'. \ format(c=self.engine.container, sid=sid1)) else: raise RuntimeError( 'Container topology {topology} not implemented'.format( topology=chain_topology))
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: int :type fib_table: str :type out_if: str :type in_if: str :type src_addr: str :type sid_list: list :raises ValueError: If unsupported SRv6 LocalSID function used or required parameter is missing. """ if behavior == SRV6BEHAVIOUR_END: params = '' elif behavior in [ SRV6BEHAVIOUR_END_X, SRV6BEHAVIOUR_END_DX4, SRV6BEHAVIOUR_END_DX6 ]: if interface is None or next_hop is None: raise ValueError( 'Required parameter(s) missing.\ninterface:{0}' '\nnext_hop:{1}'.format(interface, next_hop)) interface_name = Topology.get_interface_name(node, interface) params = '{0} {1}'.format(interface_name, next_hop) elif behavior == SRV6BEHAVIOUR_END_DX2: if interface is None: raise ValueError( 'Required parameter missing.\ninterface:{0}'.format( interface)) params = '{0}'.format(interface) elif behavior in [SRV6BEHAVIOUR_END_DT4, SRV6BEHAVIOUR_END_DT6]: if fib_table is None: raise ValueError( 'Required parameter missing.\nfib_table: {0}'.format( fib_table)) params = '{0}'.format(fib_table) elif behavior == SRV6BEHAVIOUR_END_AS: 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('Required parameter(s) missing.\nnext_hop:{0}' '\nout_if:{1}\nin_if:{2}\nsrc_addr:{3}\n' 'sid_list:{4}'.format(next_hop, out_if, in_if, src_addr, sid_list)) sid_conf = 'next ' + ' next '.join(sid_list) params = 'nh {0} oif {1} iif {2} src {3} {4}'.\ format(next_hop, out_if, in_if, src_addr, sid_conf) elif behavior in [SRV6BEHAVIOUR_END_AD, SRV6BEHAVIOUR_END_AM]: if next_hop is None or out_if is None or in_if is None: raise ValueError('Required parameter(s) missing.\nnext_hop:{0}' '\nout_if:{1}\nin_if:{2}'.format( next_hop, out_if, in_if)) params = 'nh {0} oif {1} iif {2}'.format(next_hop, out_if, in_if) else: raise ValueError( 'Unsupported SRv6 LocalSID function: {0}'.format(behavior)) with VatTerminal(node, json_param=False) as vat: vat.vat_terminal_exec_cmd_from_template('srv6/sr_localsid_add.vat', local_sid=local_sid, behavior=behavior, params=params) if "exec error: Misc" in vat.vat_stdout: raise RuntimeError( 'Create SRv6 LocalSID {0} failed on node {1}'.format( local_sid, node['host']))
def vpp_put_vxlan_and_vlan_interfaces_up(node, vxlan_count, node_vlan_if): """ Update topology with VXLAN interfaces and VLAN sub-interfaces data and put interfaces up. :param node: VPP node. :param vxlan_count: Number of tunnel interfaces. :param node_vlan_if: VPP node interface key where VLAN sub-interfaces have been created. :type node: dict :type vxlan_count: int :type node_vlan_if: str """ if_data = InterfaceUtil.vpp_get_interface_data(node) vlan_if_name = Topology.get_interface_name(node, node_vlan_if) if vxlan_count > 10: tmp_fn = '/tmp/put_subinterfaces_up.config' commands = list() for i in xrange(0, vxlan_count): vxlan_subif_key = Topology.add_new_port(node, 'vxlan_tunnel') vxlan_subif_name = 'vxlan_tunnel{nr}'.format(nr=i) vxlan_found = False vxlan_subif_idx = None vlan_subif_key = Topology.add_new_port(node, 'vlan_subif') vlan_subif_name = '{if_name}.{vlan}'.format( if_name=vlan_if_name, vlan=i + 1) vlan_found = False vlan_idx = None for data in if_data: if_name = data['interface_name'] if not vxlan_found and if_name == vxlan_subif_name: vxlan_subif_idx = data['sw_if_index'] vxlan_found = True elif not vlan_found and if_name == vlan_subif_name: vlan_idx = data['sw_if_index'] vlan_found = True if vxlan_found and vlan_found: break Topology.update_interface_sw_if_index(node, vxlan_subif_key, vxlan_subif_idx) Topology.update_interface_name(node, vxlan_subif_key, vxlan_subif_name) commands.append( 'sw_interface_set_flags sw_if_index {sw_idx} admin-up ' 'link-up\n'.format(sw_idx=vxlan_subif_idx)) Topology.update_interface_sw_if_index(node, vlan_subif_key, vlan_idx) Topology.update_interface_name(node, vlan_subif_key, vlan_subif_name) commands.append( 'sw_interface_set_flags sw_if_index {sw_idx} admin-up ' 'link-up\n'.format(sw_idx=vlan_idx)) VatExecutor().write_and_execute_script(node, tmp_fn, commands) return cmd = 'sw_interface_set_flags' args1 = dict(sw_if_index=None, admin_up_down=1) args2 = dict(sw_if_index=None, admin_up_down=1) err_msg = 'Failed to put VXLAN and VLAN interfaces up on host {host}'. \ format(host=node['host']) with PapiExecutor(node) as papi_exec: for i in xrange(0, vxlan_count): vxlan_subif_key = Topology.add_new_port(node, 'vxlan_tunnel') vxlan_subif_name = 'vxlan_tunnel{nr}'.format(nr=i) vxlan_found = False vxlan_subif_idx = None vlan_subif_key = Topology.add_new_port(node, 'vlan_subif') vlan_subif_name = '{if_name}.{vlan}'.format( if_name=vlan_if_name, vlan=i + 1) vlan_found = False vlan_idx = None for data in if_data: if not vxlan_found \ and data['interface_name'] == vxlan_subif_name: vxlan_subif_idx = data['sw_if_index'] vxlan_found = True elif not vlan_found \ and data['interface_name'] == vlan_subif_name: vlan_idx = data['sw_if_index'] vlan_found = True if vxlan_found and vlan_found: break Topology.update_interface_sw_if_index(node, vxlan_subif_key, vxlan_subif_idx) Topology.update_interface_name(node, vxlan_subif_key, vxlan_subif_name) args1['sw_if_index'] = vxlan_subif_idx Topology.update_interface_sw_if_index(node, vlan_subif_key, vlan_idx) Topology.update_interface_name(node, vlan_subif_key, vlan_subif_name) args2['sw_if_index'] = vlan_idx history = False if 1 < i < vxlan_count else True papi_exec.add(cmd, history=history, **args1). \ add(cmd, history=history, **args2) if i > 0 and i % (Constants.PAPI_MAX_API_BULK / 2) == 0: papi_exec.get_replies(err_msg) papi_exec.add(cmd, **args1).add(cmd, **args2) papi_exec.get_replies()
def vpp_ipsec_create_tunnel_interfaces(nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range): """Create multiple IPsec tunnel interfaces between two VPP nodes. :param nodes: VPP nodes to create tunnel interfaces. :param if1_ip_addr: VPP node 1 interface IP4 address. :param if2_ip_addr: VPP node 2 interface IP4 address. :param if1_key: VPP node 1 interface key from topology file. :param if2_key: VPP node 2 interface key from topology file. :param n_tunnels: Number of tunnell interfaces to create. :param crypto_alg: The encryption algorithm name. :param integ_alg: The integrity algorithm name. :param raddr_ip1: Policy selector remote IPv4 start address for the first tunnel in direction node1->node2. :param raddr_ip2: Policy selector remote IPv4 start address for the first tunnel in direction node2->node1. :param raddr_range: Mask specifying range of Policy selector Remote IPv4 addresses. Valid values are from 1 to 32. :type nodes: dict :type if1_ip_addr: str :type if2_ip_addr: str :type if1_key: str :type if2_key: str :type n_tunnels: int :type crypto_alg: CryptoAlg :type integ_alg: IntegAlg :type raddr_ip1: string :type raddr_ip2: string :type raddr_range: int """ spi_1 = 100000 spi_2 = 200000 addr_incr = 1 << (32 - raddr_range) tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config' tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config' vat = VatExecutor() with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2: for i in range(0, n_tunnels): ckey = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)) ikey = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) integ = '' if not crypto_alg.alg_name.startswith('aes-gcm-'): integ = ('integ_alg {integ_alg} ' 'local_integ_key {local_integ_key} ' 'remote_integ_key {remote_integ_key} '.format( integ_alg=integ_alg.alg_name, local_integ_key=ikey, remote_integ_key=ikey)) tmp_f1.write( 'exec set interface ip address {uifc} {laddr}/24\n' 'ipsec_tunnel_if_add_del ' 'local_spi {local_spi} ' 'remote_spi {remote_spi} ' 'crypto_alg {crypto_alg} ' 'local_crypto_key {local_crypto_key} ' 'remote_crypto_key {remote_crypto_key} ' '{integ} ' 'local_ip {laddr} ' 'remote_ip {raddr}\n'.format( local_spi=spi_1 + i, remote_spi=spi_2 + i, crypto_alg=crypto_alg.alg_name, local_crypto_key=ckey, remote_crypto_key=ckey, integ=integ, laddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr, raddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr, uifc=Topology.get_interface_name( nodes['DUT1'], if1_key))) tmp_f2.write( 'exec set interface ip address {uifc} {laddr}/24\n' 'ipsec_tunnel_if_add_del ' 'local_spi {local_spi} ' 'remote_spi {remote_spi} ' 'crypto_alg {crypto_alg} ' 'local_crypto_key {local_crypto_key} ' 'remote_crypto_key {remote_crypto_key} ' '{integ} ' 'local_ip {laddr} ' 'remote_ip {raddr}\n'.format( local_spi=spi_2 + i, remote_spi=spi_1 + i, crypto_alg=crypto_alg.alg_name, local_crypto_key=ckey, remote_crypto_key=ckey, integ=integ, laddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr, raddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr, uifc=Topology.get_interface_name( nodes['DUT2'], if2_key))) vat.execute_script(tmp_fn1, nodes['DUT1'], timeout=300, json_out=False, copy_on_execute=True) vat.execute_script(tmp_fn2, nodes['DUT2'], timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_fn1) os.remove(tmp_fn2) with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2: for i in range(0, n_tunnels): tmp_f1.write( 'exec set interface unnumbered ipsec{i} use {uifc}\n' 'exec set interface state ipsec{i} up\n' 'exec ip route add {taddr}/32 via {raddr} ipsec{i}\n'. format(taddr=ip_address(unicode(raddr_ip2)) + i, raddr=ip_address(unicode(if2_ip_addr)) + i * addr_incr, i=i, uifc=Topology.get_interface_name( nodes['DUT1'], if1_key))) tmp_f2.write( 'exec set interface unnumbered ipsec{i} use {uifc}\n' 'exec set interface state ipsec{i} up\n' 'exec ip route add {taddr}/32 via {raddr} ipsec{i}\n'. format(taddr=ip_address(unicode(raddr_ip1)) + i, raddr=ip_address(unicode(if1_ip_addr)) + i * addr_incr, i=i, uifc=Topology.get_interface_name( nodes['DUT2'], if2_key))) vat.execute_script(tmp_fn1, nodes['DUT1'], timeout=300, json_out=False, copy_on_execute=True) vat.execute_script(tmp_fn2, nodes['DUT2'], timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_fn1) os.remove(tmp_fn2)
def vpp_ipsec_create_tunnel_interfaces(node1, node2, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels, crypto_alg, crypto_key, integ_alg, integ_key, raddr_ip1, raddr_ip2, raddr_range): """Create multiple IPsec tunnel interfaces between two VPP nodes. :param node1: VPP node 1 to create tunnel interfaces. :param node2: VPP node 2 to create tunnel interfaces. :param if1_ip_addr: VPP node 1 interface IP4 address. :param if2_ip_addr: VPP node 2 interface IP4 address. :param if1_key: VPP node 1 interface key from topology file. :param if2_key: VPP node 2 interface key from topology file. :param n_tunnels: Number of tunnell interfaces to create. :param crypto_alg: The encryption algorithm name. :param crypto_key: The encryption key string. :param integ_alg: The integrity algorithm name. :param integ_key: The integrity key string. :param raddr_ip1: Policy selector remote IPv4 start address for the first tunnel in direction node1->node2. :param raddr_ip2: Policy selector remote IPv4 start address for the first tunnel in direction node2->node1. :param raddr_range: Mask specifying range of Policy selector Remote IPv4 addresses. Valid values are from 1 to 32. :type node1: dict :type node2: dict :type if1_ip_addr: str :type if2_ip_addr: str :type if1_key: str :type if2_key: str :type n_tunnels: int :type crypto_alg: CryptoAlg :type crypto_key: str :type integ_alg: IntegAlg :type integ_key: str :type raddr_ip1: string :type raddr_ip2: string :type raddr_range: int """ spi_1 = 10000 spi_2 = 20000 raddr_ip1_i = int(ip_address(unicode(raddr_ip1))) raddr_ip2_i = int(ip_address(unicode(raddr_ip2))) addr_incr = 1 << (32 - raddr_range) tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config' tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config' ckey = crypto_key.encode('hex') ikey = integ_key.encode('hex') with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2: for i in range(0, n_tunnels): if_s = 'ipsec{}'.format(i) dut1_tunnel_s = 'create ipsec tunnel local-ip {0} local-spi ' \ '{1} remote-ip {2} remote-spi {3}\n'.format( if1_ip_addr, spi_1+i, if2_ip_addr, spi_2+i) tmp_f1.write(dut1_tunnel_s) dut2_tunnel_s = 'create ipsec tunnel local-ip {0} local-spi ' \ '{1} remote-ip {2} remote-spi {3}\n'.format( if2_ip_addr, spi_2+i, if1_ip_addr, spi_1+i) tmp_f2.write(dut2_tunnel_s) loc_c_key = 'set interface ipsec key {0} local crypto {1} ' \ '{2}\n'.format(if_s, crypto_alg.alg_name, ckey) tmp_f1.write(loc_c_key) tmp_f2.write(loc_c_key) rem_c_key = 'set interface ipsec key {0} remote crypto {1} ' \ '{2}\n'.format(if_s, crypto_alg.alg_name, ckey) tmp_f1.write(rem_c_key) tmp_f2.write(rem_c_key) if crypto_alg.alg_name != 'aes-gcm-128': loc_i_key = 'set interface ipsec key {0} local integ {1} ' \ '{2}\n'.format(if_s, integ_alg.alg_name, ikey) tmp_f1.write(loc_i_key) tmp_f2.write(loc_i_key) rem_i_key = 'set interface ipsec key {0} remote integ {1}' \ ' {2}\n'.format(if_s, integ_alg.alg_name, ikey) tmp_f1.write(rem_i_key) tmp_f2.write(rem_i_key) raddr_ip1_s = ip_address(raddr_ip1_i + addr_incr*i) raddr_ip2_s = ip_address(raddr_ip2_i + addr_incr*i) dut1_rte_s = 'ip route add {0}/{1} via {2} {3}\n'.format( raddr_ip2_s, raddr_range, if2_ip_addr, if_s) tmp_f1.write(dut1_rte_s) dut2_rte_s = 'ip route add {0}/{1} via {2} {3}\n'.format( raddr_ip1_s, raddr_range, if1_ip_addr, if_s) tmp_f2.write(dut2_rte_s) dut1_if = Topology.get_interface_name(node1, if1_key) dut1_unnum_s = 'set interface unnumbered {0} use {1}\n'.format( if_s, dut1_if) tmp_f1.write(dut1_unnum_s) dut2_if = Topology.get_interface_name(node2, if2_key) dut2_unnum_s = 'set interface unnumbered {0} use {1}\n'.format( if_s, dut2_if) tmp_f2.write(dut2_unnum_s) up_s = 'set int state {0} up\n'.format(if_s) tmp_f1.write(up_s) tmp_f2.write(up_s) vat = VatExecutor() vat.scp_and_execute_cli_script(tmp_fn1, node1, 300) vat.scp_and_execute_cli_script(tmp_fn2, node2, 300) os.remove(tmp_fn1) os.remove(tmp_fn2)
def _configure_vpp_chain_vswitch(self, **kwargs): """Configure VPP as vswitch in container. :param kwargs: Named parameters. :type kwargs: dict """ dut = self.engine.container.name.split(u"_")[0] if dut == u"DUT1": if1_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs[u"dut1_if2"]) if2_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs[u"dut1_if1"]) if_red_name = Topology.get_interface_name( self.engine.container.node, kwargs[u"dut1_if2"]) if_black_name = Topology.get_interface_name( self.engine.container.node, kwargs[u"dut1_if1"]) tg_pf_ip4 = kwargs[u"tg_pf2_ip4"] tg_pf_mac = kwargs[u"tg_pf2_mac"] else: tg_pf_ip4 = kwargs[u"tg_pf1_ip4"] tg_pf_mac = kwargs[u"tg_pf1_mac"] if1_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs[u"dut2_if1"]) if2_pci = Topology.get_interface_pci_addr( self.engine.container.node, kwargs[u"dut2_if2"]) if_red_name = Topology.get_interface_name( self.engine.container.node, kwargs[u"dut2_if1"]) if_black_name = Topology.get_interface_name( self.engine.container.node, kwargs[u"dut2_if2"]) n_instances = int(kwargs[u"n_instances"]) rxq = 1 if u"rxq" in kwargs: rxq = int(kwargs[u"rxq"]) nodes = kwargs[u"nodes"] cpuset_cpus = CpuUtils.get_affinity_nf( nodes, dut, nf_chains=1, nf_nodes=1, nf_chain=1, nf_node=1, vs_dtc=0, nf_dtc=8, nf_mtcr=1, nf_dtcr=1 ) self.engine.create_vpp_startup_config_vswitch( cpuset_cpus, rxq, if1_pci, if2_pci ) instances = [] for i in range(1, n_instances + 1): instances.append( f"create interface memif id {i} socket-id 1 master\n" f"set interface state memif1/{i} up\n" f"set interface l2 bridge memif1/{i} 1\n" f"create interface memif id {i} socket-id 2 master\n" f"set interface state memif2/{i} up\n" f"set interface l2 bridge memif2/{i} 2\n" f"set ip neighbor memif2/{i} {tg_pf_ip4} {tg_pf_mac} " f"static\n\n" ) self.engine.create_vpp_exec_config( u"memif_create_chain_vswitch_ipsec.exec", socket1=f"{kwargs[u'guest_dir']}/{dut}_memif-vswitch-1", socket2=f"{kwargs[u'guest_dir']}/{dut}_memif-vswitch-2", if_red_name=if_red_name, if_black_name=if_black_name, instances=u"\n\n".join(instances))
def configure_sr_localsid(node, local_sid, behavior, interface=None, next_hop=None, fib_table=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). :type node: dict :type local_sid: str :type behavior: str :type interface: str :type next_hop: int :type fib_table: str :raises ValueError: If unsupported SRv6 LocalSID function used or required parameter is missing. """ if behavior == SRv6Behaviour.END: params = '' elif behavior in [ SRv6Behaviour.END_X, SRv6Behaviour.END_DX4, SRv6Behaviour.END_DX6 ]: if interface is None or next_hop is None: raise ValueError('Required data missing.\ninterface:{0}\n' 'next_hop:{1}'.format(interface, next_hop)) interface_name = Topology.get_interface_name(node, interface) params = '{0} {1}'.format(interface_name, next_hop) elif behavior == SRv6Behaviour.END_DX2: if interface is None: raise ValueError( 'Required data missing.\ninterface:{0}\n'.format( interface)) params = '{0}'.format(interface) elif behavior in [SRv6Behaviour.END_DT4, SRv6Behaviour.END_DT6]: if fib_table is None: raise ValueError( 'Required data missing.\nfib_table:{0}\n'.format( fib_table)) params = '{0}'.format(fib_table) else: raise ValueError( 'Unsupported SRv6 LocalSID function: {0}'.format(behavior)) with VatTerminal(node) as vat: resp = vat.vat_terminal_exec_cmd_from_template( 'srv6/sr_localsid_add.vat', local_sid=local_sid, behavior=behavior, params=params) VatJsonUtil.verify_vat_retval( resp[0], err_msg='Create SRv6 LocalSID {0} failed on node {1}'.format( local_sid, node['host']))