Repository: secdev/scapy

Issue:
Scapy doesn't recognize SOME/IP-SD layer
#### Environment

-   Scapy version: v2.4.3.dev31
-   Python version: 3.6.4
-   Operating System: Windows 10 64bit

#### How to reproduce
```
from scapy.all import *
load_contrib("automotive.someip_sd")

ea = SDEntry_Service()
ea.tpe = 0
ea.srv_id = 0x1234
ea.inst_id = 0x5678
ea.ttl = 3

sd = SD()
sd.set_entryArray(ea)
some = sd.get_someip(True)

print("original packet:")
some.show()

some_raw = SOMEIP(some.build())

print("rebuilt packet:")
some_raw.show()
```

#### Actual result

```
original packet:
###[ SOME/IP ]### 
  \msg_id    \
   |###[ MessageId ]### 
   |  srv_id    = 65535
   |  sub_id    = EVENT_ID
   |  event_id  = 256
  len       = None
  \req_id    \
   |###[ RequestId ]### 
   |  client_id = 0
   |  session_id= 0
  proto_ver = 1
  iface_ver = 1
  msg_type  = NOTIFICATION
  retcode   = E_OK
###[ 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    = 4660
      |  inst_id   = 22136
      |  major_ver = 0
      |  ttl       = 0x3
      |  minor_ver = 0
     len_option_array= None
     \option_array\

rebuilt packet:
###[ SOME/IP ]### 
  \msg_id    \
   |###[ MessageId ]### 
   |  srv_id    = 65535
   |  sub_id    = EVENT_ID
   |  event_id  = 256
  len       = 36
  \req_id    \
   |###[ RequestId ]### 
   |  client_id = 0
   |  session_id= 0
  proto_ver = 1
  iface_ver = 1
  msg_type  = NOTIFICATION
  retcode   = E_OK
###[ Raw ]### 
     load      = '\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x124Vx\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00'

```

#### Expected result

Scapy should recognize the SD layer, but instead there is only a Raw layer given. I understand, that there is no simple bind, because of the fieldclasses, but there should be another possibility to bind the SOMEIP and the SD layer. 

#### Related resources

Autostar writes in the official protocol docs:

> [PRS_SOMEIPSD_00151]dService Discovery messages shall use the Service-ID (16Bits) of 0xFFFF

> [PRS_SOMEIPSD_00152]dService Discovery messages shall use the Method-ID (16Bits) of 0x8100

> [PRS_SOMEIPSD_00154]dService Discovery messages shall have a Client-ID (16Bits) set to  0x0000, since there exists only a single SOME/IP-SD instance.

This could be used to recognize a SD Layer.



Discussion:
Thanks for the issue, seems like a legit error.
Do you want to provide a pull request?
I will provide one if a solution was found
You probably have to modify the `post_build` function in the `SOMEIP` class
It's not about the building. The packet is built correctly and wireshark captures it properly. The problem is, that the SD Layer is not binded to the SOME/IP Layer. The solution is not that easy, because a bind like the following doesn't work.

`bind_layers(SOMEIP, SD, msg_id.srv_id = 0xffff)`

The problem is the SOME/IP's fields which define the SD Layer are not simple fields, but a field class.
Hi, thanks for your reply. This is the same, we observed. A solution would be a custom implementation of ‘guess_payload’. Sorry for my point to ‘post_build’. I did a similar thing on ISOTP: https://github.com/secdev/scapy/blob/d31378c886be2b831f80d29df4b5974c2921bf42/scapy/contrib/isotp.py#L211

Hope this example helps you for your PR.

> On 27. Nov 2019, at 08:27, kruemelbacke <notifications@github.com> wrote:
> 
> It's not about the building. The packet is built correctly and wireshark captures it properly. The problem is, that the SD Layer is not binded to the SOME/IP Layer. The solution is not that easy, because a bind like the following doesn't work.
> 
> bind_layers(SOMEIP, SD, msg_id.srv_id = 0xffff)
> 
> The problem is the SOME/IP's fields which define the SD Layer are not simple fields, but a field class.
> 
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub, or unsubscribe.

Unfortunately no, but thank you for your answer. The field that defines if there is a SD Layer is not in the payload itself, but in the SOMEIP Layer. The problem here is, that the `guess_payload_class` only gets the payload as param and not the needed SOMEIP Layer. Any further idea how to bind the layers?
```
###[ SOME/IP ]###
  \msg_id\
   |###[ MessageId ]###
   |  srv_id= 0xFFFF <==========
   |  sub_id= METHOD_ID        These fields define if there's a SD Layer or not (or event_id)
   |  method_id= 0x8100 <=======
...
```
Hi, I might be wrong, but shouldn't it be possible to use `self` in `guess_payload_class`. `self` should be the `SOMEIP` packet, which then should allow you to ask for `self.msg_id.srv_id`.
Oh well, that's brilliant! Thank you, I will test it

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
@@ -28,43 +28,20 @@
 # scapy.contrib.description = Scalable service-Oriented MiddlewarE/IP (SOME/IP)
 # scapy.contrib.status = loads
 
