scapy icon indicating copy to clipboard operation
scapy copied to clipboard

Speed up route import on Linux-based systems

Open grawity opened this issue 5 years ago • 4 comments

Just wanted to submit this for tracking purposes – I can't promise a PR anytime soon.

Brief description

On startup, Scapy reads the entire kernel routing table (not just the main table, all tables) at a rate of ~700 routes per second. On my system, which has the full IPv6 BGP table (~70k routes) this means Scapy takes ~1m45s to start.

Perhaps part of the problem is that it reads /proc/net/route and /proc/net/ipv6_route – which are very slow on current Linux kernels. Route query using netlink is 6–8 times faster.

Environment

  • Scapy version: git-archive.devbad14cb1a
  • Python version: 3.7
  • Operating System: Linux 4.9.x, 4.19.56, 5.1.x

How to reproduce

Add tens of thousands of IPv6 routes to the kernel, then start Scapy.

Actual result

Time it takes to start scapy:

# time echo "exit()" | scapy
[logo omitted]
>>> 
real	1m44.910s
user	0m7.717s
sys	1m30.925s

Expected result

Scapy should start somewhat faster.

If possible, Scapy should use rtnetlink or ip -json route (which in turn uses netlink). Maybe runtime detection of whether "ip -json" is supported. Maybe only load the "main" table and not all tables.

Related resources

Time it takes to get route dumps from the kernel, via Netlink and via /proc:

$ time ip -6 route list table all | wc -l
70831

real	0m0.529s
user	0m0.172s
sys	0m0.346s
$ time cat /proc/net/ipv6_route | wc -l
70787

real	0m23.524s
user	0m0.018s
sys	0m22.658s

grawity avatar Jul 08 '19 19:07 grawity

Thanks for reporting the issue. Does sending packets work fine? I am afraid that the route lookup will be slow as it was not designed to handle a full-view.

guedou avatar Jul 09 '19 16:07 guedou

It is reasonably fast (the first route lookup for a destination takes ~2 seconds until "Finished sending 1 packets" – not blazing fast, but good enough for an interactive tool).

...But I guess that's why the loading takes so long – not /proc/net/route but rather the route addition becoming slower and slower with each new route?

Though I still think it'd be beneficial to use a method that allows dumping only the main&local kernel routing tables and ignoring the non-default ones (in my case the full BGP view is in a non-default kernel table).

grawity avatar Jul 09 '19 21:07 grawity

It is true that we could be looking for a more time-efficient method. This exact process was done for Windows some time ago, and we ended up performing C calls to get the routes faster.

It sounds much harder to do on Linux, though we could look into Netlink sockets: http://olegkutkov.me/2019/03/24/getting-linux-routing-table-using-netlink/

In fact, there are already multiple Python libraries:

  • https://github.com/svinota/pyroute2 (quite a messy project. Lots of unfinished implementations (BSD, Windows...) that simply return [])
  • https://github.com/facebook/gnlpy (by Facebook. Only 1-2 files)

Feel free to provide a PR if you have an idea ! I personally won't look into this before 2.4.3 is released, because we don't want to introduce new untested material so close to the release.

gpotter2 avatar Jul 11 '19 22:07 gpotter2

Update: A proper workaround woule be to simply start Scapy in a ip namespace, this way you can precisely control what it has access to on boot. If you don't want it to have your 2000 ipv6 routes, configure it this way.

It IS scapy's expected behavior to load and process all routes it has access to. Although it would be nice if it were faster. But this will be low-priority since the above workaround is very reasonable.

gpotter2 avatar Jun 25 '22 19:06 gpotter2