527 lines
18 KiB
Python
527 lines
18 KiB
Python
|
# 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 = 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.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, Raw, bind_top_down, bind_bottom_up
|
||
|
from scapy.fields import XShortField, BitEnumField, ConditionalField, \
|
||
|
BitField, XBitField, IntField, XByteField, ByteEnumField, \
|
||
|
ShortField, X3BytesField, StrLenField, IPField, FieldLenField, \
|
||
|
PacketListField, XIntField
|
||
|
|
||
|
|
||
|
class SOMEIP(Packet):
|
||
|
""" SOME/IP Packet."""
|
||
|
|
||
|
PROTOCOL_VERSION = 0x01
|
||
|
INTERFACE_VERSION = 0x01
|
||
|
LEN_OFFSET = 0x08
|
||
|
LEN_OFFSET_TP = 0x0c
|
||
|
TYPE_REQUEST = 0x00
|
||
|
TYPE_REQUEST_NO_RET = 0x01
|
||
|
TYPE_NOTIFICATION = 0x02
|
||
|
TYPE_REQUEST_ACK = 0x40
|
||
|
TYPE_REQUEST_NORET_ACK = 0x41
|
||
|
TYPE_NOTIFICATION_ACK = 0x42
|
||
|
TYPE_RESPONSE = 0x80
|
||
|
TYPE_ERROR = 0x81
|
||
|
TYPE_RESPONSE_ACK = 0xc0
|
||
|
TYPE_ERROR_ACK = 0xc1
|
||
|
TYPE_TP_REQUEST = 0x20
|
||
|
TYPE_TP_REQUEST_NO_RET = 0x21
|
||
|
TYPE_TP_NOTIFICATION = 0x22
|
||
|
TYPE_TP_RESPONSE = 0x23
|
||
|
TYPE_TP_ERROR = 0x24
|
||
|
RET_E_OK = 0x00
|
||
|
RET_E_NOT_OK = 0x01
|
||
|
RET_E_UNKNOWN_SERVICE = 0x02
|
||
|
RET_E_UNKNOWN_METHOD = 0x03
|
||
|
RET_E_NOT_READY = 0x04
|
||
|
RET_E_NOT_REACHABLE = 0x05
|
||
|
RET_E_TIMEOUT = 0x06
|
||
|
RET_E_WRONG_PROTOCOL_V = 0x07
|
||
|
RET_E_WRONG_INTERFACE_V = 0x08
|
||
|
RET_E_MALFORMED_MSG = 0x09
|
||
|
RET_E_WRONG_MESSAGE_TYPE = 0x0a
|
||
|
|
||
|
_OVERALL_LEN_NOPAYLOAD = 16
|
||
|
|
||
|
name = "SOME/IP"
|
||
|
|
||
|
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),
|
||
|
IntField("len", None),
|
||
|
XShortField("client_id", 0),
|
||
|
XShortField("session_id", 0),
|
||
|
XByteField("proto_ver", PROTOCOL_VERSION),
|
||
|
XByteField("iface_ver", INTERFACE_VERSION),
|
||
|
ByteEnumField("msg_type", TYPE_REQUEST, {
|
||
|
TYPE_REQUEST: "REQUEST",
|
||
|
TYPE_REQUEST_NO_RET: "REQUEST_NO_RETURN",
|
||
|
TYPE_NOTIFICATION: "NOTIFICATION",
|
||
|
TYPE_REQUEST_ACK: "REQUEST_ACK",
|
||
|
TYPE_REQUEST_NORET_ACK: "REQUEST_NO_RETURN_ACK",
|
||
|
TYPE_NOTIFICATION_ACK: "NOTIFICATION_ACK",
|
||
|
TYPE_RESPONSE: "RESPONSE",
|
||
|
TYPE_ERROR: "ERROR",
|
||
|
TYPE_RESPONSE_ACK: "RESPONSE_ACK",
|
||
|
TYPE_ERROR_ACK: "ERROR_ACK",
|
||
|
TYPE_TP_REQUEST: "TP_REQUEST",
|
||
|
TYPE_TP_REQUEST_NO_RET: "TP_REQUEST_NO_RETURN",
|
||
|
TYPE_TP_NOTIFICATION: "TP_NOTIFICATION",
|
||
|
TYPE_TP_RESPONSE: "TP_RESPONSE",
|
||
|
TYPE_TP_ERROR: "TP_ERROR",
|
||
|
}),
|
||
|
ByteEnumField("retcode", 0, {
|
||
|
RET_E_OK: "E_OK",
|
||
|
RET_E_NOT_OK: "E_NOT_OK",
|
||
|
RET_E_UNKNOWN_SERVICE: "E_UNKNOWN_SERVICE",
|
||
|
RET_E_UNKNOWN_METHOD: "E_UNKNOWN_METHOD",
|
||
|
RET_E_NOT_READY: "E_NOT_READY",
|
||
|
RET_E_NOT_REACHABLE: "E_NOT_REACHABLE",
|
||
|
RET_E_TIMEOUT: "E_TIMEOUT",
|
||
|
RET_E_WRONG_PROTOCOL_V: "E_WRONG_PROTOCOL_VERSION",
|
||
|
RET_E_WRONG_INTERFACE_V: "E_WRONG_INTERFACE_VERSION",
|
||
|
RET_E_MALFORMED_MSG: "E_MALFORMED_MESSAGE",
|
||
|
RET_E_WRONG_MESSAGE_TYPE: "E_WRONG_MESSAGE_TYPE",
|
||
|
}),
|
||
|
ConditionalField(BitField("offset", 0, 28),
|
||
|
lambda pkt: SOMEIP._is_tp(pkt)),
|
||
|
ConditionalField(BitField("res", 0, 3),
|
||
|
lambda pkt: SOMEIP._is_tp(pkt)),
|
||
|
ConditionalField(BitField("more_seg", 0, 1),
|
||
|
lambda pkt: SOMEIP._is_tp(pkt))
|
||
|
]
|
||
|
|
||
|
def post_build(self, pkt, pay):
|
||
|
length = self.len
|
||
|
if length is None:
|
||
|
if SOMEIP._is_tp(self):
|
||
|
length = SOMEIP.LEN_OFFSET_TP + len(pay)
|
||
|
else:
|
||
|
length = SOMEIP.LEN_OFFSET + len(pay)
|
||
|
|
||
|
pkt = pkt[:4] + struct.pack("!I", length) + pkt[8:]
|
||
|
return pkt + pay
|
||
|
|
||
|
def answers(self, other):
|
||
|
if other.__class__ == self.__class__:
|
||
|
if self.msg_type in [SOMEIP.TYPE_REQUEST_NO_RET,
|
||
|
SOMEIP.TYPE_REQUEST_NORET_ACK,
|
||
|
SOMEIP.TYPE_NOTIFICATION,
|
||
|
SOMEIP.TYPE_TP_REQUEST_NO_RET,
|
||
|
SOMEIP.TYPE_TP_NOTIFICATION]:
|
||
|
return 0
|
||
|
return self.payload.answers(other.payload)
|
||
|
return 0
|
||
|
|
||
|
@staticmethod
|
||
|
def _is_tp(pkt):
|
||
|
"""Returns true if pkt is using SOMEIP-TP, else returns false."""
|
||
|
|
||
|
tp = [SOMEIP.TYPE_TP_REQUEST, SOMEIP.TYPE_TP_REQUEST_NO_RET,
|
||
|
SOMEIP.TYPE_TP_NOTIFICATION, SOMEIP.TYPE_TP_RESPONSE,
|
||
|
SOMEIP.TYPE_TP_ERROR]
|
||
|
if isinstance(pkt, Packet):
|
||
|
return pkt.msg_type in tp
|
||
|
else:
|
||
|
return pkt[15] in tp
|
||
|
|
||
|
def fragment(self, fragsize=1392):
|
||
|
"""Fragment SOME/IP-TP"""
|
||
|
fnb = 0
|
||
|
fl = self
|
||
|
lst = list()
|
||
|
while fl.underlayer is not None:
|
||
|
fnb += 1
|
||
|
fl = fl.underlayer
|
||
|
|
||
|
for p in fl:
|
||
|
s = raw(p[fnb].payload)
|
||
|
nb = (len(s) + fragsize) // fragsize
|
||
|
for i in range(nb):
|
||
|
q = p.copy()
|
||
|
del q[fnb].payload
|
||
|
q[fnb].len = SOMEIP.LEN_OFFSET_TP + \
|
||
|
len(s[i * fragsize:(i + 1) * fragsize])
|
||
|
q[fnb].more_seg = 1
|
||
|
if i == nb - 1:
|
||
|
q[fnb].more_seg = 0
|
||
|
q[fnb].offset += i * fragsize // 16
|
||
|
r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize])
|
||
|
r.overload_fields = p[fnb].payload.overload_fields.copy()
|
||
|
q.add_payload(r)
|
||
|
lst.append(q)
|
||
|
|
||
|
return lst
|
||
|
|
||
|
|
||
|
def _bind_someip_layers():
|
||
|
bind_top_down(UDP, SOMEIP, sport=30490, dport=30490)
|
||
|
|
||
|
for i in range(15):
|
||
|
bind_bottom_up(UDP, SOMEIP, sport=30490 + i)
|
||
|
bind_bottom_up(TCP, SOMEIP, sport=30490 + i)
|
||
|
bind_bottom_up(UDP, SOMEIP, dport=30490 + i)
|
||
|
bind_bottom_up(TCP, SOMEIP, dport=30490 + i)
|
||
|
|
||
|
|
||
|
_bind_someip_layers()
|
||
|
|
||
|
|
||
|
class _SDPacketBase(Packet):
|
||
|
""" base class to be used among all SD Packet definitions."""
|
||
|
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)
|
||
|
]
|
||
|
|
||
|
|
||
|
class SDEntry_Service(_SDPacketBase):
|
||
|
name = "Service Entry"
|
||
|
fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC(
|
||
|
SDENTRY_TYPE_SRV_FINDSERVICE)
|
||
|
fields_desc += [
|
||
|
XIntField("minor_ver", 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 _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
|
||
|
|
||
|
return cls(payload, **kargs)
|
||
|
|
||
|
|
||
|
def _sdoption_class(payload, **kargs):
|
||
|
pl_type = orb(payload[2])
|
||
|
|
||
|
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)
|
||
|
|
||
|
return cls(payload, **kargs)
|
||
|
|
||
|
|
||
|
# SD Option
|
||
|
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)
|
||
|
]
|
||
|
|
||
|
|
||
|
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_Config(_SDPacketBase):
|
||
|
name = "Config Option"
|
||
|
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):
|
||
|
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
|
||
|
|
||
|
@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"
|
||
|
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(_SDPacketBase):
|
||
|
name = "IP4 EndPoint Option"
|
||
|
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(_SDPacketBase):
|
||
|
name = "IP4 Multicast Option"
|
||
|
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(_SDPacketBase):
|
||
|
name = "IP4 SDEndPoint Option"
|
||
|
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(_SDPacketBase):
|
||
|
name = "IP6 EndPoint Option"
|
||
|
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(_SDPacketBase):
|
||
|
name = "IP6 Multicast Option"
|
||
|
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(_SDPacketBase):
|
||
|
name = "IP6 SDEndPoint Option"
|
||
|
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()
|
||
|
|
||
|
|
||
|
##
|
||
|
# 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 = [
|
||
|
XByteField("flags", 0),
|
||
|
X3BytesField("res", 0),
|
||
|
FieldLenField("len_entry_array", None,
|
||
|
length_of="entry_array", fmt="!I"),
|
||
|
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_class,
|
||
|
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,
|
||
|
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)
|