Repository: secdev/scapy

Issue:
PPPoED miscalculated Padding
### Brief description

If bytes 4 and 5 of the PPPoED payload are 0, the whole payload is considered `Padding`.

Most of the time those bytes will contain a `PPPoED_Tag.tag_type`, e.g `0x0103`. With either vendor-specific tags or the end-of-list tag they can be 0. (The end-of-list tag is deprecated but convenient to illustrate the issue.)

I believe `PPPoED.extract_padding` added in 65eac3369b3673f70295b888d793577a82b936ce
https://github.com/secdev/scapy/blob/139966f37ee4188c676b958260312b1e4ad81be2/scapy/layers/ppp.py#L80-L84
is meant to calculate the padding from `PPPoED.len` (bytes 4 and 5 in the PPPoED packet), but uses the payload bytes instead.

### Scapy version

65eac3369b3673f70295b888d793577a82b936ce

### Python version

any

### Operating system

any

### Additional environment information

_No response_

### How to reproduce

```python
from scapy.all import *

eth = Ether(dst="ff:ff:ff:ff:ff:ff", src="12:12:12:12:12:12", type=0x8863)
pppoed = PPPoED(version=1, type=1, code=0x9, sessionid=0, len=8)
server_name = PPPoETag(tag_type=0x0101, tag_len=0)
end_of_list = PPPoETag(tag_type=0, tag_len=0)

original = eth / pppoed / server_name / end_of_list
dissected = Ether(original.build())

print(f"original:  {original.summary()}")
print(f"dissected: {dissected.summary()}")
```

### Actual result

Example output for v2.5.0rc2:
```
original:  Ether / PPPoE Active Discovery Initiation (PADI) / PPPoETag / PPPoETag
dissected: Ether / PPPoE Active Discovery Initiation (PADI) / Padding
```
Tags are considered `Padding`.

### Expected result

Example output for v2.4.4:
```
original:  12:12:12:12:12:12 > ff:ff:ff:ff:ff:ff (PPP_DISC) / PPPoED / PPPoETag / PPPoETag
dissected: 12:12:12:12:12:12 > ff:ff:ff:ff:ff:ff (PPP_DISC) / PPPoED / PPPoED_Tags
```
Tags are correctly identified as `PPPoED_Tags`

### Related resources

https://www.rfc-editor.org/rfc/rfc2516


Discussion:

Resolving patch:
diff --git a/scapy/layers/ppp.py b/scapy/layers/ppp.py
--- a/scapy/layers/ppp.py
+++ b/scapy/layers/ppp.py
@@ -78,10 +78,7 @@ class PPPoED(PPPoE):
                    ShortField("len", None)]
 
     def extract_padding(self, s):
-        if len(s) < 5:
-            return s, None
-        length = struct.unpack("!H", s[4:6])[0]
-        return s[:length], s[length:]
+        return s[:self.len], s[self.len:]
 
     def mysummary(self):
         return self.sprintf("%code%")


Test patch:
diff --git a/test/scapy/layers/ppp.uts b/test/scapy/layers/ppp.uts
--- a/test/scapy/layers/ppp.uts
+++ b/test/scapy/layers/ppp.uts
@@ -17,11 +17,26 @@ assert PPPoED_Tags in p
 q=p[PPPoED_Tags]
 assert q.tag_list is not None
 r=q.tag_list
+assert len(r) == 2
 assert r[0].tag_type==0x0101
 assert r[1].tag_type==0x0103
 assert r[1].tag_len==4
 assert r[1].tag_value==b'\x01\x02\x03\x04'
-assert r[2].tag_type==0x0000
+
+assert Padding in p and len(p[Padding]) == 4
+
+= PPPoE with tags (appended)
+~ ppp ppoe
+eth = Ether(dst="ff:ff:ff:ff:ff:ff", src="12:12:12:12:12:12", type=0x8863)
+pppoed = PPPoED(version=1, type=1, code=0x9, sessionid=0, len=8)
+server_name = PPPoETag(tag_type=0x0101, tag_len=0)
+end_of_list = PPPoETag(tag_type=0, tag_len=0)
+
+original = eth / pppoed / server_name / end_of_list
+dissected = Ether(original.build())
+assert PPPoED_Tags in dissected
+assert dissected[PPPoED_Tags].tag_list[0].tag_type == 0x0101
+assert dissected[PPPoED_Tags].tag_list[1].tag_type == 0
 
 =  PPPoE with padding
 ~ ppp pppoe