Repository: secdev/scapy

Issue:
Add support for IPv6 addresses in VRRPv3 layer
#### Brief description

The VRRPv3 layer has a field addrlist which supports IPv4 addresses only.

This prevents use of the layer for generating IPv6 VRRPv3 advertisement packets.

#### Environment

-   Scapy version: `v2.4.3-242-gdcfb63d0`
-   Python version: `3.7.6`
-   Operating System: `macOS Mojave 10.14.6`

Problem was originally observed on CentOS 7.7 with scapy v2.4.3 using python 3.6. Tested using current github master branch (as above) on macOS to confirm.

#### How to reproduce

```
from scapy.layers.l2 import Ether
from scapy.layers.inet6 import IPv6
from scapy.layers.vrrp import VRRPv3

pkt = (Ether(src="00:00:5e:00:02:64",dst="33:33:00:00:00:12")/IPv6(src="2001:db8::1",dst="ff02::12")/VRRPv3(vrid=100,ipcount=1,addrlist=[ "2001:db8::100" ]))
```

#### Actual result

```
Traceback (most recent call last):
  File "/Users/mgsmith/src/scapy/scapy/fields.py", line 535, in h2i
    inet_aton(x)
OSError: illegal IP address string passed to inet_aton

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/mgsmith/src/scapy/scapy/base_classes.py", line 266, in __call__
    i.__init__(*args, **kargs)
  File "/Users/mgsmith/src/scapy/scapy/packet.py", line 157, in __init__
    self.fields[fname] = self.get_field(fname).any2i(self, value)
  File "/Users/mgsmith/src/scapy/scapy/fields.py", line 1412, in any2i
    return [self.field.any2i(pkt, e) for e in x]
  File "/Users/mgsmith/src/scapy/scapy/fields.py", line 1412, in <listcomp>
    return [self.field.any2i(pkt, e) for e in x]
  File "/Users/mgsmith/src/scapy/scapy/fields.py", line 562, in any2i
    return self.h2i(pkt, x)
  File "/Users/mgsmith/src/scapy/scapy/fields.py", line 537, in h2i
    x = Net(x)
  File "/Users/mgsmith/src/scapy/scapy/base_classes.py", line 108, in __init__
    self.parsed, self.netmask = self._parse_net(net)
  File "/Users/mgsmith/src/scapy/scapy/base_classes.py", line 101, in _parse_net
    tmp[0] = socket.gethostbyname(tmp[0])
socket.gaierror: [Errno 8] nodename nor servname provided, or not known
```

#### Expected result

No error, valid packet created.

#### Related resources

RFC 5798
I have a patch which adds this support which I will submit as a pull request. See https://github.com/mgsmith1000/scapy/commit/020a6695d48d16b5521bf78a4a1ef5efba14fd21.



Discussion:

Resolving patch:
diff --git a/scapy/layers/vrrp.py b/scapy/layers/vrrp.py
--- a/scapy/layers/vrrp.py
+++ b/scapy/layers/vrrp.py
@@ -10,7 +10,7 @@
 
 from scapy.packet import Packet, bind_layers
 from scapy.fields import BitField, ByteField, FieldLenField, FieldListField, \
-    IPField, IntField, XShortField
+    IPField, IP6Field, IntField, MultipleTypeField, StrField, XShortField
 from scapy.compat import chb, orb
 from scapy.layers.inet import IP, in4_chksum, checksum
 from scapy.layers.inet6 import IPv6, in6_chksum
@@ -62,9 +62,18 @@ class VRRPv3(Packet):
         BitField("res", 0, 4),
         BitField("adv", 100, 12),
         XShortField("chksum", None),
-        # FIXME: addrlist should also allow IPv6 addresses :/
-        FieldListField("addrlist", [], IPField("", "0.0.0.0"),
-                       count_from=lambda pkt: pkt.ipcount)]
+        MultipleTypeField(
+            [
+                (FieldListField("addrlist", [], IPField("", "0.0.0.0"),
+                                count_from=lambda pkt: pkt.ipcount),
+                 lambda p: isinstance(p.underlayer, IP)),
+                (FieldListField("addrlist", [], IP6Field("", "::"),
+                                count_from=lambda pkt: pkt.ipcount),
+                 lambda p: isinstance(p.underlayer, IPv6)),
+            ],
+            StrField("addrlist", "")
+        )
+    ]
 
     def post_build(self, p, pay):
         if self.chksum is None:


Test patch:
diff --git a/test/regression.uts b/test/regression.uts
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -10572,6 +10572,14 @@ s == b'E\x00\x00$\x00\x01\x00\x00@p|g\x7f\x00\x00\x01\x7f\x00\x00\x01!\x01d\x00\
 p = IP(s)
 VRRP in p and p[VRRP].chksum == 0x7afd
 
+= VRRP IPv6 - build
+s6 = raw(IPv6()/VRRPv3())
+s6 == b'`\x00\x00\x00\x00\x08p@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x011\x01d\x01\x00dj\x1f'
+
+= VRRP IPv6 - dissection
+p6 = IPv6(s6)
+VRRPv3 in p6 and p6[VRRPv3].chksum == 0x6a1f
+
 = VRRP - chksums
 # VRRPv3
 p = Ether(src="00:00:5e:00:02:02",dst="01:00:5e:00:00:12")/IP(src="20.0.0.3", dst="224.0.0.18",ttl=255)/VRRPv3(priority=254,vrid=2,version=3,adv=1,addrlist=["20.0.1.2","20.0.1.3"])
@@ -10582,6 +10590,12 @@ p = Ether(src="00:00:5e:00:02:02",dst="01:00:5e:00:00:12")/IP(src="20.0.0.3", ds
 b = Ether(raw(p))
 assert b[VRRP].chksum == 0xc6f4
 
+= VRRP IPv6 - chksums
+# VRRPv3 IPv6
+p = Ether(src="00:00:5e:00:02:02",dst="33:33:00:00:00:12")/IPv6(src="2001:db8::1", dst="ff02::12",hlim=255)/VRRPv3(priority=254,vrid=2,version=3,adv=1,ipcount=2,addrlist=["2001:db8::2","2001:db8::3"])
+c = Ether(raw(p))
+assert c[VRRPv3].chksum == 0x481b
+
 ############
 ############
 + L2TP tests