# SPDX-License-Identifier: GPL-2.0-or-later
# This file is part of Scapy
# See https://scapy.net/ for more information
# Copyright (C) 2018 Hao Zheng <haozheng10@gmail.com>

# scapy.contrib.description = Generic Network Virtualization Encapsulation (GENEVE)
# scapy.contrib.status = loads

"""
Geneve: Generic Network Virtualization Encapsulation

draft-ietf-nvo3-geneve-16
"""

import struct

from scapy.fields import BitField, XByteField, XShortEnumField, X3BytesField, StrLenField, PacketListField
from scapy.packet import Packet, bind_layers
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6
from scapy.layers.l2 import Ether, ETHER_TYPES
from scapy.compat import chb, orb

CLASS_IDS = {0x0100: "Linux",
             0x0101: "Open vSwitch",
             0x0102: "Open Virtual Networking (OVN)",
             0x0103: "In-band Network Telemetry (INT)",
             0x0104: "VMware",
             0x0105: "Amazon.com, Inc.",
             0x0106: "Cisco Systems, Inc.",
             0x0107: "Oracle Corporation",
             0x0110: "Amazon.com, Inc.",
             0x0118: "IBM",
             0x0128: "Ericsson",
             0xFEFF: "Unassigned",
             0xFFFF: "Experimental"}


class GeneveOptions(Packet):
    name = "Geneve Options"
    fields_desc = [XShortEnumField("classid", 0x0000, CLASS_IDS),
                   XByteField("type", 0x00),
                   BitField("reserved", 0, 3),
                   BitField("length", None, 5),
                   StrLenField('data', '', length_from=lambda x:x.length * 4)]

    def post_build(self, p, pay):
        if self.length is None:
            tmp_len = len(self.data) // 4
            p = p[:3] + struct.pack("!B", tmp_len) + p[4:]
        return p + pay


class GENEVE(Packet):
    name = "GENEVE"
    fields_desc = [BitField("version", 0, 2),
                   BitField("optionlen", None, 6),
                   BitField("oam", 0, 1),
                   BitField("critical", 0, 1),
                   BitField("reserved", 0, 6),
                   XShortEnumField("proto", 0x0000, ETHER_TYPES),
                   X3BytesField("vni", 0),
                   XByteField("reserved2", 0x00),
                   PacketListField("options", [], GeneveOptions, length_from=lambda pkt:pkt.optionlen * 4)]

    def post_build(self, p, pay):
        if self.optionlen is None:
            tmp_len = (len(p) - 8) // 4
            p = chb(tmp_len & 0x2f | orb(p[0]) & 0xc0) + p[1:]
        return p + pay

    def answers(self, other):
        if isinstance(other, GENEVE):
            if ((self.proto == other.proto) and (self.vni == other.vni)):
                return self.payload.answers(other.payload)
        else:
            return self.payload.answers(other)
        return 0

    def mysummary(self):
        return self.sprintf("GENEVE (vni=%GENEVE.vni%,"
                            "optionlen=%GENEVE.optionlen%,"
                            "proto=%GENEVE.proto%)")


bind_layers(UDP, GENEVE, dport=6081)
bind_layers(GENEVE, Ether, proto=0x6558)
bind_layers(GENEVE, IP, proto=0x0800)
bind_layers(GENEVE, IPv6, proto=0x86dd)

scapy/contrib/geneve.py
# SPDX-License-Identifier: GPL-2.0-or-later
# This file is part of Scapy
# See https://scapy.net/ for more information
# Copyright (C) 2018 Hao Zheng <haozheng10@gmail.com>

# scapy.contrib.description = Generic Network Virtualization Encapsulation (GENEVE)
# scapy.contrib.status = loads

"""
Geneve: Generic Network Virtualization Encapsulation

draft-ietf-nvo3-geneve-16
"""

import struct

from scapy.fields import BitField, XByteField, XShortEnumField, X3BytesField, StrLenField, PacketListField
from scapy.packet import Packet, bind_layers
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6
from scapy.layers.l2 import Ether, ETHER_TYPES
from scapy.compat import chb, orb

CLASS_IDS = {0x0100: "Linux",
             0x0101: "Open vSwitch",
             0x0102: "Open Virtual Networking (OVN)",
             0x0103: "In-band Network Telemetry (INT)",
             0x0104: "VMware",
             0x0105: "Amazon.com, Inc.",
             0x0106: "Cisco Systems, Inc.",
             0x0107: "Oracle Corporation",
             0x0110: "Amazon.com, Inc.",
             0x0118: "IBM",
             0x0128: "Ericsson",
             0xFEFF: "Unassigned",
             0xFFFF: "Experimental"}


class GeneveOptions(Packet):
    name = "Geneve Options"
    fields_desc = [XShortEnumField("classid", 0x0000, CLASS_IDS),
                   XByteField("type", 0x00),
                   BitField("reserved", 0, 3),
                   BitField("length", None, 5),
                   StrLenField('data', '', length_from=lambda x:x.length * 4)]

    def post_build(self, p, pay):
        if self.length is None:
            tmp_len = len(self.data) // 4
            p = p[:3] + struct.pack("!B", tmp_len) + p[4:]
        return p + pay


