Skip to content

RTM_IFINFO routing messages aren't parsed correctly on NetBSD (32-bit) #5054

Description

@evverx

Brief description

scapy fails as soon as it's launched with

bash-5.3# python3.14 -m scapy
...
  File "/root/scapy/scapy/all.py", line 26, in <module>
    from scapy.route import *
  File "/root/scapy/scapy/route.py", line 250, in <module>
    conf.route = Route()
                 ~~~~~^^
  File "/root/scapy/scapy/route.py", line 37, in __init__
    self.resync()
    ~~~~~~~~~~~^^
  File "/root/scapy/scapy/route.py", line 47, in resync
    self.routes = read_routes()
                  ~~~~~~~~~~~^^
  File "/root/scapy/scapy/arch/bpf/pfroute.py", line 1084, in read_routes
    iface = ifaces[msg.rtm_index]["name"]
            ~~~~~~^^^^^^^^^^^^^^^
KeyError: 2

I tracked it down and it turned out that RTM_IFINFO messages aren't parsed correctly. For example

from scapy.arch.bpf import pfroute
b = b'\xa8\x00\x04\x14\x10\x00\x00\x00I\x80\x00\x00\x03\x00\x00\x00\x18\x00\x00\x00\x02\x00\x00\x00\x98\x81\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88.cj\x00\x00\x00\x00\n/\x1b#\x00\x00\x00\x00\x0b\x12\x03\x00\x18\x03\x00\x00lo0\x00\x00\x00\x00\x00'
pfroute.pfmsghdr(b).show()

shows

###[ pfmsghdr ]###
  rtm_msglen= 168
  rtm_version= 4
  rtm_type  = RTM_IFINFO
###[ if_msghdr ]###
     ifm_addrs = RTA_IFP
     ifm_flags = UP+LOOPBACK+DRV_RUNNING+MULTICAST
     ifm_index = 3
     _ifm_spare1= 0
     \ifm_data  \
      |###[ if_data ]###
      |  ifi_type  = 24
      |  ifi_addrlen= 0
      |  ifi_hdrlen= 0
      |  ifi_link_state= 512
      |  ifi_mtu   = 8493056
      |  ifi_metric= 0
      |  ifi_baudrate= 0
      |  ifi_ipackets= 768
      |  ifi_ierrors= 0
      |  ifi_opackets= 768
      |  ifi_oerrors= 0
      |  ifi_collision= 0
      |  ifi_ibytes= 29952
      |  ifi_obytes= 29952
      |  ifi_imcasts= 0
      |  ifi_omcasts= 0
      |  ifi_iqdrops= 0
      |  ifi_noproto= 0
      |  ifi_lastchange= b'\x00\x88.cj'
     \addrs     \
      |###[ sockaddr ]###
      |  sa_len    = 10
      |  sa_family = 47
      |  sa_data   = 1b23000000000b12
      |###[ sockaddr ]###
      |  sa_len    = 24
      |  sa_family = 3
      |  sa_data   = 00006c6f300000000000

where ifi_lastchange is shorter than it should be.

NetBSD switched to 64-bit time_t in https://www.netbsd.org/releases/formal-6/NetBSD-6.0.html so struct timespec is (8+4) bytes.

I went with the following quick fix locally

diff --git a/scapy/arch/bpf/pfroute.py b/scapy/arch/bpf/pfroute.py
index dcb78457..6e3866cd 100644
--- a/scapy/arch/bpf/pfroute.py
+++ b/scapy/arch/bpf/pfroute.py
@@ -505,7 +505,7 @@ elif NETBSD:
             Field("ifi_omcasts", 0, fmt="=Q"),
             Field("ifi_iqdrops", 0, fmt="=Q"),
             Field("ifi_noproto", 0, fmt="=Q"),
-            StrFixedLenField("ifi_lastchange", 0, length=16 if IS_64BITS else 8),
+            StrFixedLenField("ifi_lastchange", 0, length=16 if IS_64BITS else 12),
         ]
 
         def default_payload_class(self, payload: bytes) -> Type[Packet]:

and got

###[ pfmsghdr ]###
  rtm_msglen= 168
  rtm_version= 4
  rtm_type  = RTM_IFINFO
###[ if_msghdr ]###
     ifm_addrs = RTA_IFP
     ifm_flags = UP+LOOPBACK+DRV_RUNNING+MULTICAST
     ifm_index = 3
     _ifm_spare1= 0
     \ifm_data  \
      |###[ if_data ]###
      |  ifi_type  = 24
      |  ifi_addrlen= 0
      |  ifi_hdrlen= 0
      |  ifi_link_state= 512
      |  ifi_mtu   = 8493056
      |  ifi_metric= 0
      |  ifi_baudrate= 0
      |  ifi_ipackets= 768
      |  ifi_ierrors= 0
      |  ifi_opackets= 768
      |  ifi_oerrors= 0
      |  ifi_collision= 0
      |  ifi_ibytes= 29952
      |  ifi_obytes= 29952
      |  ifi_imcasts= 0
      |  ifi_omcasts= 0
      |  ifi_iqdrops= 0
      |  ifi_noproto= 0
      |  ifi_lastchange= b'\x00\x88.cj\x00\x00\x00\x00\n/\x1b'
     \addrs     \
      |###[ sockaddr ]###
      |  sa_len    = 11
      |  sa_family = AF_LINK
      |  sdl_index = 3
      |  sdl_type  = 24
      |  sdl_nlen  = 3
      |  sdl_alen  = 0
      |  sdl_slen  = 0
      |  sdl_iface = b'lo0'
      |  sdl_addr  = b''
      |  sdl_sel   = b''
      |  sdl_data  = 
      |  sdl_pad   = 0000000000

but it would maybe be better to calculate the size of struct timespec dynamically. I'm not sure yet.

Scapy version

326cf5e

Python version

3.14.3

Operating system

NetBSD 10.1 i386

Additional environment information

No response

How to reproduce

Run python3.14 -m scapy on a i386 NetBSD machine.

Actual result

scapy fails with

...
  File "/root/scapy/scapy/all.py", line 26, in <module>
    from scapy.route import *
  File "/root/scapy/scapy/route.py", line 250, in <module>
    conf.route = Route()
                 ~~~~~^^
  File "/root/scapy/scapy/route.py", line 37, in __init__
    self.resync()
    ~~~~~~~~~~~^^
  File "/root/scapy/scapy/route.py", line 47, in resync
    self.routes = read_routes()
                  ~~~~~~~~~~~^^
  File "/root/scapy/scapy/arch/bpf/pfroute.py", line 1084, in read_routes
    iface = ifaces[msg.rtm_index]["name"]
            ~~~~~~^^^^^^^^^^^^^^^
KeyError: 2

Expected result

scapy should start normally.

Related resources

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions