Repository: secdev/scapy

Issue:
SOME/IP-SD Entry-Array is broken by building it from RAW
### Environment
- Scapy version: 2.4.3.dev172
- Python version: 3.7.5
- Operating Systen: Debian Unstable (Linux xxx 5.3.0-2-amd64 #1 SMP Debian 5.3.9-3 (2019-11-19) x86_64 GNU/Linux)

### How to reproduce

```
from scapy.all import *
load_contrib("automotive.someip_sd")

ea1 = SDEntry_Service()
ea1.type = 1
ea1.srv_id = 0x1234
ea1.inst_id = 0x5678
ea1.ttl = 3

ea2 = SDEntry_Service()
ea2.type = 0
ea2.srv_id = 0x8765
ea2.inst_id = 0x4321
ea2.ttl = 2

sd1 = SD()
sd1.set_entryArray([ea1])

sd2 = SD()
sd2.set_entryArray([ea2,ea1])

print("Expected:")
sd1.show()

print("From RAW:")
SD(sd1.build()).show()

print("_SDEntry type gets the type of the first Entry. All entrys get type 0")
print("---------------------------------------------------------------------")
print("Expected:")
sd2.show()
print("From RAW:")
SD(sd2.build()).show()

print("Other order of Entrys:")
print("Expected:")
sd2.set_entryArray([ea1,ea2])
sd2.show()
print("From RAW:")
SD(sd2.build()).show()
```
### Result
```
Expected:
###[ SD ]### 
  flags     = 0
  res       = 0x0
  len_entry_array= None
  \entry_array\
   |###[ Service Entry ]### 
   |  type      = 1
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 4660
   |  inst_id   = 22136
   |  major_ver = 0
   |  ttl       = 0x3
   |  minor_ver = 0
  len_option_array= None
  \option_array\

From RAW:
###[ SD ]### 
  flags     = 0
  res       = 0x0
  len_entry_array= 16
  \entry_array\
   |###[ _SDEntry ]### 
   |  type      = 1
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 4660
   |  inst_id   = 22136
   |  major_ver = 0
   |  ttl       = 0x3
   |###[ Service Entry ]### 
   |     type      = 0
   |     index_1   = 0
   |     index_2   = 0
   |     n_opt_1   = 0
   |     n_opt_2   = 0
   |     srv_id    = 0
   |     inst_id   = 0
   |     major_ver = 0
   |     ttl       = 0x0
   |     minor_ver = 0
  len_option_array= 0
  \option_array\

_SDEntry type gets the type of the first Entry. All entrys get type 0
---------------------------------------------------------------------
Expected:
###[ SD ]### 
  flags     = 0
  res       = 0x0
  len_entry_array= None
  \entry_array\
   |###[ Service Entry ]### 
   |  type      = 0
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 34661
   |  inst_id   = 17185
   |  major_ver = 0
   |  ttl       = 0x2
   |  minor_ver = 0
   |###[ Service Entry ]### 
   |  type      = 1
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 4660
   |  inst_id   = 22136
   |  major_ver = 0
   |  ttl       = 0x3
   |  minor_ver = 0
  len_option_array= None
  \option_array\

From RAW:
###[ SD ]### 
  flags     = 0
  res       = 0x0
  len_entry_array= 32
  \entry_array\
   |###[ _SDEntry ]### 
   |  type      = 0
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 34661
   |  inst_id   = 17185
   |  major_ver = 0
   |  ttl       = 0x2
   |###[ Service Entry ]### 
   |     type      = 0
   |     index_1   = 0
   |     index_2   = 0
   |     n_opt_1   = 0
   |     n_opt_2   = 0
   |     srv_id    = 256
   |     inst_id   = 0
   |     major_ver = 18
   |     ttl       = 0x345678
   |     minor_ver = 3
   |###[ Service Entry ]### 
   |        type      = 0
   |        index_1   = 0
   |        index_2   = 0
   |        n_opt_1   = 0
   |        n_opt_2   = 0
   |        srv_id    = 0
   |        inst_id   = 0
   |        major_ver = 0
   |        ttl       = 0x0
   |        minor_ver = 0
  len_option_array= 0
  \option_array\

Other order of Entrys:
Expected:
###[ SD ]### 
  flags     = 0
  res       = 0x0
  len_entry_array= None
  \entry_array\
   |###[ Service Entry ]### 
   |  type      = 1
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 4660
   |  inst_id   = 22136
   |  major_ver = 0
   |  ttl       = 0x3
   |  minor_ver = 0
   |###[ Service Entry ]### 
   |  type      = 0
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 34661
   |  inst_id   = 17185
   |  major_ver = 0
   |  ttl       = 0x2
   |  minor_ver = 0
  len_option_array= None
  \option_array\

From RAW:
###[ SD ]### 
  flags     = 0
  res       = 0x0
  len_entry_array= 32
  \entry_array\
   |###[ _SDEntry ]### 
   |  type      = 1
   |  index_1   = 0
   |  index_2   = 0
   |  n_opt_1   = 0
   |  n_opt_2   = 0
   |  srv_id    = 4660
   |  inst_id   = 22136
   |  major_ver = 0
   |  ttl       = 0x3
   |###[ Service Entry ]### 
   |     type      = 0
   |     index_1   = 0
   |     index_2   = 0
   |     n_opt_1   = 0
   |     n_opt_2   = 0
   |     srv_id    = 0
   |     inst_id   = 0
   |     major_ver = 135
   |     ttl       = 0x654321
   |     minor_ver = 2
   |###[ Service Entry ]### 
   |        type      = 0
   |        index_1   = 0
   |        index_2   = 0
   |        n_opt_1   = 0
   |        n_opt_2   = 0
   |        srv_id    = 0
   |        inst_id   = 0
   |        major_ver = 0
   |        ttl       = 0x0
   |        minor_ver = 0
  len_option_array= 0
  \option_array\
```
### Description
The first two outputs show the _SDEntry layer is added to the package. The others show that the type of each entry is always set to 0, while _SDEntry takes the type of the first entry.

### Related resources
[AUTOSAR_PRS_SOMEIPServiceDiscoveryProtocol.pdf](https://www.autosar.org/fileadmin/user_upload/standards/foundation/1-2/AUTOSAR_PRS_SOMEIPServiceDiscoveryProtocol.pdf) - 4.1.2.3  Entry Format

@baarse @polybassa 


Discussion:
Thanks for this issue. We will look into it. Do you have a PR to fix this?
Not yet.
Works for me for now by using this patch. But EventGroups no longer Works.

```
diff -u someip_sd_orig.py someip_sd.py 
--- someip_sd_orig.py	2019-11-27 18:54:34.541247352 +0100
+++ someip_sd.py	2019-11-27 18:54:42.565233818 +0100
@@ -300,7 +300,7 @@
         X3BytesField("res", 0),
         FieldLenField("len_entry_array", None,
                       length_of="entry_array", fmt="!I"),
-        PacketListField("entry_array", None, cls=_SDEntry,
+        PacketListField("entry_array", None, cls=SDEntry_Service,
                         length_from=lambda pkt: pkt.len_entry_array),
         FieldLenField("len_option_array", None,
                       length_of="option_array", fmt="!I"),
```

Resolving patch:
diff --git a/scapy/contrib/automotive/someip.py b/scapy/contrib/automotive/someip.py
--- a/scapy/contrib/automotive/someip.py
+++ b/scapy/contrib/automotive/someip.py
@@ -39,9 +39,9 @@
 from scapy.modules.six.moves import range
 from scapy.packet import Packet, Raw, bind_top_down, bind_bottom_up
 from scapy.fields import XShortField, BitEnumField, ConditionalField, \
-    BitField, XBitField, IntField, XByteField, ByteEnumField, ByteField, \
-    ShortField, X3BytesField, StrField, IPField, FieldLenField, \
-    PacketListField
+    BitField, XBitField, IntField, XByteField, ByteEnumField, \
+    ShortField, X3BytesField, StrLenField, IPField, FieldLenField, \
+    PacketListField, XIntField
 
 
 class SOMEIP(Packet):
@@ -210,226 +210,223 @@ def _bind_someip_layers():
 
 class _SDPacketBase(Packet):
     """ base class to be used among all SD Packet definitions."""
-    # use this dictionary to set default values for desired fields (mostly on
-    # subclasses where not all fields are defined locally)
-    # - key : field_name, value : desired value
-    # - it will be used from 'init_fields' function, upon packet initialization
-    #
-    # example : _defaults =
-    #  {'field_1_name':field_1_value,'field_2_name':field_2_value}
-    _defaults = {}
-
-    def _set_defaults(self):
-        for key in self._defaults:
-            try:
-                self.get_field(key)
-            except KeyError:
-                pass
-            else:
-                self.setfieldval(key, self._defaults[key])
+    def extract_padding(self, s):
+        return "", s
+
+
+SDENTRY_TYPE_SRV_FINDSERVICE = 0x00
+SDENTRY_TYPE_SRV_OFFERSERVICE = 0x01
+SDENTRY_TYPE_SRV = (SDENTRY_TYPE_SRV_FINDSERVICE,
+                    SDENTRY_TYPE_SRV_OFFERSERVICE)
+SDENTRY_TYPE_EVTGRP_SUBSCRIBE = 0x06
+SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK = 0x07
+SDENTRY_TYPE_EVTGRP = (SDENTRY_TYPE_EVTGRP_SUBSCRIBE,
+                       SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK)
+SDENTRY_OVERALL_LEN = 16
+
+
+def _MAKE_SDENTRY_COMMON_FIELDS_DESC(type):
+    return [
+        XByteField("type", type),
+        XByteField("index_1", 0),
+        XByteField("index_2", 0),
+        XBitField("n_opt_1", 0, 4),
+        XBitField("n_opt_2", 0, 4),
+        XShortField("srv_id", 0),
+        XShortField("inst_id", 0),
+        XByteField("major_ver", 0),
+        X3BytesField("ttl", 0)
+    ]
 
-    def init_fields(self):
-        super(_SDPacketBase, self).init_fields()
-        self._set_defaults()
 
+class SDEntry_Service(_SDPacketBase):
+    name = "Service Entry"
+    fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC(
+        SDENTRY_TYPE_SRV_FINDSERVICE)
+    fields_desc += [
+        XIntField("minor_ver", 0)
+    ]
 
-# SD ENTRY
-# - Service
-# - EventGroup
-class _SDEntry(_SDPacketBase):
-    TYPE_FMT = ">B"
-    TYPE_PAYLOAD_I = 0
-    TYPE_SRV_FINDSERVICE = 0x00
-    TYPE_SRV_OFFERSERVICE = 0x01
-    TYPE_SRV = (TYPE_SRV_FINDSERVICE, TYPE_SRV_OFFERSERVICE)
-    TYPE_EVTGRP_SUBSCRIBE = 0x06
-    TYPE_EVTGRP_SUBSCRIBE_ACK = 0x07
-    TYPE_EVTGRP = (TYPE_EVTGRP_SUBSCRIBE, TYPE_EVTGRP_SUBSCRIBE_ACK)
-    OVERALL_LEN = 16
 
-    fields_desc = [
-        ByteField("type", 0),
-        ByteField("index_1", 0),
-        ByteField("index_2", 0),
-        BitField("n_opt_1", 0, 4),
-        BitField("n_opt_2", 0, 4),
-        ShortField("srv_id", 0),
-        ShortField("inst_id", 0),
-        ByteField("major_ver", 0),
-        X3BytesField("ttl", 0)
+class SDEntry_EventGroup(_SDPacketBase):
+    name = "Eventgroup Entry"
+    fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC(
+        SDENTRY_TYPE_EVTGRP_SUBSCRIBE)
+    fields_desc += [
+        XBitField("res", 0, 12),
+        XBitField("cnt", 0, 4),
+        XShortField("eventgroup_id", 0)
     ]
 
-    def guess_payload_class(self, payload):
-        pl_type = orb(payload[_SDEntry.TYPE_PAYLOAD_I])
 
-        if pl_type in _SDEntry.TYPE_SRV:
-            return SDEntry_Service
-        elif pl_type in _SDEntry.TYPE_EVTGRP:
-            return SDEntry_EventGroup
+def _sdentry_class(payload, **kargs):
+    TYPE_PAYLOAD_I = 0
+    pl_type = orb(payload[TYPE_PAYLOAD_I])
+    cls = None
 
+    if pl_type in SDENTRY_TYPE_SRV:
+        cls = SDEntry_Service
+    elif pl_type in SDENTRY_TYPE_EVTGRP:
+        cls = SDEntry_EventGroup
 
-class SDEntry_Service(_SDEntry):
-    _defaults = {"type": _SDEntry.TYPE_SRV_FINDSERVICE}
+    return cls(payload, **kargs)
 
-    name = "Service Entry"
-    fields_desc = [
-        _SDEntry,
-        IntField("minor_ver", 0)
-    ]
 
+def _sdoption_class(payload, **kargs):
+    pl_type = orb(payload[2])
 
-class SDEntry_EventGroup(_SDEntry):
-    _defaults = {"type": _SDEntry.TYPE_EVTGRP_SUBSCRIBE}
+    cls = {
+        SDOPTION_CFG_TYPE: SDOption_Config,
+        SDOPTION_LOADBALANCE_TYPE: SDOption_LoadBalance,
+        SDOPTION_IP4_ENDPOINT_TYPE: SDOption_IP4_EndPoint,
+        SDOPTION_IP4_MCAST_TYPE: SDOption_IP4_Multicast,
+        SDOPTION_IP4_SDENDPOINT_TYPE: SDOption_IP4_SD_EndPoint,
+        SDOPTION_IP6_ENDPOINT_TYPE: SDOption_IP6_EndPoint,
+        SDOPTION_IP6_MCAST_TYPE: SDOption_IP6_Multicast,
+        SDOPTION_IP6_SDENDPOINT_TYPE: SDOption_IP6_SD_EndPoint
+    }.get(pl_type, Raw)
 
-    name = "Eventgroup Entry"
-    fields_desc = [
-        _SDEntry,
-        BitField("res", 0, 12),
-        BitField("cnt", 0, 4),
-        ShortField("eventgroup_id", 0)
-    ]
+    return cls(payload, **kargs)
 
 
 # SD Option
-# - Configuration
-# - LoadBalancing
-# - IPv4 EndPoint
-# - IPv6 EndPoint
-# - IPv4 MultiCast
-# - IPv6 MultiCast
-# - IPv4 EndPoint
-# - IPv6 EndPoint
-class _SDOption(_SDPacketBase):
-    CFG_TYPE = 0x01
-    CFG_OVERALL_LEN = 4
-    LOADBALANCE_TYPE = 0x02
-    LOADBALANCE_LEN = 0x05
-    LOADBALANCE_OVERALL_LEN = 8
-    IP4_ENDPOINT_TYPE = 0x04
-    IP4_ENDPOINT_LEN = 0x0009
-    IP4_MCAST_TYPE = 0x14
-    IP4_MCAST_LEN = 0x0009
-    IP4_SDENDPOINT_TYPE = 0x24
-    IP4_SDENDPOINT_LEN = 0x0009
-    IP4_OVERALL_LEN = 12
-    IP6_ENDPOINT_TYPE = 0x06
-    IP6_ENDPOINT_LEN = 0x0015
-    IP6_MCAST_TYPE = 0x16
-    IP6_MCAST_LEN = 0x0015
-    IP6_SDENDPOINT_TYPE = 0x26
-    IP6_SDENDPOINT_LEN = 0x0015
-    IP6_OVERALL_LEN = 24
-
-    def guess_payload_class(self, payload):
-        pl_type = orb(payload[2])
-
-        return {
-            _SDOption.CFG_TYPE: SDOption_Config,
-            self.LOADBALANCE_TYPE: SDOption_LoadBalance,
-            self.IP4_ENDPOINT_TYPE: SDOption_IP4_EndPoint,
-            self.IP4_MCAST_TYPE: SDOption_IP4_Multicast,
-            self.IP4_SDENDPOINT_TYPE: SDOption_IP4_SD_EndPoint,
-            self.IP6_ENDPOINT_TYPE: SDOption_IP6_EndPoint,
-            self.IP6_MCAST_TYPE: SDOption_IP6_Multicast,
-            self.IP6_SDENDPOINT_TYPE: SDOption_IP6_SD_EndPoint
-        }.get(pl_type, Raw)
-
-
-class _SDOption_Header(_SDOption):
-    fields_desc = [
-        ShortField("len", None),
-        ByteField("type", 0),
-        ByteField("res_hdr", 0)
+SDOPTION_CFG_TYPE = 0x01
+SDOPTION_LOADBALANCE_TYPE = 0x02
+SDOPTION_LOADBALANCE_LEN = 0x05
+SDOPTION_IP4_ENDPOINT_TYPE = 0x04
+SDOPTION_IP4_ENDPOINT_LEN = 0x0009
+SDOPTION_IP4_MCAST_TYPE = 0x14
+SDOPTION_IP4_MCAST_LEN = 0x0009
+SDOPTION_IP4_SDENDPOINT_TYPE = 0x24
+SDOPTION_IP4_SDENDPOINT_LEN = 0x0009
+SDOPTION_IP6_ENDPOINT_TYPE = 0x06
+SDOPTION_IP6_ENDPOINT_LEN = 0x0015
+SDOPTION_IP6_MCAST_TYPE = 0x16
+SDOPTION_IP6_MCAST_LEN = 0x0015
+SDOPTION_IP6_SDENDPOINT_TYPE = 0x26
+SDOPTION_IP6_SDENDPOINT_LEN = 0x0015
+
+
+def _MAKE_COMMON_SDOPTION_FIELDS_DESC(type, length=None):
+    return [
+        ShortField("len", length),
+        XByteField("type", type),
+        XByteField("res_hdr", 0)
     ]
 
 
-class _SDOption_Tail(_SDOption):
-    fields_desc = [
-        ByteField("res_tail", 0),
-        ByteEnumField("l4_proto", 0x06, {0x06: "TCP", 0x11: "UDP"}),
+def _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC():
+    return [
+        XByteField("res_tail", 0),
+        ByteEnumField("l4_proto", 0x11, {0x06: "TCP", 0x11: "UDP"}),
         ShortField("port", 0)
     ]
 
 
-class _SDOption_IP4(_SDOption):
-    fields_desc = [
-        _SDOption_Header,
-        IPField("addr", "0.0.0.0"),
-        _SDOption_Tail
-    ]
-
-
-class _SDOption_IP6(_SDOption):
-    fields_desc = [
-        _SDOption_Header,
-        IP6Field("addr", "2001:cdba:0000:0000:0000:0000:3257:9652"),
-        _SDOption_Tail
-    ]
-
-
-class SDOption_Config(_SDOption):
-    LEN_OFFSET = 0x01
-
+class SDOption_Config(_SDPacketBase):
     name = "Config Option"
-    _defaults = {'type': _SDOption.CFG_TYPE}
-    fields_desc = [
-        _SDOption_Header,
-        StrField("cfg_str", "")
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(SDOPTION_CFG_TYPE) + [
+        StrLenField("cfg_str", "\x00", length_from=lambda pkt: pkt.len - 1)
     ]
 
     def post_build(self, pkt, pay):
-        length = self.len
-        if length is None:
-            length = len(self.cfg_str) + self.LEN_OFFSET
+        if self.len is None:
+            length = len(self.cfg_str) + 1  # res_hdr field takes 1 byte
             pkt = struct.pack("!H", length) + pkt[2:]
         return pkt + pay
 
-
-class SDOption_LoadBalance(_SDOption):
+    @staticmethod
+    def make_string(data):
+        # Build a valid null-terminated configuration string from a dict or a
+        # list with key-value pairs.
+        #
+        # Example:
+        #    >>> SDOption_Config.make_string({ "hello": "world" })
+        #    b'\x0bhello=world\x00'
+        #
+        #    >>> SDOption_Config.make_string([
+        #    ...     ("x", "y"),
+        #    ...     ("abc", "def"),
+        #    ...     ("123", "456")
+        #    ... ])
+        #    b'\x03x=y\x07abc=def\x07123=456\x00'
+
+        if isinstance(data, dict):
+            data = data.items()
+
+        # combine entries
+        data = ("{}={}".format(k, v) for k, v in data)
+        # prepend length
+        data = ("{}{}".format(chr(len(v)), v) for v in data)
+        # concatenate
+        data = "".join(data)
+        data += "\x00"
+
+        return data.encode("utf8")
+
+
+class SDOption_LoadBalance(_SDPacketBase):
     name = "LoadBalance Option"
-    _defaults = {'type': _SDOption.LOADBALANCE_TYPE,
-                 'len': _SDOption.LOADBALANCE_LEN}
-    fields_desc = [
-        _SDOption_Header,
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
+        SDOPTION_LOADBALANCE_TYPE, SDOPTION_LOADBALANCE_LEN)
+    fields_desc += [
         ShortField("priority", 0),
         ShortField("weight", 0)
     ]
 
 
-class SDOption_IP4_EndPoint(_SDOption_IP4):
+class SDOption_IP4_EndPoint(_SDPacketBase):
     name = "IP4 EndPoint Option"
-    _defaults = {'type': _SDOption.IP4_ENDPOINT_TYPE,
-                 'len': _SDOption.IP4_ENDPOINT_LEN}
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
+        SDOPTION_IP4_ENDPOINT_TYPE, SDOPTION_IP4_ENDPOINT_LEN)
+    fields_desc += [
+        IPField("addr", "0.0.0.0"),
+    ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
 
 
-class SDOption_IP4_Multicast(_SDOption_IP4):
+class SDOption_IP4_Multicast(_SDPacketBase):
     name = "IP4 Multicast Option"
-    _defaults = {'type': _SDOption.IP4_MCAST_TYPE,
-                 'len': _SDOption.IP4_MCAST_LEN}
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
+        SDOPTION_IP4_MCAST_TYPE, SDOPTION_IP4_MCAST_LEN)
+    fields_desc += [
+        IPField("addr", "0.0.0.0"),
+    ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
 
 
-class SDOption_IP4_SD_EndPoint(_SDOption_IP4):
+class SDOption_IP4_SD_EndPoint(_SDPacketBase):
     name = "IP4 SDEndPoint Option"
-    _defaults = {'type': _SDOption.IP4_SDENDPOINT_TYPE,
-                 'len': _SDOption.IP4_SDENDPOINT_LEN}
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
+        SDOPTION_IP4_SDENDPOINT_TYPE, SDOPTION_IP4_SDENDPOINT_LEN)
+    fields_desc += [
+        IPField("addr", "0.0.0.0"),
+    ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
 
 
-class SDOption_IP6_EndPoint(_SDOption_IP6):
+class SDOption_IP6_EndPoint(_SDPacketBase):
     name = "IP6 EndPoint Option"
-    _defaults = {'type': _SDOption.IP6_ENDPOINT_TYPE,
-                 'len': _SDOption.IP6_ENDPOINT_LEN}
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
+        SDOPTION_IP6_ENDPOINT_TYPE, SDOPTION_IP6_ENDPOINT_LEN)
+    fields_desc += [
+        IP6Field("addr", "::"),
+    ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
 
 
-class SDOption_IP6_Multicast(_SDOption_IP6):
+class SDOption_IP6_Multicast(_SDPacketBase):
     name = "IP6 Multicast Option"
-    _defaults = {'type': _SDOption.IP6_MCAST_TYPE,
-                 'len': _SDOption.IP6_MCAST_LEN}
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
+        SDOPTION_IP6_MCAST_TYPE, SDOPTION_IP6_MCAST_LEN)
+    fields_desc += [
+        IP6Field("addr", "::"),
+    ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
 
 
-class SDOption_IP6_SD_EndPoint(_SDOption_IP6):
+class SDOption_IP6_SD_EndPoint(_SDPacketBase):
     name = "IP6 SDEndPoint Option"
-    _defaults = {'type': _SDOption.IP6_SDENDPOINT_TYPE,
-                 'len': _SDOption.IP6_SDENDPOINT_LEN}
+    fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
+        SDOPTION_IP6_SDENDPOINT_TYPE, SDOPTION_IP6_SDENDPOINT_LEN)
+    fields_desc += [
+        IP6Field("addr", "::"),
+    ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
 
 
 ##
@@ -462,15 +459,15 @@ class SD(_SDPacketBase):
 
     name = "SD"
     fields_desc = [
-        ByteField("flags", 0),
+        XByteField("flags", 0),
         X3BytesField("res", 0),
         FieldLenField("len_entry_array", None,
                       length_of="entry_array", fmt="!I"),
-        PacketListField("entry_array", None, cls=_SDEntry,
+        PacketListField("entry_array", None, cls=_sdentry_class,
                         length_from=lambda pkt: pkt.len_entry_array),
         FieldLenField("len_option_array", None,
                       length_of="option_array", fmt="!I"),
-        PacketListField("option_array", None, cls=_SDOption,
+        PacketListField("option_array", None, cls=_sdoption_class,
                         length_from=lambda pkt: pkt.len_option_array)
     ]
 
@@ -516,7 +513,6 @@ def set_optionArray(self, option_list):
 bind_bottom_up(SOMEIP, SD,
                srv_id=SD.SOMEIP_MSGID_SRVID,
                sub_id=SD.SOMEIP_MSGID_SUBID,
-               client_id=SD.SOMEIP_CLIENT_ID,
                event_id=SD.SOMEIP_MSGID_EVENTID,
                proto_ver=SD.SOMEIP_PROTO_VER,
                iface_ver=SD.SOMEIP_IFACE_VER,


Test patch:
diff --git a/test/contrib/automotive/someip.uts b/test/contrib/automotive/someip.uts
--- a/test/contrib/automotive/someip.uts
+++ b/test/contrib/automotive/someip.uts
@@ -134,34 +134,29 @@ assert(f[1].more_seg == 0)
 
 = Check packet length on empty build
 p = SDEntry_Service()
-assert(len(bytes(p)) == SDEntry_Service.OVERALL_LEN)
+assert(len(bytes(p)) == SDENTRY_OVERALL_LEN)
 
 = Build 1
-p = SDEntry_Service(type = SDEntry_Service.TYPE_SRV_OFFERSERVICE,
+p = SDEntry_Service(type = SDENTRY_TYPE_SRV_OFFERSERVICE,
 				    index_1 = 0x11, index_2 = 0x22, srv_id = 0x3333,
 					inst_id = 0x4444, major_ver = 0x55,
 					ttl = 0x666666, minor_ver = 0xdeadbeef)
 p_str = bytes(p)
 bin_str = b"\x01\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\xde\xad\xbe\xef"
 assert(p_str == bin_str)
-assert(len(p_str) == SDEntry_Service.OVERALL_LEN)
+assert(len(p_str) == SDENTRY_OVERALL_LEN)
 
 = Build 2
 p = SDEntry_Service(n_opt_1 = 0xf1, n_opt_2 = 0xf2)
 p_str = bytes(p)
 bin_str = b"\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
 assert(p_str == bin_str)
-assert(len(p_str) == SDEntry_Service.OVERALL_LEN)
-
-= Check payload guess
-p_entry_srv = SDEntry_Service()
-assert(SDEntry_Service().guess_payload_class(bytes(p_entry_srv)) ==
-       SDEntry_Service)
+assert(len(p_str) == SDENTRY_OVERALL_LEN)
 
 = Dissect
 p = SDEntry_Service(
     b"\x01\x22\x33\x00\x44\x44\x55\x55\x66\x77\x77\x77\xde\xad\xbe\xef")
-assert(p.type == SDEntry_Service.TYPE_SRV_OFFERSERVICE)
+assert(p.type == SDENTRY_TYPE_SRV_OFFERSERVICE)
 assert(p.index_1 == 0x22)
 assert(p.index_2 == 0x33)
 assert(p.srv_id == 0x4444)
@@ -174,7 +169,7 @@ assert(p.minor_ver == 0xdeadbeef)
 
 = Check packet length on empty build
 p = SDEntry_EventGroup()
-assert(len(bytes(p)) == SDEntry_EventGroup.OVERALL_LEN)
+assert(len(bytes(p)) == SDENTRY_OVERALL_LEN)
 
 = Build
 p = SDEntry_EventGroup(index_1 = 0x11, index_2 = 0x22, srv_id = 0x3333,
@@ -183,30 +178,7 @@ p = SDEntry_EventGroup(index_1 = 0x11, index_2 = 0x22, srv_id = 0x3333,
 p_str = bytes(p)
 bin_str = b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88"
 assert(p_str == bin_str)
-assert(len(bytes(p)) == SDEntry_EventGroup.OVERALL_LEN)
-
-= Check payload guess
-p_entry_evtgrp = SDEntry_EventGroup()
-assert(SDEntry_EventGroup().guess_payload_class(
-    bytes(p_entry_evtgrp)) == SDEntry_EventGroup)
-
-+ SD Option Config
-
-= Check pkg type
-p = SDOption_Config()
-assert(p.type == 0x01)
-
-= Check length without payload
-assert(len(bytes(p)) == 4)
-
-= Check add payload and check length
-import binascii
-p.cfg_str = binascii.hexlify(b"5abc=x7def=1230")
-assert(bytes(p) == b"\x00\x1f\x01\x00" +
-       binascii.hexlify(b"5abc=x7def=1230"))
-
-= Check payload guess
-assert(SDOption_Config().guess_payload_class(bytes(p)) == SDOption_Config)
+assert(len(bytes(p)) == SDENTRY_OVERALL_LEN)
 
 = Dissect
 p = SDEntry_EventGroup(
@@ -220,122 +192,6 @@ assert(p.ttl == 0x666666)
 assert(p.cnt == 0x7)
 assert(p.eventgroup_id == 0x8888)
 
-+ SD Option Load Balance
-
-= Check pkg type & lengths(static and overall)
-p = SDOption_LoadBalance()
-assert(p.type == 0x02)
-assert(p.len == 0x05)
-assert(len(bytes(p)) == 8)
-
-= Check payload guess
-assert(SDOption_LoadBalance().guess_payload_class(
-    bytes(p)) == SDOption_LoadBalance)
-
-= Dissect
-p = SDOption_LoadBalance(b'\x00\x05\x02\x01\x00\x02\x00\x03')
-assert(p.type == 0x02)
-assert(p.len == 0x05)
-assert(p.res_hdr == 0x01)
-assert(p.priority == 0x02)
-assert(p.weight == 0x03)
-
-+ SD Option IP4 Endpoint
-
-= Check pkg type & length
-p = SDOption_IP4_EndPoint()
-assert(p.type == 0x04)
-assert(p.len == 0x0009)
-
-= Check payload guess
-assert(SDOption_IP4_EndPoint().guess_payload_class(
-    bytes(p)) == SDOption_IP4_EndPoint)
-
-= Dissect
-p = SDOption_IP4_EndPoint(b'\x00\x09\x04')
-assert(p.type == 0x04)
-assert(p.len == 0x0009)
-
-+ SD Option IP4 Multicast
-
-= Check pkg type & length
-p = SDOption_IP4_Multicast()
-assert(p.type == 0x14)
-assert(p.len == 0x0009)
-
-= Payload guess
-assert(SDOption_IP4_Multicast().guess_payload_class(
-    bytes(p)) == SDOption_IP4_Multicast)
-
-= Dissect
-p = SDOption_IP4_Multicast(b'\x00\x09\x14')
-assert(p.type == 0x14)
-assert(p.len == 0x0009)
-
-+ SD OPTION IP4 SD EndPoint
-
-= Check pkg type & length
-p = SDOption_IP4_SD_EndPoint()
-assert(p.type == 0x24)
-assert(p.len == 0x0009)
-
-= Check payload guess
-assert(SDOption_IP4_SD_EndPoint().guess_payload_class(
-    bytes(p)) == SDOption_IP4_SD_EndPoint)
-
-= Dissect
-p = SDOption_IP4_SD_EndPoint(b'\x00\x09\x24')
-assert(p.type == 0x24)
-assert(p.len == 0x0009)
-
-+ SD Option IP6 End Point
-
-= Check pkg type & length
-p = SDOption_IP6_EndPoint()
-assert(p.type == 0x06)
-assert(p.len == 0x0015)
-
-= Check payload guess
-assert(SDOption_IP6_EndPoint().guess_payload_class(
-    bytes(p)) == SDOption_IP6_EndPoint)
-
-= Dissect
-p = SDOption_IP6_EndPoint(b'\x00\x15\x06')
-assert(p.type == 0x06)
-assert(p.len == 0x0015)
-
-+ SD Option IP6 Multicast
-
-= Check pkg type & length
-p = SDOption_IP6_Multicast()
-assert(p.type == 0x16)
-assert(p.len == 0x0015)
-
-= Check payload guess
-assert(SDOption_IP6_Multicast().guess_payload_class(
-    bytes(p)) == SDOption_IP6_Multicast)
-
-= Dissect
-p = SDOption_IP6_Multicast(b'\x00\x15\x16')
-assert(p.type == 0x16)
-assert(p.len == 0x0015)
-
-+ SD OPTION IP6 SD EndPoint
-
-= Check pkg type & length
-p = SDOption_IP6_SD_EndPoint()
-assert(p.type == 0x26)
-assert(p.len == 0x015)
-
-= Check payload guess
-assert(SDOption_IP6_SD_EndPoint().guess_payload_class(
-    bytes(p)) == SDOption_IP6_SD_EndPoint)
-
-= Dissect
-p = SDOption_IP6_SD_EndPoint(b'\x00\x15\x26')
-assert(p.type == 0x26)
-assert(p.len == 0x0015)
-
 + SD Flags
 
 = Build and check flags
@@ -416,3 +272,452 @@ p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()])
 
 assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32)
 assert(struct.unpack("!L", bytes(p)[40:44])[0] == 24)
+
+
++ Git issue 2348: SOME/IP-SD Entry-Array is broken by building it from RAW
+
+= Single SD entry
+# offer service
+ea1 = SDEntry_Service()
+ea1.type = 1
+ea1.srv_id = 0x1234
+ea1.inst_id = 0x5678
+ea1.ttl = 0x333333
+
+# subscribe eventgroup
+ea2 = SDEntry_EventGroup()
+ea2.type = 0x6
+ea2.srv_id = 0x8765
+ea2.inst_id = 0x4321
+ea2.ttl = 0x222222
+ea2.eventgroup_id = 0x1357
+
+sd1 = SD()
+sd1.set_entryArray([ea1])
+# this is computed on build, but we need it sooner for the assert()
+sd1.len_entry_array = 16
+sd1.len_option_array = 0
+
+assert(sd1.show(dump=True) == SD(sd1.build()).show(dump=True))
+
+= Double SD entry
+sd2 = SD()
+sd2.set_entryArray([ea2,ea1])
+# this is computed on build, but we need it sooner for the assert()
+sd2.len_entry_array = 32
+sd2.len_option_array = 0
+
+assert(sd2.show(dump=True) == SD(sd2.build()).show(dump=True))
+
+= Flipped double SD entry
+# flip the order
+sd2.set_entryArray([ea1,ea2])
+assert(sd2.show(dump=True) == SD(sd2.build()).show(dump=True))
+
+
+
+
+
++ SD Options (individual)
+= Verifying constants against spec
+assert(SDOPTION_CFG_TYPE == 0x01)
+assert(SDOPTION_LOADBALANCE_TYPE == 0x02)
+assert(SDOPTION_LOADBALANCE_LEN == 0x05)
+assert(SDOPTION_IP4_ENDPOINT_TYPE == 0x04)
+assert(SDOPTION_IP4_ENDPOINT_LEN == 0x0009)
+assert(SDOPTION_IP4_MCAST_TYPE == 0x14)
+assert(SDOPTION_IP4_MCAST_LEN == 0x0009)
+assert(SDOPTION_IP4_SDENDPOINT_TYPE == 0x24)
+assert(SDOPTION_IP4_SDENDPOINT_LEN == 0x0009)
+assert(SDOPTION_IP6_ENDPOINT_TYPE == 0x06)
+assert(SDOPTION_IP6_ENDPOINT_LEN == 0x0015)
+assert(SDOPTION_IP6_MCAST_TYPE == 0x16)
+assert(SDOPTION_IP6_MCAST_LEN == 0x0015)
+assert(SDOPTION_IP6_SDENDPOINT_TYPE == 0x26)
+assert(SDOPTION_IP6_SDENDPOINT_LEN == 0x0015)
+
+### SDOption_Config
+= SDOption_Config: Verify make_string() method from dict
+data = { "hello": "world" }
+out = SDOption_Config.make_string(data)
+assert(out == b"\x0bhello=world\x00")
+
+= SDOption_Config: Verify make_string() method from list
+data = [
+	("x", "y"),
+	("abc", "def"),
+	("123", "456")
+]
+out = SDOption_Config.make_string(data)
+assert(out == b"\x03x=y\x07abc=def\x07123=456\x00")
+
+= SDOption_Config: Build and dissect empty
+opt = SDOption_Config()
+optraw = opt.build()
+assert(optraw == b"\x00\x02\x01\x00\x00")
+
+opt = SDOption_Config(optraw)
+assert(opt.len == 0x2)
+assert(opt.type == SDOPTION_CFG_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.cfg_str == b"\x00")
+
+= SDOption_Config: Build and dissect spec example
+tststr = b"\x05abc=x\x07def=123\x00"
+opt = SDOption_Config(cfg_str=tststr)
+optraw = opt.build()
+assert(optraw == b"\x00\x10\x01\x00" + tststr)
+
+opt = SDOption_Config(optraw)
+assert(opt.len == 0x10)
+assert(opt.type == SDOPTION_CFG_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.cfg_str == tststr)
+
+= SDOption_Config: Build and dissect fully populated
+tststr = b"abcdefghijklmnopqrstuvwxyz"
+opt = SDOption_Config(len=0x1234, type=0x56, res_hdr=0x78, cfg_str=tststr)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78" + tststr)
+
+opt = SDOption_Config(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.cfg_str == tststr)
+
+
+### SDOption_LoadBalance
+= SDOption_LoadBalance: Build and dissect empty
+opt = SDOption_LoadBalance()
+optraw = opt.build()
+assert(optraw == b"\x00\x05\x02\x00\x00\x00\x00\x00")
+
+opt = SDOption_LoadBalance(optraw)
+assert(opt.len == SDOPTION_LOADBALANCE_LEN)
+assert(opt.type == SDOPTION_LOADBALANCE_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.priority == 0x0)
+assert(opt.weight == 0x0)
+
+= SDOption_LoadBalance: Build and dissect example
+opt = SDOption_LoadBalance(priority=0x1234, weight=0x5678)
+optraw = opt.build()
+assert(optraw == b"\x00\x05\x02\x00\x12\x34\x56\x78")
+
+opt = SDOption_LoadBalance(optraw)
+assert(opt.len == SDOPTION_LOADBALANCE_LEN)
+assert(opt.type == SDOPTION_LOADBALANCE_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.priority == 0x1234)
+assert(opt.weight == 0x5678)
+
+= SDOption_LoadBalance: Build and dissect fully populated
+opt = SDOption_LoadBalance(len=0x1234, type=0x56, res_hdr=0x78, priority=0x9abc, weight=0xdef0)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78\x9a\xbc\xde\xf0")
+
+opt = SDOption_LoadBalance(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.priority == 0x9abc)
+assert(opt.weight == 0xdef0)
+
+
+### SDOption_IP4_EndPoint
+= SDOption_IP4_EndPoint: Build and dissect empty
+opt = SDOption_IP4_EndPoint()
+optraw = opt.build()
+assert(optraw == b"\x00\x09\x04\x00\x00\x00\x00\x00\x00\x11\x00\x00")
+
+opt = SDOption_IP4_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP4_ENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP4_ENDPOINT_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.addr == "0.0.0.0")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x11)
+assert(opt.port == 0x0)
+
+
+= SDOption_IP4_EndPoint: Build and dissect example
+opt = SDOption_IP4_EndPoint(addr = "192.168.123.45", l4_proto = "TCP", port = 0x1234)
+optraw = opt.build()
+assert(optraw == b"\x00\x09\x04\x00\xc0\xa8\x7b\x2d\x00\x06\x12\x34")
+
+opt = SDOption_IP4_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP4_ENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP4_ENDPOINT_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.addr == "192.168.123.45")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x06)
+assert(opt.port == 0x1234)
+
+= SDOption_IP4_EndPoint: Build and dissect fully populated
+opt = SDOption_IP4_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "11.22.33.44", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78\x0b\x16\x21\x2c\x9a\xbc\xde\xf0")
+
+opt = SDOption_IP4_EndPoint(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.addr == "11.22.33.44")
+assert(opt.res_tail == 0x9a)
+assert(opt.l4_proto == 0xbc)
+assert(opt.port == 0xdef0)
+
+
+### SDOption_IP4_Multicast
+= SDOption_IP4_Multicast: Build and dissect empty
+opt = SDOption_IP4_Multicast()
+optraw = opt.build()
+assert(optraw == b"\x00\x09\x14\x00\x00\x00\x00\x00\x00\x11\x00\x00")
+
+opt = SDOption_IP4_Multicast(optraw)
+assert(opt.len == SDOPTION_IP4_MCAST_LEN)
+assert(opt.type == SDOPTION_IP4_MCAST_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.addr == "0.0.0.0")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x11)
+assert(opt.port == 0x0)
+
+
+= SDOption_IP4_Multicast: Build and dissect example
+opt = SDOption_IP4_Multicast(addr = "192.168.123.45", l4_proto = "TCP", port = 0x1234)
+optraw = opt.build()
+assert(optraw == b"\x00\x09\x14\x00\xc0\xa8\x7b\x2d\x00\x06\x12\x34")
+
+opt = SDOption_IP4_Multicast(optraw)
+assert(opt.len == SDOPTION_IP4_MCAST_LEN)
+assert(opt.type == SDOPTION_IP4_MCAST_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.addr == "192.168.123.45")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x06)
+assert(opt.port == 0x1234)
+
+= SDOption_IP4_Multicast: Build and dissect fully populated
+opt = SDOption_IP4_Multicast(len=0x1234, type=0x56, res_hdr=0x78, addr = "11.22.33.44", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78\x0b\x16\x21\x2c\x9a\xbc\xde\xf0")
+
+opt = SDOption_IP4_Multicast(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.addr == "11.22.33.44")
+assert(opt.res_tail == 0x9a)
+assert(opt.l4_proto == 0xbc)
+assert(opt.port == 0xdef0)
+
+
+### SDOption_IP4_SD_EndPoint
+= SDOption_IP4_SD_EndPoint: Build and dissect empty
+opt = SDOption_IP4_SD_EndPoint()
+optraw = opt.build()
+assert(optraw == b"\x00\x09\x24\x00\x00\x00\x00\x00\x00\x11\x00\x00")
+
+opt = SDOption_IP4_SD_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP4_SDENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP4_SDENDPOINT_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.addr == "0.0.0.0")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x11)
+assert(opt.port == 0x0)
+
+
+= SDOption_IP4_SD_EndPoint: Build and dissect example
+opt = SDOption_IP4_SD_EndPoint(addr = "192.168.123.45", l4_proto = "TCP", port = 0x1234)
+optraw = opt.build()
+assert(optraw == b"\x00\x09\x24\x00\xc0\xa8\x7b\x2d\x00\x06\x12\x34")
+
+opt = SDOption_IP4_SD_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP4_SDENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP4_SDENDPOINT_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.addr == "192.168.123.45")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x06)
+assert(opt.port == 0x1234)
+
+= SDOption_IP4_SD_EndPoint: Build and dissect fully populated
+opt = SDOption_IP4_SD_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "11.22.33.44", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78\x0b\x16\x21\x2c\x9a\xbc\xde\xf0")
+
+opt = SDOption_IP4_SD_EndPoint(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.addr == "11.22.33.44")
+assert(opt.res_tail == 0x9a)
+assert(opt.l4_proto == 0xbc)
+assert(opt.port == 0xdef0)
+
+### SDOption_IP6_EndPoint
+= SDOption_IP6_EndPoint: Build and dissect empty
+opt = SDOption_IP6_EndPoint()
+optraw = opt.build()
+assert(optraw == b"\x00\x15\x06\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x11\x00\x00")
+
+opt = SDOption_IP6_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP6_ENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP6_ENDPOINT_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.addr == "::")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x11)
+assert(opt.port == 0x0)
+
+
+= SDOption_IP6_EndPoint: Build and dissect example
+opt = SDOption_IP6_EndPoint(addr = "2001:cdba::3257:9652", l4_proto = "TCP", port = 0x1234)
+optraw = opt.build()
+assert(optraw == b"\x00\x15\x06\x00" + b"\x20\x01\xcd\xba\x00\x00\x00\x00\x00\x00\x00\x00\x32\x57\x96\x52" + b"\x00\x06\x12\x34")
+
+opt = SDOption_IP6_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP6_ENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP6_ENDPOINT_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.addr == "2001:cdba::3257:9652")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x06)
+assert(opt.port == 0x1234)
+
+= SDOption_IP6_EndPoint: Build and dissect fully populated
+opt = SDOption_IP6_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "1234:5678:9abc:def0:0fed:cba9:8765:4321", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78" + b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x0f\xed\xcb\xa9\x87\x65\x43\x21" + b"\x9a\xbc\xde\xf0")
+
+opt = SDOption_IP6_EndPoint(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.addr == "1234:5678:9abc:def0:fed:cba9:8765:4321")
+assert(opt.res_tail == 0x9a)
+assert(opt.l4_proto == 0xbc)
+assert(opt.port == 0xdef0)
+
+
+### SDOption_IP6_Multicast
+= SDOption_IP6_Multicast: Build and dissect empty
+opt = SDOption_IP6_Multicast()
+optraw = opt.build()
+assert(optraw == b"\x00\x15\x16\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x11\x00\x00")
+
+opt = SDOption_IP6_Multicast(optraw)
+assert(opt.len == SDOPTION_IP6_MCAST_LEN)
+assert(opt.type == SDOPTION_IP6_MCAST_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.addr == "::")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x11)
+assert(opt.port == 0x0)
+
+
+= SDOption_IP6_Multicast: Build and dissect example
+opt = SDOption_IP6_Multicast(addr = "2001:cdba::3257:9652", l4_proto = "TCP", port = 0x1234)
+optraw = opt.build()
+assert(optraw == b"\x00\x15\x16\x00" + b"\x20\x01\xcd\xba\x00\x00\x00\x00\x00\x00\x00\x00\x32\x57\x96\x52" + b"\x00\x06\x12\x34")
+
+opt = SDOption_IP6_Multicast(optraw)
+assert(opt.len == SDOPTION_IP6_MCAST_LEN)
+assert(opt.type == SDOPTION_IP6_MCAST_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.addr == "2001:cdba::3257:9652")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x06)
+assert(opt.port == 0x1234)
+
+= SDOption_IP6_Multicast: Build and dissect fully populated
+opt = SDOption_IP6_Multicast(len=0x1234, type=0x56, res_hdr=0x78, addr = "1234:5678:9abc:def0:0fed:cba9:8765:4321", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78" + b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x0f\xed\xcb\xa9\x87\x65\x43\x21" + b"\x9a\xbc\xde\xf0")
+
+opt = SDOption_IP6_Multicast(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.addr == "1234:5678:9abc:def0:fed:cba9:8765:4321")
+assert(opt.res_tail == 0x9a)
+assert(opt.l4_proto == 0xbc)
+assert(opt.port == 0xdef0)
+
+
+### SDOption_IP6_SD_EndPoint
+= SDOption_IP6_SD_EndPoint: Build and dissect empty
+opt = SDOption_IP6_SD_EndPoint()
+optraw = opt.build()
+assert(optraw == b"\x00\x15\x26\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x11\x00\x00")
+
+opt = SDOption_IP6_SD_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP6_SDENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP6_SDENDPOINT_TYPE)
+assert(opt.res_hdr == 0x0)
+assert(opt.addr == "::")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x11)
+assert(opt.port == 0x0)
+
+
+= SDOption_IP6_SD_EndPoint: Build and dissect example
+opt = SDOption_IP6_SD_EndPoint(addr = "2001:cdba::3257:9652", l4_proto = "TCP", port = 0x1234)
+optraw = opt.build()
+assert(optraw == b"\x00\x15\x26\x00" + b"\x20\x01\xcd\xba\x00\x00\x00\x00\x00\x00\x00\x00\x32\x57\x96\x52" + b"\x00\x06\x12\x34")
+
+opt = SDOption_IP6_SD_EndPoint(optraw)
+assert(opt.len == SDOPTION_IP6_SDENDPOINT_LEN)
+assert(opt.type == SDOPTION_IP6_SDENDPOINT_TYPE)
+assert(opt.res_hdr == 0x00)
+assert(opt.addr == "2001:cdba::3257:9652")
+assert(opt.res_tail == 0x0)
+assert(opt.l4_proto == 0x06)
+assert(opt.port == 0x1234)
+
+= SDOption_IP6_SD_EndPoint: Build and dissect fully populated
+opt = SDOption_IP6_SD_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "1234:5678:9abc:def0:0fed:cba9:8765:4321", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0)
+optraw = opt.build()
+assert(optraw == b"\x12\x34\x56\x78" + b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x0f\xed\xcb\xa9\x87\x65\x43\x21" + b"\x9a\xbc\xde\xf0")
+
+opt = SDOption_IP6_SD_EndPoint(optraw)
+assert(opt.len == 0x1234)
+assert(opt.type == 0x56)
+assert(opt.res_hdr == 0x78)
+assert(opt.addr == "1234:5678:9abc:def0:fed:cba9:8765:4321")
+assert(opt.res_tail == 0x9a)
+assert(opt.l4_proto == 0xbc)
+assert(opt.port == 0xdef0)
+
+= verify building and parsing of multiple SDOptions
+def _opts_check(opts):
+	optslen = sum([len(o) for o in opts])
+	sd = SD()
+	sd.set_optionArray(opts)
+	sd.len_entry_array = 0
+	sd.len_option_array = optslen
+	sd.show()
+	SD(sd.build()).show()
+	assert(sd.show(dump=True) == SD(sd.build()).show(dump=True))
+
+# options are built and reparsed, to make sure all is calculated
+opts = [
+	SDOption_Config(SDOption_Config(cfg_str="hello world").build()),
+	SDOption_LoadBalance(SDOption_LoadBalance().build()),
+	SDOption_IP4_EndPoint(SDOption_IP4_EndPoint().build()),
+	SDOption_IP4_Multicast(SDOption_IP4_Multicast().build()),
+	SDOption_IP4_SD_EndPoint(SDOption_IP4_SD_EndPoint().build()),
+	SDOption_IP6_EndPoint(SDOption_IP6_EndPoint().build()),
+	SDOption_IP6_Multicast(SDOption_IP6_Multicast().build()),
+	SDOption_IP6_SD_EndPoint(SDOption_IP6_SD_EndPoint().build()),
+]
+_opts_check(opts[0:0])
+_opts_check(opts[0:2])
+_opts_check(opts)
+_opts_check(opts[::-1])
+_opts_check(opts + opts[::-1])
+