Repository: secdev/scapy

Issue:
Route parsing fails after calling `setns`
#### Brief description

When run inside a network namespace with `setns` called form python, scapy fails to read the routes correctly.

#### Environment

- Scapy version: 2.4.0, 2.4.0.dev396
- Python version: 3.7, 3.6
- Operating System: Arch Linux

#### How to reproduce

1. Setup a network namespace
2. Add a inteface to that namespace
3. Run the attaches script

```
sudo ip netns add TEST
sudo ip link set netns TEST dev enp0s25
sudo python netns-crash.py
```

#### Actual result

```
Traceback (most recent call last):
  File "netns-crash.py", line 5, in <module>
    conf.route.resync()
  File "[...]/venv/lib/python3.7/site-packages/scapy/route.py", line 36, in resync
    self.routes = read_routes()
  File "[...]/venv/lib/python3.7/site-packages/scapy/arch/linux.py", line 275, in read_routes
    ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
UnboundLocalError: local variable 'ifreq' referenced before assignment
```

#### Expected result

The routes should be resynced, then a packet should be sent.
When running the script without the call to `conf.route.resync`, it crashed because it tries to send it on an interface only available in the main namespace


Discussion:
Just checked and confirmed that the same crash occurs on python 3.6
I'm not sure how this would happen in current development code (at least the bug about `ifreq` being undefined seems fixed in current development version, while it existed in 2.4.0):

```Python
        try:
            ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", iff.encode("utf8")))  # noqa: E501
        except IOError:  # interface is present in routing tables but does not have any assigned IP  # noqa: E501
            ifaddr = "0.0.0.0"
        else:
            addrfamily = struct.unpack("h", ifreq[16:18])[0]
            if addrfamily == socket.AF_INET:
                ifaddr = scapy.utils.inet_ntoa(ifreq[20:24])
            else:
                warning("Interface %s: unknown address family (%i)", iff, addrfamily)  # noqa: E501
                continue
```

Do you get the same message with 2.4.0.dev396?
Yes, I do:

```
Traceback (most recent call last):
  File "netns-crash.py", line 5, in <module>
    conf.route.resync()
  File "[...]venv/lib/python3.6/site-packages/scapy/route.py", line 36, in resync
    self.routes = read_routes()
  File "[...]venv/lib/python3.6/site-packages/scapy/arch/linux.py", line 283, in read_routes
    ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
UnboundLocalError: local variable 'ifreq' referenced before assignment
```

The error occurs a bit below the code you pasted (line 283):

```
        # Attempt to detect an interface alias based on addresses inconsistencies  # noqa: E501
        dst_int = socket.htonl(int(dst, 16)) & 0xffffffff
        msk_int = socket.htonl(int(msk, 16)) & 0xffffffff
>        ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
        gw_str = scapy.utils.inet_ntoa(struct.pack("I", int(gw, 16)))
        metric = int(metric)
```
You're totally right!

Resolving patch:
diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
--- a/scapy/arch/linux.py
+++ b/scapy/arch/linux.py
@@ -269,10 +269,12 @@ def read_routes():
             ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", iff.encode("utf8")))  # noqa: E501
         except IOError:  # interface is present in routing tables but does not have any assigned IP  # noqa: E501
             ifaddr = "0.0.0.0"
+            ifaddr_int = 0
         else:
             addrfamily = struct.unpack("h", ifreq[16:18])[0]
             if addrfamily == socket.AF_INET:
                 ifaddr = scapy.utils.inet_ntoa(ifreq[20:24])
+                ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
             else:
                 warning("Interface %s: unknown address family (%i)", iff, addrfamily)  # noqa: E501
                 continue
@@ -280,7 +282,6 @@ def read_routes():
         # Attempt to detect an interface alias based on addresses inconsistencies  # noqa: E501
         dst_int = socket.htonl(int(dst, 16)) & 0xffffffff
         msk_int = socket.htonl(int(msk, 16)) & 0xffffffff
-        ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
         gw_str = scapy.utils.inet_ntoa(struct.pack("I", int(gw, 16)))
         metric = int(metric)
 


Test patch:
diff --git a/test/linux.uts b/test/linux.uts
--- a/test/linux.uts
+++ b/test/linux.uts
@@ -313,3 +313,20 @@ except subprocess.CalledProcessError:
     assert False
 except Exception:
     assert False
+
+
+= Routing table, interface with no names
+~ linux
+
+from mock import patch
+
+@patch("scapy.arch.linux.ioctl")
+def test_read_routes(mock_ioctl):
+    def raise_ioerror(*args, **kwargs):
+        if args[1] == 0x8912:
+            return args[2]
+        raise IOError
+    mock_ioctl.side_effect = raise_ioerror
+    read_routes()
+
+test_read_routes()