diff options
-rw-r--r-- | data/templates/vpp/override.conf.j2 | 14 | ||||
-rw-r--r-- | data/templates/vpp/startup.conf.j2 | 116 | ||||
-rw-r--r-- | debian/control | 4 | ||||
-rw-r--r-- | interface-definitions/vpp.xml.in | 342 | ||||
-rw-r--r-- | python/vyos/ethtool.py | 3 | ||||
-rw-r--r-- | python/vyos/vpp.py | 28 | ||||
-rwxr-xr-x | src/conf_mode/vpp.py | 145 |
7 files changed, 651 insertions, 1 deletions
diff --git a/data/templates/vpp/override.conf.j2 b/data/templates/vpp/override.conf.j2 new file mode 100644 index 000000000..a2c2b04ed --- /dev/null +++ b/data/templates/vpp/override.conf.j2 @@ -0,0 +1,14 @@ +[Unit] +After= +After=vyos-router.service +ConditionPathExists= +ConditionPathExists=/run/vpp/vpp.conf + +[Service] +EnvironmentFile= +ExecStart= +ExecStart=/usr/bin/vpp -c /run/vpp/vpp.conf +WorkingDirectory= +WorkingDirectory=/run/vpp +Restart=always +RestartSec=10 diff --git a/data/templates/vpp/startup.conf.j2 b/data/templates/vpp/startup.conf.j2 new file mode 100644 index 000000000..f33539fba --- /dev/null +++ b/data/templates/vpp/startup.conf.j2 @@ -0,0 +1,116 @@ +# Generated by /usr/libexec/vyos/conf_mode/vpp.py + +unix { + nodaemon + log /var/log/vpp.log + full-coredump + cli-listen /run/vpp/cli.sock + gid vpp + # exec /etc/vpp/bootstrap.vpp +{% if unix is vyos_defined %} +{% if unix.poll_sleep_usec is vyos_defined %} + poll-sleep-usec {{ unix.poll_sleep_usec }} +{% endif %} +{% endif %} +} + +{% if cpu is vyos_defined %} +cpu { +{% if cpu.main_core is vyos_defined %} + main-core {{ cpu.main_core }} +{% endif %} +{% if cpu.corelist_workers is vyos_defined %} + corelist-workers {{ cpu.corelist_workers | join(',') }} +{% endif %} +{% if cpu.skip_cores is vyos_defined %} + skip-cores {{ cpu.skip_cores }} +{% endif %} +{% if cpu.workers is vyos_defined %} + workers {{ cpu.workers }} +{% endif %} +} +{% endif %} + +{# ip heap-size does not work now (23.06-rc2~1-g3a4e62ad4) #} +{# vlib_call_all_config_functions: unknown input `ip heap-size 32M ' #} +{% if ip is vyos_defined %} +#ip { +#{% if ip.heap_size is vyos_defined %} +# heap-size {{ ip.heap_size }}M +#{% endif %} +#} +{% endif %} + +{% if ip6 is vyos_defined %} +ip6 { +{% if ip6.hash_buckets is vyos_defined %} + hash-buckets {{ ip6.hash_buckets }} +{% endif %} +{% if ip6.heap_size is vyos_defined %} + heap-size {{ ip6.heap_size }}M +{% endif %} +} +{% endif %} + +{% if l2learn is vyos_defined %} +l2learn { +{% if l2learn.limit is vyos_defined %} + limit {{ l2learn.limit }} +{% endif %} +} +{% endif %} + +{% if logging is vyos_defined %} +logging { +{% if logging.default_log_level is vyos_defined %} + default-log-level {{ logging.default_log_level }} +{% endif %} +} +{% endif %} + +{% if physmem is vyos_defined %} +physmem { +{% if physmem.max_size is vyos_defined %} + max-size {{ physmem.max_size.upper() }} +{% endif %} +} +{% endif %} + +plugins { + path /usr/lib/x86_64-linux-gnu/vpp_plugins/ + plugin default { disable } + plugin dpdk_plugin.so { enable } + plugin linux_cp_plugin.so { enable } + plugin linux_nl_plugin.so { enable } +} + +linux-cp { + lcp-sync + lcp-auto-subint +} + +dpdk { + # Whitelist the fake PCI address 0000:00:00.0 + # This prevents all devices from being added to VPP-DPDK by default + dev 0000:00:00.0 +{% for iface, iface_config in interface.items() %} +{% if iface_config.pci is vyos_defined %} + dev {{ iface_config.pci }} { + name {{ iface }} +{% if iface_config.num_rx_desc is vyos_defined %} + num-rx-desc {{ iface_config.num_rx_desc }} +{% endif %} +{% if iface_config.num_tx_desc is vyos_defined %} + num-tx-desc {{ iface_config.num_tx_desc }} +{% endif %} +{% if iface_config.num_rx_queues is vyos_defined %} + num-rx-queues {{ iface_config.num_rx_queues }} +{% endif %} +{% if iface_config.num_tx_queues is vyos_defined %} + num-tx-queues {{ iface_config.num_tx_queues }} +{% endif %} + } +{% endif %} +{% endfor %} + uio-bind-force +} diff --git a/debian/control b/debian/control index 8cbad99d9..e9e560442 100644 --- a/debian/control +++ b/debian/control @@ -87,6 +87,7 @@ Depends: libqmi-utils, libstrongswan-extra-plugins (>=5.9), libstrongswan-standard-plugins (>=5.9), + libvppinfra, libvyosconfig0, lldpd, lm-sensors, @@ -173,6 +174,9 @@ Depends: uidmap, usb-modeswitch, usbutils, + vpp, + vpp-plugin-core, + vpp-plugin-dpdk, vyatta-bash, vyatta-cfg, vyos-http-api-tools, diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in new file mode 100644 index 000000000..51ab776c3 --- /dev/null +++ b/interface-definitions/vpp.xml.in @@ -0,0 +1,342 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="vpp" owner="${vyos_conf_scripts_dir}/vpp.py"> + <properties> + <help>Accelerated data-plane</help> + <priority>1280</priority> + </properties> + <children> + <node name="cpu"> + <properties> + <help>CPU settings</help> + </properties> + <children> + <leafNode name="corelist-workers"> + <properties> + <help>List of cores worker threads</help> + <valueHelp> + <format><id></format> + <description>CPU core id</description> + </valueHelp> + <valueHelp> + <format><idN>-<idM></format> + <description>CPU core id range (use '-' as delimiter)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--allow-range --range 0-512"/> + </constraint> + <constraintErrorMessage>not a valid CPU core value or range</constraintErrorMessage> + <multi/> + </properties> + </leafNode> + <leafNode name="main-core"> + <properties> + <help>Main core</help> + <valueHelp> + <format>u32:0-512</format> + <description>Assign main thread to specific core</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-512"/> + </constraint> + </properties> + </leafNode> + <leafNode name="skip-cores"> + <properties> + <help>Skip cores</help> + <valueHelp> + <format>u32:0-512</format> + <description>Skip cores</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-512"/> + </constraint> + </properties> + </leafNode> + <leafNode name="workers"> + <properties> + <help>Create worker threads</help> + <valueHelp> + <format>u32:0-4294967295</format> + <description>Worker threads</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-512"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <tagNode name="interface"> + <properties> + <help>Interface</help> + <valueHelp> + <format>ethN</format> + <description>Interface name</description> + </valueHelp> + <constraint> + <regex>((eth|lan)[0-9]+|(eno|ens|enp|enx).+)</regex> + </constraint> + <constraintErrorMessage>Invalid interface name</constraintErrorMessage> + </properties> + <children> + <leafNode name="num-rx-desc"> + <properties> + <help>Number of receive ring descriptors</help> + <valueHelp> + <format>u32:256-8192</format> + <description>Number of receive ring descriptors</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 256-8192"/> + </constraint> + </properties> + </leafNode> + <leafNode name="num-tx-desc"> + <properties> + <help>Number of tranceive ring descriptors</help> + <valueHelp> + <format>u32:256-8192</format> + <description>Number of tranceive ring descriptors</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 256-8192"/> + </constraint> + </properties> + </leafNode> + <leafNode name="num-rx-queues"> + <properties> + <help>Number of receive ring descriptors</help> + <valueHelp> + <format>u32:256-8192</format> + <description>Number of receive queues</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 256-8192"/> + </constraint> + </properties> + </leafNode> + <leafNode name="num-tx-queues"> + <properties> + <help>Number of tranceive ring descriptors</help> + <valueHelp> + <format>u32:256-8192</format> + <description>Number of tranceive queues</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 256-8192"/> + </constraint> + </properties> + </leafNode> + <leafNode name='pci'> + <properties> + <help>PCI address allocation</help> + <valueHelp> + <format>auto</format> + <description>Auto detect PCI address</description> + </valueHelp> + <valueHelp> + <format><xxxx:xx:xx.x></format> + <description>Set Peripheral Component Interconnect (PCI) address</description> + </valueHelp> + <constraint> + <regex>(auto|[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])</regex> + </constraint> + </properties> + <defaultValue>auto</defaultValue> + </leafNode> + <leafNode name="rx-mode"> + <properties> + <help>Receive packet processing mode</help> + <completionHelp> + <list>polling interrupt adaptive</list> + </completionHelp> + <valueHelp> + <format>polling</format> + <description>Constantly check for new data</description> + </valueHelp> + <valueHelp> + <format>interrupt</format> + <description>Interrupt mode</description> + </valueHelp> + <valueHelp> + <format>adaptive</format> + <description>Adaptive mode</description> + </valueHelp> + <constraint> + <regex>(polling|interrupt|adaptive)</regex> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <node name="ip"> + <properties> + <help>IP settings</help> + </properties> + <children> + <leafNode name="heap-size"> + <properties> + <help>IPv4 heap size</help> + <valueHelp> + <format>u32:0-4294967295</format> + <description>Amount of memory (in Mbytes) dedicated to the destination IP lookup table</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + <defaultValue>32</defaultValue> + </leafNode> + </children> + </node> + <node name="ip6"> + <properties> + <help>IPv6 settings</help> + </properties> + <children> + <leafNode name="heap-size"> + <properties> + <help>IPv6 heap size</help> + <valueHelp> + <format>u32:0-4294967295</format> + <description>Amount of memory (in Mbytes) dedicated to the destination IP lookup table</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + <defaultValue>32</defaultValue> + </leafNode> + <leafNode name="hash-buckets"> + <properties> + <help>IPv6 forwarding table hash buckets</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>IPv6 forwarding table hash buckets</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + <defaultValue>65536</defaultValue> + </leafNode> + </children> + </node> + <node name="l2learn"> + <properties> + <help>Level 2 MAC address learning settings</help> + </properties> + <children> + <leafNode name="limit"> + <properties> + <help>Number of MAC addresses in the L2 FIB</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Number of concurent entries</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + <defaultValue>4194304</defaultValue> + </leafNode> + </children> + </node> + <node name="logging"> + <properties> + <help>Loggint settings</help> + </properties> + <children> + <leafNode name="default-log-level"> + <properties> + <help>default-log-level</help> + <completionHelp> + <list>alert crit debug disabled emerg err info notice warn</list> + </completionHelp> + <valueHelp> + <format>alert</format> + <description>Alert</description> + </valueHelp> + <valueHelp> + <format>crit</format> + <description>Critical</description> + </valueHelp> + <valueHelp> + <format>debug</format> + <description>Debug</description> + </valueHelp> + <valueHelp> + <format>disabled</format> + <description>Disabled</description> + </valueHelp> + <valueHelp> + <format>emerg</format> + <description>Emergency</description> + </valueHelp> + <valueHelp> + <format>err</format> + <description>Error</description> + </valueHelp> + <valueHelp> + <format>info</format> + <description>Informational</description> + </valueHelp> + <valueHelp> + <format>notice</format> + <description>Notice</description> + </valueHelp> + <valueHelp> + <format>warn</format> + <description>Warning</description> + </valueHelp> + <constraint> + <regex>(alert|crit|debug|disabled|emerg|err|info|notice|warn)</regex> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="physmem"> + <properties> + <help>Memory settings</help> + </properties> + <children> + <leafNode name="max-size"> + <properties> + <help>Set memory size for protectable memory allocator (pmalloc) memory space</help> + <valueHelp> + <format><number>m</format> + <description>Megabyte</description> + </valueHelp> + <valueHelp> + <format><number>g</format> + <description>Gigabyte</description> + </valueHelp> + </properties> + </leafNode> + </children> + </node> + <node name="unix"> + <properties> + <help>Unix settings</help> + </properties> + <children> + <leafNode name="poll-sleep-usec"> + <properties> + <help>Add a fixed-sleep between main loop poll</help> + <valueHelp> + <format>u32:0-4294967295</format> + <description>Number of receive queues</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + <defaultValue>0</defaultValue> + </leafNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 68234089c..9b7da89fa 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -21,7 +21,8 @@ from vyos.util import popen # These drivers do not support using ethtool to change the speed, duplex, or # flow control settings _drivers_without_speed_duplex_flow = ['vmxnet3', 'virtio_net', 'xen_netfront', - 'iavf', 'ice', 'i40e', 'hv_netvsc', 'veth', 'ixgbevf'] + 'iavf', 'ice', 'i40e', 'hv_netvsc', 'veth', 'ixgbevf', + 'tun'] class Ethtool: """ diff --git a/python/vyos/vpp.py b/python/vyos/vpp.py new file mode 100644 index 000000000..decc6c087 --- /dev/null +++ b/python/vyos/vpp.py @@ -0,0 +1,28 @@ +# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +from vyos.util import call + + +def lcp_create_host_interface(ifname): + """LCP reprepsents a connection point between VPP dataplane + and the host stack + """ + return call(f'vppctl lcp create {ifname} host-if {ifname}') + + +def set_interface_rx_mode(ifname, mode): + """Rx mode""" + return call(f'sudo vppctl set interface rx-mode {ifname} {mode}') diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py new file mode 100755 index 000000000..aa6c14e89 --- /dev/null +++ b/src/conf_mode/vpp.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from pathlib import Path + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.ifconfig import Section +from vyos.ifconfig import EthernetIf +from vyos.ifconfig import interface +from vyos.util import call +from vyos.util import rc_cmd +from vyos.template import render +from vyos.xml import defaults + +from vyos import ConfigError +from vyos import vpp +from vyos import airbag + +airbag.enable() + +service_name = 'vpp' +service_conf = Path(f'/run/vpp/{service_name}.conf') +systemd_override = '/run/systemd/system/vpp.service.d/10-override.conf' + + +def _get_pci_address_by_interface(iface): + from vyos.util import rc_cmd + rc, out = rc_cmd(f'ethtool -i {iface}') + if rc == 0: + output_lines = out.split('\n') + for line in output_lines: + if 'bus-info' in line: + return line.split(None, 1)[1].strip() + + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + base = ['vpp'] + base_ethernet = ['interfaces', 'ethernet'] + if not conf.exists(base): + return None + + config = conf.get_config_dict(base, + get_first_key=True, + key_mangling=('-', '_'), + no_tag_node_value_mangle=True) + + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + if 'interface' in default_values: + del default_values['interface'] + config = dict_merge(default_values, config) + + if 'interface' in config: + for iface, iface_config in config['interface'].items(): + default_values_iface = defaults(base + ['interface']) + config['interface'][iface] = dict_merge(default_values_iface, config['interface'][iface]) + + # Get PCI address auto + for iface, iface_config in config['interface'].items(): + if iface_config['pci'] == 'auto': + config['interface'][iface]['pci'] = _get_pci_address_by_interface(iface) + + config['other_interfaces'] = conf.get_config_dict(base_ethernet, key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) + + return config + + +def verify(config): + # bail out early - looks like removal from running config + if not config: + return None + + if 'interface' not in config: + raise ConfigError(f'"interface" is required but not set!') + + if 'cpu' in config: + if 'corelist_workers' in config['cpu'] and 'main_core' not in config['cpu']: + raise ConfigError(f'"cpu main-core" is required but not set!') + + +def generate(config): + if not config: + # Remove old config and return + service_conf.unlink(missing_ok=True) + return None + + render(service_conf, 'vpp/startup.conf.j2', config) + render(systemd_override, 'vpp/override.conf.j2', config) + + return None + + +def apply(config): + if not config: + print(f'systemctl stop {service_name}.service') + call(f'systemctl stop {service_name}.service') + return + else: + print(f'systemctl restart {service_name}.service') + call(f'systemctl restart {service_name}.service') + + call('systemctl daemon-reload') + + call('sudo sysctl -w vm.nr_hugepages=4096') + for iface, _ in config['interface'].items(): + # Create lcp + if iface not in Section.interfaces(): + vpp.lcp_create_host_interface(iface) + + # update interface config + #e = EthernetIf(iface) + #e.update(config['other_interfaces'][iface]) + + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) |