svinota / pyroute2

Python Netlink and PF_ROUTE library — network configuration and monitoring
https://pyroute2.org/
Other
944 stars 243 forks source link

IPRoute.neigh("dump") does not use NDA_IFINDEX #739

Open ffourcot opened 3 years ago

ffourcot commented 3 years ago

Hello,

NDA_IFINDEX is currently one of the only way to filter ARP cache output in kernel. However, since ifindex is a field name and a NDA name, only the first one is set in the request:

In [3]: IPRoute().neigh('dump', ifindex=2)
{'family': <AddressFamily.AF_INET: 2>, '__pad': 0, 'ifindex': 2, 'state': 0, 'flags': 0, 'ndm_type': 0, 'attrs': [], 'value': <class 'pyroute2.netlink.NotInitialized'>, 'header': {'type': 30, 'flags': 769, 'sequence_number': 255, 'pid': 5741, 'length': 28}}

Message should be:

{'family': <AddressFamily.AF_INET: 2>, '__pad': 0, 'ifindex': 0, 'state': 0, 'flags': 0, 'ndm_type': 0, 'attrs': [('NDA_IFINDEX', 2)], 'value': <class 'pyroute2.netlink.NotInitialized'>, 'header': {'type': 30, 'flags': 769, 'sequence_number': 255, 'pid': 5741, 'length': 36}}

Since ifindex field is not read by the kernel to enable filtering, the performance impact can be huge. With a hotpatch (just skip ifindex in fields), the execution time is reduced from 1,2s to 90ms on a simple scenario.

I can try to provide a clean patch if you want

ljluestc commented 6 months ago
from pyroute2 import IPRoute

def patch_neigh_dump(ipr):
    # Define the attribute value for NDA_IFINDEX
    NDA_IFINDEX = 3

    # Retrieve the original `neigh` method
    original_neigh = ipr.neigh

    def custom_neigh_dump(*args, **kwargs):
        # Add the NDA_IFINDEX attribute to the kwargs
        kwargs['attrs'] = [('NDA_IFINDEX', kwargs.get('ifindex', 0))]

        # Remove the ifindex from kwargs to avoid setting it twice
        if 'ifindex' in kwargs:
            del kwargs['ifindex']

        # Call the original `neigh` method with updated kwargs
        return original_neigh(*args, **kwargs)

    # Replace the original `neigh` method with the custom implementation
    ipr.neigh = custom_neigh_dump

# Apply the patch
ipr = IPRoute()
patch_neigh_dump(ipr)

# Now, when calling `neigh("dump")`, it should correctly utilize NDA_IFINDEX
result = ipr.neigh("dump")
print(result)
svinota commented 6 months ago

I knew we hit this name collision again :|

Thanks @ffourcot for the heads-up, let's see what we can do with it.