+import ctypes
+import collections
 import struct
 
 from scapy.layers.inet import TCP, UDP
-from scapy.compat import raw
+from scapy.layers.inet6 import IP6Field
+from scapy.compat import raw, orb
 from scapy.config import conf
 from scapy.modules.six.moves import range
-from scapy.packet import Packet, bind_top_down, bind_bottom_up
+from scapy.packet import Packet, Raw, bind_top_down, bind_bottom_up
 from scapy.fields import XShortField, BitEnumField, ConditionalField, \
-    BitField, XBitField, PacketField, IntField, XByteField, ByteEnumField
-
-
-class _SOMEIP_MessageId(Packet):
-    """ MessageId subpacket."""
-    name = "MessageId"
-    fields_desc = [
-        XShortField("srv_id", 0),
-        BitEnumField("sub_id", 0, 1, {0: "METHOD_ID", 1: "EVENT_ID"}),
-        ConditionalField(XBitField("method_id", 0, 15),
-                         lambda pkt: pkt.sub_id == 0),
-        ConditionalField(XBitField("event_id", 0, 15),
-                         lambda pkt: pkt.sub_id == 1)
-    ]
-
-    def extract_padding(self, s):
-        return "", s
-
-
-class _SOMEIP_RequestId(Packet):
-    """ RequestId subpacket."""
-    name = "RequestId"
-    fields_desc = [
-        XShortField("client_id", 0),
-        XShortField("session_id", 0)
-    ]
-
-    def extract_padding(self, s):
-        return "", s
+    BitField, XBitField, IntField, XByteField, ByteEnumField, ByteField, \
+    ShortField, X3BytesField, StrField, IPField, FieldLenField, \
+    PacketListField
 
 
 class SOMEIP(Packet):
