Repository: secdev/scapy

Issue:
Packet with scapy.contrib.lldp.LLDPDUManagementAddress raises a TypeError when method repr() is called on it.
#### Brief description

Packet with scapy.contrib.lldp.LLDPDUManagementAddress raises a TypeError when method __repr__() is called on it.

#### Environment

-   Scapy version: c38a4782928aaa0e657c41638ce1f469aac2edb1 -  2.4.4.dev308
-   Python version: 3.9.0
-   Operating System: Windows 10 Pro

#### How to reproduce

```python
import socket

from scapy.layers.l2 import Ether
from scapy.contrib.lldp import *

src_mac = 'aa:bb:cc:dd:ee:ff'
dst_mas = '11:22:33:44:55:66'

eth = Ether(src=src_mac, dst=dst_mas)
chassis_id = LLDPDUChassisID(subtype=0x04, id=src_mac)
port_id = LLDPDUPortID(subtype=0x05, id='1')
ttl = LLDPDUTimeToLive(ttl=5)

frame = eth / chassis_id / port_id / ttl
frame.show2()
frame.__repr__()

ip = '192.168.0.10'
management_address = LLDPDUManagementAddress(
    management_address_subtype=0x01, management_address=socket.inet_aton(ip))

frame_2 = frame.copy()
frame_2 /= management_address
frame_2.show2()
frame_2.__repr__()

```

#### Actual result
Code from the reproduction works ok until the last line frame.__repr__() when TypeError is raised.

```
Traceback (most recent call last):
  File "C:\Users\Bartek\PycharmProjects\scapy\scapy\my_test.py", line 25, in <module>
    frame_2.__repr__()
  File "C:\Users\Bartek\PycharmProjects\scapy\scapy\packet.py", line 557, in __repr__
    repr(self.payload),
  File "C:\Users\Bartek\PycharmProjects\scapy\scapy\packet.py", line 557, in __repr__
    repr(self.payload),
  File "C:\Users\Bartek\PycharmProjects\scapy\scapy\packet.py", line 557, in __repr__
    repr(self.payload),
  [Previous line repeated 1 more time]
  File "C:\Users\Bartek\PycharmProjects\scapy\scapy\packet.py", line 535, in __repr__
    val = f.i2repr(self, fval)
  File "C:\Users\Bartek\PycharmProjects\scapy\scapy\fields.py", line 1818, in i2repr
    x[:(self.length_from or (lambda x: 0))(pkt)]  # type: ignore
  File "C:\Users\Bartek\PycharmProjects\scapy\scapy\contrib\lldp.py", line 635, in <lambda>
    pkt._management_address_string_length - 1),
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'

Process finished with exit code 1

```


#### Expected result

I would expect not to get the error when call __repr__() on the packet with LLDPDUManagementAddress layer.

#### Related resources

N/A



Discussion:

Resolving patch:
diff --git a/scapy/contrib/lldp.py b/scapy/contrib/lldp.py
--- a/scapy/contrib/lldp.py
+++ b/scapy/contrib/lldp.py
@@ -332,7 +332,8 @@ class LLDPDUChassisID(LLDPDU):
                 IPField('id', None),
                 lambda pkt: pkt.subtype == 0x05
             ),
-        ], StrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
+        ], StrLenField('id', '', length_from=lambda pkt: 0 if pkt._length is
+                       None else pkt._length - 1)
         )
     ]
 
@@ -387,7 +388,8 @@ class LLDPDUPortID(LLDPDU):
                 IPField('id', None),
                 lambda pkt: pkt.subtype == 0x04
             ),
-        ], StrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
+        ], StrLenField('id', '', length_from=lambda pkt: 0 if pkt._length is
+                       None else pkt._length - 1)
         )
     ]
 
@@ -631,7 +633,8 @@ class LLDPDUManagementAddress(LLDPDU):
         ByteEnumField('management_address_subtype', 0x00,
                       IANA_ADDRESS_FAMILY_NUMBERS),
         XStrLenField('management_address', '',
-                     length_from=lambda pkt:
+                     length_from=lambda pkt: 0
+                     if pkt._management_address_string_length is None else
                      pkt._management_address_string_length - 1),
         ByteEnumField('interface_numbering_subtype',
                       SUBTYPE_INTERFACE_NUMBER_UNKNOWN,
@@ -681,7 +684,9 @@ class LLDPDUGenericOrganisationSpecific(LLDPDU):
         BitFieldLenField('_length', None, 9, length_of='data', adjust=lambda pkt, x: len(pkt.data) + 4),  # noqa: E501
         ThreeBytesEnumField('org_code', 0, ORG_UNIQUE_CODES),
         ByteField('subtype', 0x00),
-        XStrLenField('data', '', length_from=lambda pkt: pkt._length - 4)
+        XStrLenField('data', '',
+                     length_from=lambda pkt: 0 if pkt._length is None else
+                     pkt._length - 4)
     ]
 
 


Test patch:
diff --git a/test/contrib/lldp.uts b/test/contrib/lldp.uts
--- a/test/contrib/lldp.uts
+++ b/test/contrib/lldp.uts
@@ -25,6 +25,16 @@ frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/ \
 frm = frm.build()
 frm = Ether(frm)
 
+= build: check length calculation (#GH3107)
+
+frame = Ether(src='aa:bb:cc:dd:ee:ff', dst='11:22:33:44:55:66') / \
+        LLDPDUChassisID(subtype=0x04, id='aa:bb:cc:dd:ee:ff') / \
+        LLDPDUPortID(subtype=0x05, id='1') / \
+        LLDPDUTimeToLive(ttl=5) / \
+        LLDPDUManagementAddress(management_address_subtype=0x01, management_address=socket.inet_aton('192.168.0.10'))
+data = b'\x11"3DUf\xaa\xbb\xcc\xdd\xee\xff\x88\xcc\x02\x07\x04\xaa\xbb\xcc\xdd\xee\xff\x04\x02\x051\x06\x02\x00\x05\x10\x0c\x05\x01\xc0\xa8\x00\n\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+assert bytes(frame) == data
+
 = add padding if required
 
 conf.contribs['LLDP'].strict_mode_disable()