class GENEVE(Packet):
    name = "GENEVE"
    fields_desc = [BitField("version", 0, 2),
                   BitField("optionlen", None, 6),
                   BitField("oam", 0, 1),
                   BitField("critical", 0, 1),
                   BitField("reserved", 0, 6),
                   XShortEnumField("proto", 0x0000, ETHER_TYPES),
                   X3BytesField("vni", 0),
                   XByteField("reserved2", 0x00),
                   PacketListField("options", [], GeneveOptions, length_from=lambda pkt:pkt.optionlen * 4)]

    def post_build(self, p, pay):
        if self.optionlen is None:
            tmp_len = (len(p) - 8) // 4
            p = chb(tmp_len & 0x3f | orb(p[0]) & 0xc0) + p[1:]
        return p + pay

    def answers(self, other):
        if isinstance(other, GENEVE):
            if ((self.proto == other.proto) and (self.vni == other.vni)):
                return self.payload.answers(other.payload)
        else:
            return self.payload.answers(other)
        return 0

    def mysummary(self):
        return self.sprintf("GENEVE (vni=%GENEVE.vni%,"
                            "optionlen=%GENEVE.optionlen%,"
                            "proto=%GENEVE.proto%)")


bind_layers(UDP, GENEVE, dport=6081)
bind_layers(GENEVE, Ether, proto=0x6558)
bind_layers(GENEVE, IP, proto=0x0800)
bind_layers(GENEVE, IPv6, proto=0x86dd)

scapy/contrib/geneve.py
diff --git a/scapy/contrib/geneve.py b/scapy/contrib/geneve.py
index 77ce847a5ed..60430fee1f8 100644
--- a/scapy/contrib/geneve.py
+++ b/scapy/contrib/geneve.py
@@ -66,7 +66,7 @@ class GENEVE(Packet):
     def post_build(self, p, pay):
         if self.optionlen is None:
             tmp_len = (len(p) - 8) // 4
-            p = chb(tmp_len & 0x2f | orb(p[0]) & 0xc0) + p[1:]
+            p = chb(tmp_len & 0x3f | orb(p[0]) & 0xc0) + p[1:]
         return p + pay
 
     def answers(self, other):
diff --git a/test/contrib/geneve.uts b/test/contrib/geneve.uts
index 5811f56741b..e40533b3558 100644
--- a/test/contrib/geneve.uts
+++ b/test/contrib/geneve.uts
@@ -66,3 +66,9 @@ a = GENEVE(proto=0x0800)/b'E\x00\x00\x1c\x00\x01\x00\x00@\x01\xfa$\xc0\xa8\x00w\
 a = GENEVE(raw(a))
 assert a.summary() == 'GENEVE / IP / ICMP 192.168.0.119 > 172.217.18.195 echo-request 0'
 assert a.mysummary() in ['GENEVE (vni=0x0,optionlen=0,proto=0x800)', 'GENEVE (vni=0x0,optionlen=0,proto=IPv4)']
+
+= GENEVE - Optionlen
+
+data = raw(RandString(size=random.randint(0, pow(2, 6)) * 4))
+p = GENEVE(raw(GENEVE(options=GeneveOptions(data=data))))
+assert p[GENEVE].optionlen == (len(data) // 4 + 1)

[Bug] GENEVE.post_build() compute 'optionlen' incorrect
### Brief description

GENEVE.post_build() compute 'optionlen' incorrect.

I found that 'optionlen' always incorrect, when I implement class `GeneveOptINT` for (In-band Network Telemetry (INT)),.

Finally I found that `BitField("optionlen", None, 6)` the 'optionlen' is a 6bits field,
The tmp_len mask should be 0x3f instead of 0x2f.

1.Current Implement:
```Python
class GENEVE(Packet):
    def post_build(self, p, pay):
        if self.optionlen is None:
            tmp_len = (len(p) - 8) // 4
            p = chb(tmp_len & 0x2f | orb(p[0]) & 0xc0) + p[1:]
        return p + pay
```

2.Fixed method:
```Python
class GENEVE(Packet):
    def post_build(self, p, pay):
        if self.optionlen is None:
            tmp_len = (len(p) - 8) // 4
            p = chb(tmp_len & 0x3f | orb(p[0]) & 0xc0) + p[1:]
        return p + pay
```


### Scapy version

2.5.0

### Python version

3.7

### Operating system

Linux 5.10.27-051027-generic

### Additional environment information

_No response_

### How to reproduce

```
hujf@4c4c2fcab3ca:/usr/lib/python3/dist-packages$ sudo scapy
>>> from scapy.contrib.geneve import *
>>> data_str='1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff'
>>> GENEVE(raw(GENEVE(options=GeneveOptions(data=data_str))))[GENEVE].optionlen
1
```

### Actual result

```
hujf@4c4c2fcab3ca:/usr/lib/python3/dist-packages$ sudo scapy
>>> from scapy.contrib.geneve import *
>>> data_str='1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff'
>>> GENEVE(raw(GENEVE(options=GeneveOptions(data=data_str))))[GENEVE].optionlen
1
```

### Expected result

```
hujf@a7a04dccbbb8:/localdata/hujf/asic-cosim$ sudo scapy
>>> from scapy.contrib.geneve import *
>>> data_str='1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff'
>>> GENEVE(raw(GENEVE(options=GeneveOptions(data=data_str))))[GENEVE].optionlen
17
>>> 
```

### Related resources

_No response_
