svinota / pyroute2

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

How to use poll/epoll to receive Netlink broadcast? #977

Open shadychan opened 2 years ago

shadychan commented 2 years ago

I saw these information in the Quickstart :

In the runtime pyroute2 socket objects behave as normal sockets. One can use them in the poll/select, one can call recv() and sendmsg():

from pyroute2 import IPRoute

# create RTNL socket
ipr = IPRoute()

# subscribe to broadcast messages
ipr.bind()

# wait for data (do not parse it)
data = ipr.recv(65535)

# parse received data
messages = ipr.marshal.parse(data)

# shortcut: recv() + parse()
#
# (under the hood is much more, but for
# simplicity it's enough to say so)
#
messages = ipr.get()

So I wrote a demo according to it:

from pyroute2 import IPRoute

if __name__ == '__main__':
    # create RTNL socket
    ipr = IPRoute()

    # subscribe to broadcast messages
    ipr.bind()

    while True:
        # wait for data (do not parse it)
        data = ipr.recv(65535)

        # parse received data
        messages = ipr.marshal.parse(data)

        # print message
        for msg in messages:
            print(msg)

I run this demo and found the script would be blocked at the line data = ipr.recv(65535).

So, I want to use poll/epoll method to get the messages asynchronously. But I have no idea how to write the codes?

I checkout some poll/epoll examples in Google. But I found all the examples are based on a socket object. I have no idea how can I get this socket object in Pyroute2. Here are some poll/epoll introduction I have found: https://pythontic.com/modules/select/poll https://blog.csdn.net/jialan75/article/details/122005874

svinota commented 2 years ago

Here we go:

import select

from pyroute2 import IPRoute

print('create poll object')
poll = select.poll()

print('create IPRoute object')
with IPRoute(nlm_generator=True) as ipr:

    print('register IPRoute for select.POLLIN')
    poll.register(ipr, select.POLLIN)

    print('bind IPRoute to get broadcasts')
    ipr.bind()

    print('wait for input data')
    poll.poll()

    print('receive and parse; you can use ipr.recv() here or ipr.get()')
    for msg in ipr.get():
        print(msg)

The result:

$ python test.py 
create poll object
create IPRoute object
register IPRoute for select.POLLIN
bind IPRoute to get broadcasts
wait for input data
receive and parse; you can use ipr.recv() here or ipr.get()
{'family': 0, '__align': (), 'ifi_type': 1, 'index': 2, 'flags': 69699, 'change': 0, 'attrs': [('IFLA_IFNAME', 'wlp3s0'), ('IFLA_WIRELESS', {'attrs': [('SIOCGIWSCAN', '00:00:00:00:00:00:00:00:00:00:00:00', 32768)]})], 'header': {'length': 64, 'type': 16, 'flags': 0, 'sequence_number': 0, 'pid': 0, 'error': None, 'target': 'localhost', 'stats': Stats(qsize=0, delta=0, delay=0)}, 'state': 'up', 'event': 'RTM_NEWLINK'}

Since NetlinkSocket objects behave (or pretend to behave) like a normal socket, so they have .fileno() and may be used for poll/select.

Explanation about nlm_generator see here: https://github.com/svinota/pyroute2/discussions/972

shadychan commented 2 years ago

@svinota Thanks! I will try it!