@@ -106,11 +83,15 @@ class SOMEIP(Packet):
     name = "SOME/IP"
 
     fields_desc = [
-        PacketField("msg_id", _SOMEIP_MessageId(),
-                    _SOMEIP_MessageId),
+        XShortField("srv_id", 0),
+        BitEnumField("sub_id", 0, 1, {0: "METHOD_ID", 1: "EVENT_ID"}),
+        ConditionalField(XBitField("method_id", 0, 15),
+                         lambda pkt: pkt.sub_id == 0),
+        ConditionalField(XBitField("event_id", 0, 15),
+                         lambda pkt: pkt.sub_id == 1),
         IntField("len", None),
-        PacketField("req_id", _SOMEIP_RequestId(),
-                    _SOMEIP_RequestId),
+        XShortField("client_id", 0),
+        XShortField("session_id", 0),
         XByteField("proto_ver", PROTOCOL_VERSION),
         XByteField("iface_ver", INTERFACE_VERSION),
         ByteEnumField("msg_type", TYPE_REQUEST, {
@@ -225,3 +206,325 @@ def _bind_someip_layers():
 
 
 _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 init_fields(self):
+        super(_SDPacketBase, self).init_fields()
+        self._set_defaults()
+
+
+# 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)
+    ]
+
+    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
+
+
+class SDEntry_Service(_SDEntry):
+    _defaults = {"type": _SDEntry.TYPE_SRV_FINDSERVICE}
+
+    name = "Service Entry"
+    fields_desc = [
+        _SDEntry,
+        IntField("minor_ver", 0)
+    ]
+
+
+class SDEntry_EventGroup(_SDEntry):
+    _defaults = {"type": _SDEntry.TYPE_EVTGRP_SUBSCRIBE}
+
+    name = "Eventgroup Entry"
+    fields_desc = [
+        _SDEntry,
+        BitField("res", 0, 12),
+        BitField("cnt", 0, 4),
+        ShortField("eventgroup_id", 0)
+    ]
+
+
+# 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)
+    ]
+
+
+class _SDOption_Tail(_SDOption):
+    fields_desc = [
+        ByteField("res_tail", 0),
+        ByteEnumField("l4_proto", 0x06, {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
+
+    name = "Config Option"
+    _defaults = {'type': _SDOption.CFG_TYPE}
+    fields_desc = [
+        _SDOption_Header,
+        StrField("cfg_str", "")
+    ]
+
+    def post_build(self, pkt, pay):
+        length = self.len
+        if length is None:
+            length = len(self.cfg_str) + self.LEN_OFFSET
+            pkt = struct.pack("!H", length) + pkt[2:]
+        return pkt + pay
+
+
+class SDOption_LoadBalance(_SDOption):
+    name = "LoadBalance Option"
+    _defaults = {'type': _SDOption.LOADBALANCE_TYPE,
+                 'len': _SDOption.LOADBALANCE_LEN}
+    fields_desc = [
+        _SDOption_Header,
+        ShortField("priority", 0),
+        ShortField("weight", 0)
+    ]
+
+
+class SDOption_IP4_EndPoint(_SDOption_IP4):
+    name = "IP4 EndPoint Option"
+    _defaults = {'type': _SDOption.IP4_ENDPOINT_TYPE,
+                 'len': _SDOption.IP4_ENDPOINT_LEN}
+
+
+class SDOption_IP4_Multicast(_SDOption_IP4):
+    name = "IP4 Multicast Option"
+    _defaults = {'type': _SDOption.IP4_MCAST_TYPE,
+                 'len': _SDOption.IP4_MCAST_LEN}
+
+
+class SDOption_IP4_SD_EndPoint(_SDOption_IP4):
+    name = "IP4 SDEndPoint Option"
+    _defaults = {'type': _SDOption.IP4_SDENDPOINT_TYPE,
+                 'len': _SDOption.IP4_SDENDPOINT_LEN}
+
+
+class SDOption_IP6_EndPoint(_SDOption_IP6):
+    name = "IP6 EndPoint Option"
+    _defaults = {'type': _SDOption.IP6_ENDPOINT_TYPE,
+                 'len': _SDOption.IP6_ENDPOINT_LEN}
+
+
+class SDOption_IP6_Multicast(_SDOption_IP6):
+    name = "IP6 Multicast Option"
+    _defaults = {'type': _SDOption.IP6_MCAST_TYPE,
+                 'len': _SDOption.IP6_MCAST_LEN}
+
+
+class SDOption_IP6_SD_EndPoint(_SDOption_IP6):
+    name = "IP6 SDEndPoint Option"
+    _defaults = {'type': _SDOption.IP6_SDENDPOINT_TYPE,
+                 'len': _SDOption.IP6_SDENDPOINT_LEN}
+
+
+##
+# SD PACKAGE DEFINITION
+##
+class SD(_SDPacketBase):
+    """
+    SD Packet
+
+    NOTE :   when adding 'entries' or 'options', do not use list.append()
+        method but create a new list
+    e.g. :  p = SD()
+            p.option_array = [SDOption_Config(),SDOption_IP6_EndPoint()]
+    """
+    SOMEIP_MSGID_SRVID = 0xffff
+    SOMEIP_MSGID_SUBID = 0x1
+    SOMEIP_MSGID_EVENTID = 0x100
+    SOMEIP_CLIENT_ID = 0x0000
+    SOMEIP_MINIMUM_SESSION_ID = 0x0001
+    SOMEIP_PROTO_VER = 0x01
+    SOMEIP_IFACE_VER = 0x01
+    SOMEIP_MSG_TYPE = SOMEIP.TYPE_NOTIFICATION
+    SOMEIP_RETCODE = SOMEIP.RET_E_OK
+
+    _sdFlag = collections.namedtuple('Flag', 'mask offset')
+    FLAGSDEF = {
+        "REBOOT": _sdFlag(mask=0x80, offset=7),
+        "UNICAST": _sdFlag(mask=0x40, offset=6)
+    }
+
+    name = "SD"
+    fields_desc = [
+        ByteField("flags", 0),
+        X3BytesField("res", 0),
+        FieldLenField("len_entry_array", None,
+                      length_of="entry_array", fmt="!I"),
+        PacketListField("entry_array", None, cls=_SDEntry,
+                        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,
+                        length_from=lambda pkt: pkt.len_option_array)
+    ]
+
+    def get_flag(self, name):
+        name = name.upper()
+        if name in self.FLAGSDEF:
+            return ((self.flags & self.FLAGSDEF[name].mask) >>
+                    self.FLAGSDEF[name].offset)
+        else:
+            return None
+
+    def set_flag(self, name, value):
+        name = name.upper()
+        if name in self.FLAGSDEF:
+            self.flags = (self.flags &
+                          (ctypes.c_ubyte(~self.FLAGSDEF[name].mask).value)) \
+                | ((value & 0x01) << self.FLAGSDEF[name].offset)
+
+    def set_entryArray(self, entry_list):
+        if isinstance(entry_list, list):
+            self.entry_array = entry_list
+        else:
+            self.entry_array = [entry_list]
+
+    def set_optionArray(self, option_list):
+        if isinstance(option_list, list):
+            self.option_array = option_list
+        else:
+            self.option_array = [option_list]
+
+
+bind_top_down(SOMEIP, SD,
+              srv_id=SD.SOMEIP_MSGID_SRVID,
+              sub_id=SD.SOMEIP_MSGID_SUBID,
+              client_id=SD.SOMEIP_CLIENT_ID,
+              session_id=SD.SOMEIP_MINIMUM_SESSION_ID,
+              event_id=SD.SOMEIP_MSGID_EVENTID,
+              proto_ver=SD.SOMEIP_PROTO_VER,
+              iface_ver=SD.SOMEIP_IFACE_VER,
+              msg_type=SD.SOMEIP_MSG_TYPE,
+              retcode=SD.SOMEIP_RETCODE)
+
+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,
+               msg_type=SD.SOMEIP_MSG_TYPE,
+               retcode=SD.SOMEIP_RETCODE)
+
+# FIXME: Service Discovery messages shall be transported over UDP
+#        (TR_SOMEIP_00248)
+# FIXME: The port 30490 (UDP and TCP as well) shall be only used for SOME/IP-SD
+#        and not used for applications communicating over SOME/IP
+#        (TR_SOMEIP_00020)
diff --git a/scapy/contrib/automotive/someip_sd.py b/scapy/contrib/automotive/someip_sd.py
deleted file mode 100644
--- a/scapy/contrib/automotive/someip_sd.py
+++ /dev/null
@@ -1,347 +0,0 @@
-# MIT License
-
-# Copyright (c) 2018 Jose Amores
-
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-# This file is part of Scapy
-# See http://www.secdev.org/projects/scapy for more information
-# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
-# This program is published under a GPLv2 license
-
-# scapy.contrib.description = SOME/IP Service Discovery
-# scapy.contrib.status = loads
-
-import ctypes
-import collections
-import struct
-
-from scapy.packet import Packet, Raw
-from scapy.fields import ByteField, BitField, ShortField, \
-    X3BytesField, IntField, ByteEnumField, StrField, IPField, \
-    FieldLenField, PacketListField
-from scapy.contrib.automotive.someip import SOMEIP
-from scapy.layers.inet6 import IP6Field
-from scapy.compat import orb
-
-
-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 init_fields(self):
-        super(_SDPacketBase, self).init_fields()
-        self._set_defaults()
-
-
-# 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)
-    ]
-
-    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)
-
-
-class SDEntry_Service(_SDEntry):
-    _defaults = {"type": _SDEntry.TYPE_SRV_FINDSERVICE}
-
-    name = "Service Entry"
-    fields_desc = [
-        _SDEntry,
-        IntField("minor_ver", 0)
-    ]
-
-
-class SDEntry_EventGroup(_SDEntry):
-    _defaults = {"type": _SDEntry.TYPE_EVTGRP_SUBSCRIBE}
-
-    name = "Eventgroup Entry"
-    fields_desc = [
-        _SDEntry,
-        BitField("res", 0, 12),
-        BitField("cnt", 0, 4),
-        ShortField("eventgroup_id", 0)
-    ]
-
-
-# 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)
-    ]
-
-
-class _SDOption_Tail(_SDOption):
-    fields_desc = [
-        ByteField("res_tail", 0),
-        ByteEnumField("l4_proto", 0x06, {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
-
-    name = "Config Option"
-    _defaults = {'type': _SDOption.CFG_TYPE}
-    fields_desc = [
-        _SDOption_Header,
-        StrField("cfg_str", "")
-    ]
-
-    def post_build(self, pkt, pay):
-        length = self.len
-        if (length is None):
-            length = len(self.cfg_str) + self.LEN_OFFSET
-            pkt = struct.pack("!H", length) + pkt[2:]
-        return (pkt + pay)
-
-
-class SDOption_LoadBalance(_SDOption):
-    name = "LoadBalance Option"
-    _defaults = {'type': _SDOption.LOADBALANCE_TYPE,
-                 'len': _SDOption.LOADBALANCE_LEN}
-    fields_desc = [
-        _SDOption_Header,
-        ShortField("priority", 0),
-        ShortField("weight", 0)
-    ]
-
-
-class SDOption_IP4_EndPoint(_SDOption_IP4):
-    name = "IP4 EndPoint Option"
-    _defaults = {'type': _SDOption.IP4_ENDPOINT_TYPE,
-                 'len': _SDOption.IP4_ENDPOINT_LEN}
-
-
-class SDOption_IP4_Multicast(_SDOption_IP4):
-    name = "IP4 Multicast Option"
-    _defaults = {'type': _SDOption.IP4_MCAST_TYPE,
-                 'len': _SDOption.IP4_MCAST_LEN}
-
-
-class SDOption_IP4_SD_EndPoint(_SDOption_IP4):
-    name = "IP4 SDEndPoint Option"
-    _defaults = {'type': _SDOption.IP4_SDENDPOINT_TYPE,
-                 'len': _SDOption.IP4_SDENDPOINT_LEN}
-
-
-class SDOption_IP6_EndPoint(_SDOption_IP6):
-    name = "IP6 EndPoint Option"
-    _defaults = {'type': _SDOption.IP6_ENDPOINT_TYPE,
-                 'len': _SDOption.IP6_ENDPOINT_LEN}
-
-
-class SDOption_IP6_Multicast(_SDOption_IP6):
-    name = "IP6 Multicast Option"
-    _defaults = {'type': _SDOption.IP6_MCAST_TYPE,
-                 'len': _SDOption.IP6_MCAST_LEN}
-
-
-class SDOption_IP6_SD_EndPoint(_SDOption_IP6):
-    name = "IP6 SDEndPoint Option"
-    _defaults = {'type': _SDOption.IP6_SDENDPOINT_TYPE,
-                 'len': _SDOption.IP6_SDENDPOINT_LEN}
-
-
-##
-# SD PACKAGE DEFINITION
-##
-class SD(_SDPacketBase):
-    """
-    SD Packet
-
-    NOTE :   when adding 'entries' or 'options', do not use list.append()
-        method but create a new list
-    e.g. :  p = SD()
-            p.option_array = [SDOption_Config(),SDOption_IP6_EndPoint()]
-    """
-    SOMEIP_MSGID_SRVID = 0xffff
-    SOMEIP_MSGID_SUBID = 0x1
-    SOMEIP_MSGID_EVENTID = 0x100
-    SOMEIP_PROTO_VER = 0x01
-    SOMEIP_IFACE_VER = 0x01
-    SOMEIP_MSG_TYPE = SOMEIP.TYPE_NOTIFICATION
-
-    _sdFlag = collections.namedtuple('Flag', 'mask offset')
-    FLAGSDEF = {
-        "REBOOT": _sdFlag(mask=0x80, offset=7),
-        "UNICAST": _sdFlag(mask=0x40, offset=6)
-    }
-
-    name = "SD"
-    fields_desc = [
-        ByteField("flags", 0),
-        X3BytesField("res", 0),
-        FieldLenField("len_entry_array", None,
-                      length_of="entry_array", fmt="!I"),
-        PacketListField("entry_array", None, cls=_SDEntry,
-                        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,
-                        length_from=lambda pkt: pkt.len_option_array)
-    ]
-
-    def get_flag(self, name):
-        name = name.upper()
-        if (name in self.FLAGSDEF):
-            return ((self.flags & self.FLAGSDEF[name].mask) >>
-                    self.FLAGSDEF[name].offset)
-        else:
-            return None
-
-    def set_flag(self, name, value):
-        name = name.upper()
-        if (name in self.FLAGSDEF):
-            self.flags = (self.flags &
-                          (ctypes.c_ubyte(~self.FLAGSDEF[name].mask).value)) \
-                | ((value & 0x01) << self.FLAGSDEF[name].offset)
-
-    def set_entryArray(self, entry_list):
-        if (isinstance(entry_list, list)):
-            self.entry_array = entry_list
-        else:
-            self.entry_array = [entry_list]
-
-    def set_optionArray(self, option_list):
-        if (isinstance(option_list, list)):
-            self.option_array = option_list
-        else:
-            self.option_array = [option_list]
-
-    def get_someip(self, stacked=False):
-        p = SOMEIP()
-        p.msg_id.srv_id = SD.SOMEIP_MSGID_SRVID
-        p.msg_id.sub_id = SD.SOMEIP_MSGID_SUBID
-        p.msg_id.event_id = SD.SOMEIP_MSGID_EVENTID
-        p.proto_ver = SD.SOMEIP_PROTO_VER
-        p.iface_ver = SD.SOMEIP_IFACE_VER
-        p.msg_type = SD.SOMEIP_MSG_TYPE
-
-        if (stacked):
-            return (p / self)
-        else:
-            return (p)


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
@@ -29,186 +29,65 @@
 ##########
 
 
-+ Test MessageId
++ Basic operations
 
 = Load module
-
 load_contrib("automotive.someip")
-import binascii
-
-= Check MessageId with method_id
-
-p = SOMEIP().msg_id
-p.srv_id = 0x1111
-p.method_id = 0x0222
-p.event_id = 0x0333
-
-p.sub_id = 0
-
-assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
-
-assert((struct.unpack("!B", bytes(p)[2:3])[0] & 0x80) == 0x00)
-
-assert((struct.unpack("!H", bytes(p)[2:4])[0] & ~0x8000) == 0x0222)
-
-assert(bytes(p) == b"\x11\x11\x02\x22")
-
-del(p)
-
-= Dissect MessageId with method_id
-
-
-p = SOMEIP(b'\x22\x22\x03\x33')
-
-assert(p.msg_id.srv_id == 0x2222)
-
-assert(p.msg_id.method_id == 0x0333)
-
-assert(p.msg_id.sub_id == 0)
-
-del(p)
-
-= Build MessageId with event_id
-
-p = SOMEIP().msg_id
-p.srv_id = 0x1111
-p.method_id = 0x0222
-p.event_id = 0x0333
-p.sub_id = 1
-
-assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
-
-assert((struct.unpack("!B", bytes(p)[2:3])[0] & 0x80) == 0x80)
-
-assert((struct.unpack("!H", bytes(p)[2:4])[0] & ~0x8000) == 0x0333)
-
-assert(bytes(p) == b"\x11\x11\x83\x33")
-
-del(p)
-
-= Dissect MessageId with event_id
-
-p = SOMEIP(b'\x33\x33\x82\x22')
-
-assert(p.msg_id.srv_id == 0x3333)
-
-assert(p.msg_id.event_id == 0x0222)
-
-assert(p.msg_id.sub_id == 1)
-
-del(p)
-
-+ Test RequestId
-
-= Request Id
-
-p = SOMEIP().req_id
-p.client_id = 0x1111
-p.session_id = 0x2222
-
-assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
-
-assert(struct.unpack("!H", bytes(p)[2:4])[0] == 0x2222)
-
-assert(bytes(p) == b"\x11\x11\x22\x22")
-
-del(p)
-
-= Dissect RequestId
-
-method_id = b'\x22\x22\x03\x33'
-pktLen = b'\x11\x11\x11\x11'
-reqId = b'\x22\x22\x33\x33'
-p = SOMEIP(method_id + pktLen + reqId)
-
-assert(p.req_id.client_id == 0x2222)
-
-assert(p.req_id.session_id == 0x3333)
-
-del(p)
 
-+ Test SOMEIP
-
-= Check SomeIp
++ SOME/IP operation
 
+= Basic build
 p = SOMEIP()
-pstr = binascii.hexlify(bytes(p))
-binstr = binascii.hexlify(b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00")
-assert(pstr == binstr)
-
-p.payload = Raw(binascii.unhexlify("DEADBEEF"))
 pstr = bytes(p)
-binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00\xde\xad\xbe\xef"
+binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
 assert(pstr == binstr)
 
-p.payload = Raw('')
+= Build with empty payload
+p.payload = Raw(b"")
 pstr = bytes(p)
 binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
 assert(pstr == binstr)
 
-del(p)
-
-= Dissect SomeIP packet
-
-p = SOMEIP(
-    b"\x11\x11\x81\x11\x00\x00\x00\x04\x33\x33\x44\x44\x02\x03\x04\x05")
-
-assert(p.msg_id.srv_id == 0x1111)
-
-assert(p.msg_id.event_id == 0x0111)
-
-assert(p.req_id.client_id == 0x3333)
+= Build with non-empty payload
+p.payload = Raw(b"\xde\xad\xbe\xef")
+pstr = bytes(p)
+binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00\xde\xad\xbe\xef"
+assert(pstr == binstr)
 
-assert(p.req_id.session_id == 0x4444)
+= Dissect EVENT_ID packet
+p = SOMEIP(b"\x11\x11\x81\x11\x00\x00\x00\x04\x33\x33\x44\x44\x02\x03\x04\x05")
 
+assert(p.srv_id == 0x1111)
+assert(p.sub_id == 0x1)
+assert(p.method_id == None)
+assert(p.event_id == 0x0111)
+assert(p.client_id == 0x3333)
+assert(p.session_id == 0x4444)
 assert(p.proto_ver == 0x02)
-
 assert(p.iface_ver == 0x03)
-
 assert(p.msg_type == 0x04)
-
 assert(p.retcode == 0x05)
 
-del(p)
-
-+ Test SOMEIP_SubPackages
-
-= Check MessageId subpackage
-
-p = SOMEIP()
-p.msg_id.srv_id = 0x1111
-p.msg_id.method_id = 0x0222
-p.msg_id.event_id = 0x0333
-
-p.msg_id.sub_id = 0
-pstr = bytes(p)
-binstr = b"\x11\x11\x02\x22\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
-assert(pstr == binstr)
-
-p.msg_id.sub_id = 1
-pstr = bytes(p)
-binstr = b"\x11\x11\x83\x33\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
-assert(pstr == binstr)
-
-del(p)
+= Dissect METHOD_ID packet
+p = SOMEIP(b"\x11\x11\x01\x11\x00\x00\x00\x04\x33\x33\x44\x44\x02\x03\x04\x05")
 
-= Check RequestId subpackage
-
-p = SOMEIP()
-p.req_id.client_id = 0x1111
-p.req_id.session_id = 0x2222
-
-pstr = bytes(p)
-binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x11\x11\x22\x22\x01\x01\x00\x00"
-assert(pstr == binstr)
+assert(p.srv_id == 0x1111)
+assert(p.sub_id == 0x0)
+assert(p.method_id == 0x0111)
+assert(p.event_id == None)
+assert(p.client_id == 0x3333)
+assert(p.session_id == 0x4444)
+assert(p.proto_ver == 0x02)
+assert(p.iface_ver == 0x03)
+assert(p.msg_type == 0x04)
+assert(p.retcode == 0x05)
 
-+ Test SOMEIP_TP
++ SOME/IP-TP operation
 
-= Check TP
+= Build TP
 p = SOMEIP()
 p.msg_type = 0x20
 
-
 pstr = bytes(p)
 print(pstr)
 binstr = b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x20\x00\x00\x00\x00\x00'
@@ -224,9 +103,7 @@ pstr = bytes(p)
 binstr = b'\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00'
 assert(pstr == binstr)
 
-
 = Dissect TP
-
 p = SOMEIP(b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x21\x00\x00\x00\x00\x01')
 
 assert(p.msg_type == 0x21)
@@ -239,10 +116,7 @@ pstr = bytes(p)
 binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00"
 assert(pstr == binstr)
 
-+ Test SOMEIP-TP
-
 = Build TP fragmented
-
 p = SOMEIP()
 p.msg_type = 0x20
 p.add_payload(Raw("A"*1400))
@@ -254,4 +128,291 @@ assert(f[1].len == 20)
 assert(f[0].payload == Raw("A"*1392))
 assert(f[1].payload == Raw("A"*8))
 assert(f[0].more_seg == 1)
-assert(f[1].more_seg == 0)
\ No newline at end of file
+assert(f[1].more_seg == 0)
+
++ SD Entry Service
+
+= Check packet length on empty build
+p = SDEntry_Service()
+assert(len(bytes(p)) == SDEntry_Service.OVERALL_LEN)
+
+= Build 1
+p = SDEntry_Service(type = SDEntry_Service.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)
+
+= 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)
+
+= 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.index_1 == 0x22)
+assert(p.index_2 == 0x33)
+assert(p.srv_id == 0x4444)
+assert(p.inst_id == 0x5555)
+assert(p.major_ver == 0x66)
+assert(p.ttl == 0x777777)
+assert(p.minor_ver == 0xdeadbeef)
+
++ SD Entry Eventgroup
+
+= Check packet length on empty build
+p = SDEntry_EventGroup()
+assert(len(bytes(p)) == SDEntry_EventGroup.OVERALL_LEN)
+
+= Build
+p = SDEntry_EventGroup(index_1 = 0x11, index_2 = 0x22, srv_id = 0x3333,
+			           inst_id = 0x4444, major_ver = 0x55, ttl = 0x666666,
+					   cnt = 0x7, eventgroup_id = 0x8888)
+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)
+
+= Dissect
+p = SDEntry_EventGroup(
+    b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88")
+assert(p.index_1 == 0x11)
+assert(p.index_2 == 0x22)
+assert(p.srv_id == 0x3333)
+assert(p.inst_id == 0x4444)
+assert(p.major_ver == 0x55)
+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
+p = SD()
+p.set_flag("REBOOT", 1)
+assert(p.flags == 0x80)
+
+p.set_flag("REBOOT", 0)
+assert(p.flags == 0x00)
+
+p.set_flag("UNICAST", 1)
+assert(p.flags == 0x40)
+
+p.set_flag("UNICAST", 0)
+assert(p.flags == 0x00)
+
+p.set_flag("REBOOT", 1)
+p.set_flag("UNICAST", 1)
+assert(p.flags == 0xc0)
+
++ SD Get SOME/IP Packet
+
+= Build empty
+p = SOMEIP() / SD()
+assert(len(bytes(p)) == SOMEIP._OVERALL_LEN_NOPAYLOAD + 12)
+
+= Verify constants against spec TR_SOMEIP_00250
+assert(SD.SOMEIP_MSGID_SRVID == 0xffff)
+assert(SD.SOMEIP_MSGID_SUBID == 0x1)
+assert(SD.SOMEIP_MSGID_EVENTID == 0x0100)
+assert(SD.SOMEIP_CLIENT_ID == 0x0000)
+assert(SD.SOMEIP_MINIMUM_SESSION_ID == 0x0001)
+assert(SD.SOMEIP_PROTO_VER == 0x01)
+assert(SD.SOMEIP_IFACE_VER == 0x01)
+assert(SD.SOMEIP_MSG_TYPE == 0x02)
+assert(SD.SOMEIP_RETCODE == 0x00)
+
+= check that values are bound
+assert(p[SOMEIP].srv_id == SD.SOMEIP_MSGID_SRVID)
+assert(p[SOMEIP].sub_id == SD.SOMEIP_MSGID_SUBID)
+assert(p[SOMEIP].event_id == SD.SOMEIP_MSGID_EVENTID)
+assert(p[SOMEIP].client_id == SD.SOMEIP_CLIENT_ID)
+assert(p[SOMEIP].session_id != 0x0000)
+assert(p[SOMEIP].session_id >= SD.SOMEIP_MINIMUM_SESSION_ID)
+assert(p[SOMEIP].proto_ver == SD.SOMEIP_PROTO_VER)
+assert(p[SOMEIP].iface_ver == SD.SOMEIP_IFACE_VER)
+assert(p[SOMEIP].msg_type == SD.SOMEIP_MSG_TYPE)
+assert(p[SOMEIP].retcode == SD.SOMEIP_RETCODE)
+
+# FIXME: Service Discovery messages shell be transported over UDP
+# (TR_SOMEIP_00248)
+# FIXME: The port 30490 (UDP and TCP as well) shall be only used for SOME/IP-SD
+# and not used for applications communicating over SOME/IP (TR_SOMEIP_00020)
+
++ SD
+
+= Check length of package without entries nor options
+p = SD()
+assert(len(bytes(p)) == 12)
+
+= Check entries to array and size check
+p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
+assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32)
+
+p.set_entryArray([])
+assert(struct.unpack("!L", bytes(p)[4:8])[0] == 0)
+
+= Check Options to array and size check
+p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()])
+assert(struct.unpack("!L", bytes(p)[8:12])[0] == 24)
+
+p.set_optionArray([])
+assert(struct.unpack("!L", bytes(p)[8:12])[0] == 0)
+
+= Check Entries & Options to array and size check
+p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
+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)
diff --git a/test/contrib/automotive/someip_sd.uts b/test/contrib/automotive/someip_sd.uts
deleted file mode 100644
--- a/test/contrib/automotive/someip_sd.uts
+++ /dev/null
@@ -1,381 +0,0 @@
-# MIT License
-
-# Copyright (c) 2018 Jose Amores
-
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-# This file is part of Scapy
-# See http://www.secdev.org/projects/scapy for more information
-# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
-# This program is published under a GPLv2 license
-
-#########
-#########
-
-+ SD Entry Service
-
-=load module
-
-load_contrib("automotive.someip_sd")
-
-= Check packet length
-
-p = SDEntry_Service()
-assert(len(bytes(p)) == SDEntry_Service.OVERALL_LEN)
-
-= Check fields setting
-
-p.type = SDEntry_Service.TYPE_SRV_OFFERSERVICE
-p.index_1 = 0x11
-p.index_2 = 0x22
-p.srv_id = 0x3333
-p.inst_id = 0x4444
-p.major_ver = 0x55
-p.ttl = 0x666666
-p.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)
-
-= Check fields setting2
-
-p = SDEntry_Service()
-p.n_opt_1 = 0xf1
-p.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)
-
-= Check SDEntry_Service
-
-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.index_1 == 0x22)
-assert(p.index_2 == 0x33)
-assert(p.srv_id == 0x4444)
-assert(p.inst_id == 0x5555)
-assert(p.major_ver == 0x66)
-assert(p.ttl == 0x777777)
-assert(p.minor_ver == 0xdeadbeef)
-
-+ SD Entry Eventgroup
-
-= Check packet length
-
-p = SDEntry_EventGroup()
-
-assert(len(bytes(p)) == SDEntry_EventGroup.OVERALL_LEN)
-
-= Check fields setting
-
-p.index_1 = 0x11
-p.index_2 = 0x22
-p.srv_id = 0x3333
-p.inst_id = 0x4444
-p.major_ver = 0x55
-p.ttl = 0x666666
-p.cnt = 0x7
-p.eventgroup_id = 0x8888
-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)
-
-= 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)
-
-= Check SDEntry_EventGroup
-
-p = SDEntry_EventGroup(
-    b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88")
-
-assert(p.index_1 == 0x11)
-assert(p.index_2 == 0x22)
-assert(p.srv_id == 0x3333)
-assert(p.inst_id == 0x4444)
-assert(p.major_ver == 0x55)
-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)
-
-= Check SDOption_LoadBalance
-
-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)
-
-= Check SDOption_IP4_EndPoint
-
-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)
-
-= Check SDOption_IP4_Multicast
-
-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)
-
-= Check SDOption_IP4_SD_EndPoint
-
-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)
-
-= Check SDOption_IP6_EndPoint
-
-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)
-
-= Check SDOption_IP6_Multicast
-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)
-
-= Check SDOption_IP6_SD_EndPoint
-
-p = SDOption_IP6_SD_EndPoint(b'\x00\x15\x26')
-
-assert(p.type == 0x26)
-assert(p.len == 0x0015)
-
-+ SD Flags
-
-= Check the flags
-
-p = SD()
-
-p.set_flag("REBOOT", 1)
-
-assert(p.flags == 0x80)
-
-p.set_flag("REBOOT", 0)
-
-assert(p.flags == 0x00)
-
-p.set_flag("UNICAST", 1)
-
-assert(p.flags == 0x40)
-
-p.set_flag("UNICAST", 0)
-
-assert(p.flags == 0x00)
-
-p.set_flag("REBOOT", 1)
-p.set_flag("UNICAST", 1)
-
-assert(p.flags == 0xc0)
-
-+SD Get Someip Packet
-
-= Check someip packet
-
-p_sd = SD()
-sd_len = bytes(p_sd)
-
-p_someip = p_sd.get_someip()
-
-assert(len(bytes(p_someip)) == SOMEIP._OVERALL_LEN_NOPAYLOAD)
-
-p = p_sd.get_someip(stacked=True)
-
-assert(len(bytes(p)) == SOMEIP._OVERALL_LEN_NOPAYLOAD + 12)
-
-+ SD
-
-= Check length of package without entries nor options
-
-p = SD()
-
-assert(len(bytes(p)) == 12)
-
-= Check entries to array and size check
-
-p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
-
-assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32)
-
-p.set_entryArray([])
-
-assert(struct.unpack("!L", bytes(p)[4:8])[0] == 0)
-
-= Check Options to array and size check
-
-p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()])
-
-assert(struct.unpack("!L", bytes(p)[8:12])[0] == 24)
-
-p.set_optionArray([])
-
-assert(struct.unpack("!L", bytes(p)[8:12])[0] == 0)
-
-= Check Entries & Options to array and size check
-
-p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
-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)