--- a/usr/lib/python3/dist-packages/vyos/vpp/control_vpp.py +++ b/usr/lib/python3/dist-packages/vyos/vpp/control_vpp.py @@ -723,6 +723,39 @@ def disable_icmpv6_ra_punt(self, ifname: str) -> None: feature_name='ip6-icmp-ra-punt', ) + @_Decorators.api_call + def mpls_enabled_interfaces(self) -> list[int]: + """List the interfaces with MPLS enabled + + Returns: + list[int]: interface indexes + """ + return [ + iface.sw_if_index + for iface in self.__vpp_api_client.api.mpls_interface_dump() + ] + + @_Decorators.api_call + def set_interface_mpls(self, ifname: str, enable: bool = True) -> None: + """Enable or disable MPLS on an interface + + linux-cp mirrors the state to the kernel interface of the pair. VPP + refuses to enable MPLS while the default MPLS table does not exist, + it is created here. + + Args: + ifname (str): name of an interface in VPP + enable (bool): enable or disable MPLS + """ + if enable: + self.__vpp_api_client.api.mpls_table_add_del( + mt_is_add=True, mt_table={'mt_table_id': 0} + ) + self.__vpp_api_client.api.sw_interface_set_mpls_enable( + sw_if_index=self.get_sw_if_index(ifname), + enable=enable, + ) + @_Decorators.api_call def set_promisc(self, ifname: str, enable: bool = True) -> None: """Set promiscuous mode for interface --- a/usr/libexec/vyos/conf_mode/protocols_mpls.py +++ b/usr/libexec/vyos/conf_mode/protocols_mpls.py @@ -24,8 +24,10 @@ from vyos.frrender import get_frrender_dict from vyos.utils.dict import dict_search from vyos.utils.file import read_file +from vyos.utils.process import is_systemd_service_active from vyos.utils.system import sysctl_write from vyos.configverify import verify_interface_exists +from vyos.vpp.control_vpp import VPPControl from vyos import ConfigError from vyos import airbag airbag.enable() @@ -117,6 +119,20 @@ def apply(config_dict): for system_interface in system_interfaces: sysctl_write(['net', 'mpls', 'conf', system_interface, 'input'], 0) + # VPP drops labeled packets on an interface that has MPLS disabled, + # whatever the kernel setting is. linux-cp copies the state to the kernel + # interface of the pair. + if is_systemd_service_active('vpp.service'): + vpp = VPPControl() + enabled = vpp.mpls_enabled_interfaces() + configured = mpls.get('interface', {}) + for pair in vpp.lcp_pairs_list(): + if pair['kernel_name'] in configured: + if pair['vpp_index_hw'] not in enabled: + vpp.set_interface_mpls(pair['vpp_name_hw'], True) + elif pair['vpp_index_hw'] in enabled: + vpp.set_interface_mpls(pair['vpp_name_hw'], False) + return None if __name__ == '__main__': --- a/usr/libexec/vyos/conf_mode/vpp.py +++ b/usr/libexec/vyos/conf_mode/vpp.py @@ -618,6 +618,10 @@ def get_config(config=None): set_dependents('pppoe_server', conf) config['changed_pppoe_ifaces'] = changed_pppoe_ifaces + # MPLS dependency, a VPP restart drops the MPLS state of the interfaces + if conf.exists(['protocols', 'mpls', 'interface']): + set_dependents('mpls', conf) + return config