diff --git a/scapy/arch/__init__.py b/scapy/arch/__init__.py index d13db5244a4cdca5d757dbfd9e8ad3fcf838c850..23e1cb7ce655df563bbe01bbfe596d850d942d9c 100644 --- a/scapy/arch/__init__.py +++ b/scapy/arch/__init__.py @@ -82,15 +82,26 @@ if scapy.config.conf.iface is None: scapy.config.conf.iface = LOOPBACK_NAME +def get_if_addr6(iff): + """ + Returns the main global unicast address associated with provided + interface, in human readable form. If no global address is found, + None is returned. + """ + for x in in6_getifaddr(): + if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL: + return x[0] + + return None + def get_if_raw_addr6(iff): """ Returns the main global unicast address associated with provided interface, in network format. If no global address is found, None is returned. """ - r = filter(lambda x: x[2] == iff and x[1] == IPV6_ADDR_GLOBAL, in6_getifaddr()) - if len(r) == 0: - return None - else: - r = r[0][0] - return inet_pton(socket.AF_INET6, r) + ip6= get_if_addr6(iff) + if ip6 is not None: + return inet_pton(socket.AF_INET6, ip6) + + return None diff --git a/scapy/automaton.py b/scapy/automaton.py index 7502ac51d8a8243df303d892adf08bcf2f67008b..69651c50b68a741d177a070eb55854f794a10359 100644 --- a/scapy/automaton.py +++ b/scapy/automaton.py @@ -8,7 +8,7 @@ Automata with states, transitions and actions. """ from __future__ import with_statement -import types,itertools,time,os,sys,socket +import types,itertools,time,os,sys,socket,traceback from select import select from collections import deque import thread @@ -429,13 +429,16 @@ class Automaton: raise self.AutomatonError("INTERCEPT: unkown verdict: %r" % cmd.type) self.my_send(pkt) self.debug(3,"SENT : %s" % pkt.summary()) - self.packets.append(pkt.copy()) + + if self.store_packets: + self.packets.append(pkt.copy()) ## Internals def __init__(self, *args, **kargs): external_fd = kargs.pop("external_fd",{}) self.send_sock_class = kargs.pop("ll", conf.L3socket) + self.recv_sock_class = kargs.pop("recvsock", conf.L2listen) self.started = thread.allocate_lock() self.threadid = None self.breakpointed = None @@ -493,7 +496,8 @@ class Automaton: except ATMT.NewStateRequested, state_req: self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state)) if cond.atmt_type == ATMT.RECV: - self.packets.append(args[0]) + if self.store_packets: + self.packets.append(args[0]) for action in self.actions[cond.atmt_condname]: self.debug(2, " + Running action [%s]" % action.func_name) action(self, *state_req.action_args, **state_req.action_kargs) @@ -522,7 +526,7 @@ class Automaton: # Start the automaton self.state=self.initial_states[0](self) self.send_sock = self.send_sock_class() - self.listen_sock = conf.L2listen(**self.socket_kargs) + self.listen_sock = self.recv_sock_class(**self.socket_kargs) self.packets = PacketList(name="session[%s]"%self.__class__.__name__) singlestep = True @@ -556,8 +560,9 @@ class Automaton: c = Message(type=_ATMT_Command.END, result=e.args[0]) self.cmdout.send(c) except Exception,e: - self.debug(3, "Transfering exception [%s] from tid=%i"% (e,self.threadid)) - m = Message(type = _ATMT_Command.EXCEPTION, exception=e, exc_info=sys.exc_info()) + exc_info = sys.exc_info() + self.debug(3, "Transfering exception from tid=%i:\n%s"% (self.threadid, traceback.format_exc(exc_info))) + m = Message(type=_ATMT_Command.EXCEPTION, exception=e, exc_info=exc_info) self.cmdout.send(m) self.debug(3, "Stopping control thread (tid=%i)"%self.threadid) self.threadid = None diff --git a/scapy/contrib/isis.py b/scapy/contrib/isis.py new file mode 100644 index 0000000000000000000000000000000000000000..1fb5dde6c0c1a2435d4d3ca4d4d5fe46bdc569b6 --- /dev/null +++ b/scapy/contrib/isis.py @@ -0,0 +1,726 @@ +# scapy.contrib.description = ISIS +# scapy.contrib.status = loads + +""" + IS-IS Scapy Extension + ~~~~~~~~~~~~~~~~~~~~~ + + :copyright: 2014 BENOCS GmbH, Berlin (Germany) + :author: Marcel Patzlaff, mpatzlaff@benocs.com + :license: GPLv2 + + This module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + :description: + + This module provides Scapy layers for the Intermediate System + to Intermediate System routing protocol as defined in RFC 1195. + + Currently it (partially) supports the packaging/encoding + requirements of the following RFCs: + * RFC 1195 (only the TCP/IP related part) + * RFC 3358 (optional checksums) + * RFC 5301 (dynamic hostname extension) + * RFC 5302 (domain-wide prefix destribution) + * RFC 5303 (three-way handshake) + * RFC 5304 (cryptographic authentication) + * RFC 5308 (routing IPv6 with IS-IS) + + :TODO: + + - packet relations (requests, responses) + - support for recent RFCs: + * RFC 5305 (traffic engineering) + * RFC 5307 (support for G-MPLS) + * RFC 5310 (generic cryptographic authentication) + * RFC 5316 (inter-AS MPLS and G-MPLS TE) + +""" + +import struct + +from scapy.config import conf +from scapy.fields import Field, FlagsField, ByteField, ByteEnumField, \ + FieldLenField, ShortField, PacketListField, XIntField, \ + XShortField, ConditionalField, X3BytesField, IntField, BitField, IPField, \ + FieldListField, MACField, BitFieldLenField, IPPrefixField, IP6PrefixField,\ + BoundStrLenField +from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol +from scapy.layers.inet6 import IP6ListField +from scapy.packet import Packet, bind_layers +from scapy.utils import fletcher16_checkbytes +from scapy.volatile import RandString, RandByte +import random + + +EXT_VERSION = "v0.0.1" + +conf.debug_dissector= True + +####################################################################### +## ISIS Utilities + Fields ## +####################################################################### + +def isis_area2str(area): + return "".join(x.decode("hex") for x in area.split(".")) + +def isis_str2area(s): + if len(s) == 0: + return "" + + numbytes= len(s[1:]) + fmt= "%02X" + (".%02X%02X" * (numbytes / 2)) + ("" if (numbytes % 2) == 0 else ".%02X") + return fmt % tuple(map(ord,s)) + +def isis_sysid2str(sysid): + return "".join(x.decode("hex") for x in sysid.split(".")) + +def isis_str2sysid(s): + return ("%02X%02X."*3)[:-1] % tuple(map(ord, s)) + +def isis_nodeid2str(nodeid): + return "%s%s" % (isis_sysid2str(nodeid[:-3]), nodeid[-2:].decode("hex")) + +def isis_str2nodeid(s): + return "%s.%02X" % (isis_str2sysid(s[:-1]), ord(s[-1])) + +def isis_lspid2str(lspid): + return "%s%s" % (isis_nodeid2str(lspid[:-3]), lspid[-2:].decode("hex")) + +def isis_str2lspid(s): + return "%s-%02X" % (isis_str2nodeid(s[:-1]), ord(s[-1])) + + +class _ISIS_IdFieldBase(Field): + def __init__(self, name, default, length, to_str, to_id): + self.to_str= to_str + self.to_id= to_id + self.length= length + Field.__init__(self, name, default, "%is" %length) + + def i2m(self, pkt, x): + if x is None: + return "\0"*self.length + + return self.to_str(x) + + def m2i(self, pkt, x): + return self.to_id(x) + + def any2i(self, pkt, x): + if type(x) is str and len(x) == self.length: + return self.m2i(pkt, x) + + return x + + +class _ISIS_RandId(RandString): + def __init__(self, template): + self.bytecount= template.count("*") + self.format=template.replace("*","%02X") + + def _fix(self): + if self.bytecount == 0: + return "" + + val=() + + for _ in range(self.bytecount): + val+= (RandByte(),) + + return self.format % val + +class _ISIS_RandAreaId(_ISIS_RandId): + def __init__(self, bytecount= None): + self.bytecount= random.randint(1,13) if bytecount is None else bytecount + self.format= "%02X" + (".%02X%02X" * ((self.bytecount-1) / 2)) + ("" if ((self.bytecount-1) % 2) == 0 else ".%02X") + + +class ISIS_AreaIdField(Field): + def __init__(self, name, default, length_from): + Field.__init__(self, name, default) + self.length_from= length_from + + def i2m(self, pkt, x): + return isis_area2str(x) + + def m2i(self, pkt, x): + return isis_str2area(x) + + def i2len(self, pkt, x): + if x is None: + return 0 + l= len(x) + # l/5 is the number of dots in the Area ID + return (l - (l / 5)) / 2 + + def addfield(self, pkt, s, val): + sval= self.i2m(pkt, val) + return s+struct.pack("!%is" % len(sval), sval) + + def getfield(self, pkt, s): + numbytes= self.length_from(pkt) + return s[numbytes:], self.m2i(pkt, struct.unpack("!%is" % numbytes, s[:numbytes])[0]) + + def randval(self): + return _ISIS_RandAreaId() + + +class ISIS_SystemIdField(_ISIS_IdFieldBase): + def __init__(self, name, default): + _ISIS_IdFieldBase.__init__(self, name, default, 6, isis_sysid2str, isis_str2sysid) + + def randval(self): + return _ISIS_RandId("**.**.**") + + +class ISIS_NodeIdField(_ISIS_IdFieldBase): + def __init__(self, name, default): + _ISIS_IdFieldBase.__init__(self, name, default, 7, isis_nodeid2str, isis_str2nodeid) + + def randval(self): + return _ISIS_RandId("**.**.**.*") + + +class ISIS_LspIdField(_ISIS_IdFieldBase): + def __init__(self, name, default): + _ISIS_IdFieldBase.__init__(self, name, default, 8, isis_lspid2str, isis_str2lspid) + + def randval(self): + return _ISIS_RandId("**.**.**.*-*") + + +class ISIS_CircuitTypeField(FlagsField): + def __init__(self, name="circuittype", default=2, size=8, names=["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"]): + FlagsField.__init__(self, name, default, size, names) + + +####################################################################### +## ISIS TLVs ## +####################################################################### + +_isis_tlv_classes= { + 1: "ISIS_AreaTlv", + 2: "ISIS_IsReachabilityTlv", + 6: "ISIS_IsNeighbourTlv", + 8: "ISIS_PaddingTlv", + 9: "ISIS_LspEntryTlv", + 10: "ISIS_AuthenticationTlv", + 12: "ISIS_ChecksumTlv", + 14: "ISIS_BufferSizeTlv", + 22: "ISIS_ExtendedIsReachabilityTlv", + 128: "ISIS_InternalIpReachabilityTlv", + 129: "ISIS_ProtocolsSupportedTlv", + 130: "ISIS_ExternalIpReachabilityTlv", + 132: "ISIS_IpInterfaceAddressTlv", + 135: "ISIS_ExtendedIpReachabilityTlv", + 137: "ISIS_DynamicHostnameTlv", + 232: "ISIS_Ipv6InterfaceAddressTlv", + 236: "ISIS_Ipv6ReachabilityTlv", + 240: "ISIS_P2PAdjacencyStateTlv" +} + +_isis_tlv_names= { + 1: "Area TLV", + 2: "IS Reachability TLV", + 6: "IS Neighbour TLV", + 7: "Instance Identifier TLV", + 8: "Padding TLV", + 9: "LSP Entries TLV", + 10: "Authentication TLV", + 12: "Optional Checksum TLV", + 13: "Purge Originator Identification TLV", + 14: "LSP Buffer Size TLV", + 22: "Extended IS-Reachability TLV", + 23: "IS Neighbour Attribute TLV", + 24: "IS Alias ID", + 128: "IP Internal Reachability TLV", + 129: "Protocols Supported TLV", + 130: "IP External Reachability TLV", + 131: "Inter-Domain Routing Protocol Information TLV", + 132: "IP Interface Address TLV", + 134: "Traffic Engineering Router ID TLV", + 135: "Extended IP Reachability TLV", + 137: "Dynamic Hostname TLV", + 138: "GMPLS Shared Risk Link Group TLV", + 139: "IPv6 Shared Risk Link Group TLV", + 140: "IPv6 Traffic Engineering Router ID TLV", + 141: "Inter-AS Reachability Information TLV", + 142: "Group Address TLV", + 143: "Multi-Topology-Aware Port Capability TLV", + 144: "Multi-Topology Capability TLV", + 145: "TRILL Neighbour TLV", + 147: "MAC-Reachability TLV", + 148: "BFD-Enabled TLV", + 211: "Restart TLV", + 222: "Multi-Topology Intermediate Systems TLV", + 223: "Multi-Topology IS Neighbour Attributes TLV", + 229: "Multi-Topology TLV", + 232: "IPv6 Interface Address TLV", + 233: "IPv6 Global Interface Address TLV", + 235: "Multi-Topology IPv4 Reachability TLV", + 236: "IPv6 Reachability TLV", + 237: "Multi-Topology IPv6 Reachability TLV", + 240: "Point-to-Point Three-Way Adjacency TLV", + 242: "IS-IS Router Capability TLV", + 251: "Generic Information TLV" +} + + + +def _ISIS_GuessTlvClass(p, **kargs): + cls = conf.raw_layer + if len(p) >= 2: + tlvtype = struct.unpack("!B", p[0])[0] + clsname = _isis_tlv_classes.get(tlvtype, "ISIS_GenericTlv") + cls = globals()[clsname] + + return cls(p, **kargs) + + +class ISIS_GenericTlv(Packet): + name = "ISIS Generic TLV" + fields_desc = [ByteEnumField("type", 0, _isis_tlv_names), + FieldLenField("len", None, length_of="val", fmt="B"), + BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)] + + def guess_payload_class(self, p): + return conf.padding_layer + + +class ISIS_AreaEntry(Packet): + name = "ISIS Area Entry" + fields_desc = [FieldLenField("arealen", None, length_of="areaid", fmt="B"), + ISIS_AreaIdField("areaid", "49", length_from=lambda pkt: pkt.arealen)] + + def extract_padding(self, s): + return "", s + + +class ISIS_AreaTlv(ISIS_GenericTlv): + name = "ISIS Area TLV" + fields_desc = [ByteEnumField("type", 1, _isis_tlv_names), + FieldLenField("len", None, length_of= "areas", fmt="B"), + PacketListField("areas", [], ISIS_AreaEntry, length_from=lambda x: x.len)] + + +class ISIS_AuthenticationTlv(ISIS_GenericTlv): + name = "ISIS Authentication TLV" + fields_desc = [ByteEnumField("type", 10, _isis_tlv_names), + FieldLenField("len", None, length_of= "password", adjust=lambda pkt,x: x + 1, fmt="B"), + ByteEnumField("authtype", 1, {1: "Plain", 17: "HMAC-MD5"}), + BoundStrLenField("password", "", maxlen= 254, length_from=lambda pkt: pkt.len - 1)] + + +class ISIS_BufferSizeTlv(ISIS_GenericTlv): + name = "ISIS Buffer Size TLV" + fields_desc = [ByteEnumField("type", 14, _isis_tlv_names), + ByteField("len", 2), + ShortField("lspbuffersize", 1497)] + + + +class ISIS_ChecksumTlv(ISIS_GenericTlv): + name = "ISIS Optional Checksum TLV" + fields_desc = [ByteEnumField("type", 12, _isis_tlv_names), + ByteField("len", 2), + XShortField("checksum", None)] + + +class ISIS_DynamicHostnameTlv(ISIS_GenericTlv): + name = "ISIS Dynamic Hostname TLV" + fields_desc = [ByteEnumField("type", 137, _isis_tlv_names), + FieldLenField("len", None, length_of= "hostname", fmt="B"), + BoundStrLenField("hostname", "", length_from=lambda pkt: pkt.len)] + + + +class ISIS_GenericSubTlv(Packet): + name = "ISIS Generic Sub-TLV" + fields_desc = [ByteField("type", 0), + FieldLenField("len", None, length_of="val", fmt="B"), + BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)] + + def guess_payload_class(self, p): + return conf.padding_layer + +def _isis_guess_subtlv_cls(p, **kargs): + return ISIS_GenericSubTlv(p, **kargs) + + +class ISIS_ExtendedIpPrefix(Packet): + name = "ISIS Extended IP Prefix" + fields_desc = [IntField("metric", 1), + BitField("updown", 0, 1), + BitField("subtlvindicator", 0, 1), + BitFieldLenField("pfxlen", None, 6, length_of="pfx"), + IPPrefixField("pfx", None, wordbytes=1, length_from= lambda x: x.pfxlen), + ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), + ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)] + + def extract_padding(self, s): + return "", s + +class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv): + name = "ISIS Extended IP Reachability TLV" + fields_desc = [ByteEnumField("type", 135, _isis_tlv_names), + FieldLenField("len", None, length_of="pfxs", fmt="B"), + PacketListField("pfxs", [], ISIS_ExtendedIpPrefix, length_from= lambda pkt: pkt.len)] + + +class ISIS_ExtendedIsNeighbourEntry(Packet): + name = "ISIS Extended IS Neighbour Entry" + fields_desc = [ISIS_NodeIdField("neighbourid", "0102.0304.0506.07"), + X3BytesField("metric", 1), + FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"), + ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvslen > 0)] + + def extract_padding(self, s): + return "", s + +class ISIS_ExtendedIsReachabilityTlv(ISIS_GenericTlv): + name = "ISIS Extended IS Reachability TLV" + fields_desc = [ByteEnumField("type", 22, _isis_tlv_names), + FieldLenField("len", None, length_of="neighbours", fmt="B"), + PacketListField("neighbours", [], ISIS_ExtendedIsNeighbourEntry, length_from=lambda x: x.len)] + + +class ISIS_IpInterfaceAddressTlv(ISIS_GenericTlv): + name = "ISIS IP Interface Address TLV" + fields_desc = [ByteEnumField("type", 132, _isis_tlv_names), + FieldLenField("len", None, length_of= "addresses", fmt="B"), + FieldListField("addresses", [], IPField("", "0.0.0.0"), count_from= lambda pkt: pkt.len / 4)] + + +class ISIS_Ipv6InterfaceAddressTlv(ISIS_GenericTlv): + name = "ISIS IPv6 Interface Address TLV" + fields_desc = [ByteEnumField("type", 232, _isis_tlv_names), + FieldLenField("len", None, length_of= "addresses", fmt="B"), + IP6ListField("addresses", [], count_from= lambda pkt: pkt.len / 16)] + + + +class ISIS_Ipv6Prefix(Packet): + name = "ISIS IPv6 Prefix" + fields_desc = [IntField("metric", 1), + BitField("updown", 0, 1), + BitField("external", 0, 1), + BitField("subtlvindicator", 0, 1), + BitField("reserved", 0, 5), + FieldLenField("pfxlen", None, length_of="pfx", fmt="B"), + IP6PrefixField("pfx", None, wordbytes=1, length_from= lambda x: x.pfxlen), + ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), + ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)] + + def extract_padding(self, s): + return "", s + +class ISIS_Ipv6ReachabilityTlv(ISIS_GenericTlv): + name= "ISIS IPv6 Reachability TLV" + fields_desc = [ByteEnumField("type", 236, _isis_tlv_names), + FieldLenField("len", None, length_of= "pfxs", fmt="B"), + PacketListField("pfxs", [], ISIS_Ipv6Prefix, length_from= lambda pkt: pkt.len)] + +class ISIS_IsNeighbourTlv(ISIS_GenericTlv): + name = "ISIS IS Neighbour TLV" + fields_desc = [ByteEnumField("type", 6, _isis_tlv_names), + FieldLenField("len", None, length_of= "neighbours", fmt="B"), + FieldListField("neighbours", [], MACField("", "00.00.00.00.00.00"), count_from= lambda pkt: pkt.len / 6)] + + +class ISIS_LspEntry(Packet): + name = "ISIS LSP Entry" + fields_desc = [ShortField("lifetime", 1200), + ISIS_LspIdField("lspid", "0102.0304.0506.07-08"), + XIntField("seqnum", 0x00000001), + XShortField("checksum", None)] + + def extract_padding(self, s): + return "", s + + +class ISIS_LspEntryTlv(ISIS_GenericTlv): + name = "ISIS LSP Entry TLV" + fields_desc = [ByteEnumField("type", 9, _isis_tlv_names), + FieldLenField("len", None, length_of= "entries", fmt="B"), + PacketListField("entries", [], ISIS_LspEntry, count_from= lambda pkt: pkt.len / 16)] + + +class _AdjacencyStateTlvLenField(Field): + def i2m(self, pkt, x): + if pkt.neighbourextlocalcircuitid is not None: + return 15 + + if pkt.neighboursystemid is not None: + return 11 + + if pkt.extlocalcircuitid is not None: + return 5 + + return 1 + +class ISIS_P2PAdjacencyStateTlv(ISIS_GenericTlv): + name = "ISIS P2P Adjacency State TLV" + fields_desc = [ByteEnumField("type", 240, _isis_tlv_names), + _AdjacencyStateTlvLenField("len", None, fmt="B"), + ByteEnumField("state", "Down", {0x2 : "Down", 0x1 : "Initialising", 0x0 : "Up"}), + ConditionalField(IntField("extlocalcircuitid", None), lambda pkt: pkt.len >= 5), + ConditionalField(ISIS_SystemIdField("neighboursystemid", None), lambda pkt: pkt.len >= 11), + ConditionalField(IntField("neighbourextlocalcircuitid", None), lambda pkt: pkt.len == 15)] + +# TODO dynamically allocate sufficient size +class ISIS_PaddingTlv(ISIS_GenericTlv): + name = "ISIS Padding TLV" + fields_desc = [ByteEnumField("type", 8, _isis_tlv_names), + FieldLenField("len", None, length_of= "padding", fmt="B"), + BoundStrLenField("padding", "", length_from=lambda pkt: pkt.len)] + + +class ISIS_ProtocolsSupportedTlv(ISIS_GenericTlv): + name = "ISIS Protocols Supported TLV" + fields_desc = [ByteEnumField("type", 129, _isis_tlv_names), + FieldLenField("len", None, count_of= "nlpids", fmt="B"), + FieldListField("nlpids", [], ByteEnumField("", "IPv4", network_layer_protocol_ids), count_from= lambda pkt: pkt.len)] + + +####################################################################### +## ISIS Old-Style TLVs ## +####################################################################### + +class ISIS_IpReachabilityEntry(Packet): + name = "ISIS IP Reachability" + fields_desc = [ByteField("defmetric", 1), + ByteField("delmetric", 0x80), + ByteField("expmetric", 0x80), + ByteField("errmetric", 0x80), + IPField("ipaddress", "0.0.0.0"), + IPField("subnetmask", "255.255.255.255")] + + def extract_padding(self, s): + return "", s + +class ISIS_InternalIpReachabilityTlv(ISIS_GenericTlv): + name = "ISIS Internal IP Reachability TLV" + fields_desc = [ByteEnumField("type", 128, _isis_tlv_names), + FieldLenField("len", None, length_of= "entries", fmt="B"), + PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from= lambda x: x.len / 12)] + +class ISIS_ExternalIpReachabilityTLV(ISIS_GenericTlv): + name = "ISIS External IP Reachability TLV" + fields_desc = [ByteEnumField("type", 130, _isis_tlv_names), + FieldLenField("len", None, length_of= "entries", fmt="B"), + PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from= lambda x: x.len / 12)] + + +class ISIS_IsReachabilityEntry(Packet): + name = "ISIS IS Reachability" + fields_desc = [ByteField("defmetric", 1), + ByteField("delmetric", 0x80), + ByteField("expmetric", 0x80), + ByteField("errmetric", 0x80), + ISIS_NodeIdField("neighbourid", "0102.0304.0506.07")] + + def extract_padding(self, s): + return "", s + +class ISIS_IsReachabilityTlv(ISIS_GenericTlv): + name = "ISIS IS Reachability TLV" + fields_desc = [ByteEnumField("type", 2, _isis_tlv_names), + FieldLenField("len", None, fmt="B", length_of= "neighbours", adjust= lambda pkt,x: x+1), + ByteField("virtual", 0), + PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from= lambda x: (x.len - 1) / 11)] + + +####################################################################### +## ISIS PDU Packets ## +####################################################################### +_isis_pdu_names = { + 15: "L1 LAN Hello", + 16: "L2 LAN Hello", + 17: "P2P Hello", + 18: "L1 LSP", + 20: "L2 LSP", + 24: "L1 CSNP", + 25: "L2 CSNP", + 26: "L1 PSNP", + 27: "L2 PSNP" +} + +class ISIS_CommonHdr(Packet): + name= "ISIS Common Header" + fields_desc= [ + ByteEnumField("nlpid", 0x83, network_layer_protocol_ids), + ByteField("hdrlen", None), + ByteField("version", 1), + ByteField("idlen", 0), + ByteEnumField("pdutype", None, _isis_pdu_names), + ByteField("pduversion", 1), + ByteField("hdrreserved", 0), + ByteField("maxareaaddr", 0) + ] + + def post_build(self, pkt, pay): + # calculating checksum if requested + pdu= pkt + pay + checksumInfo= self[1].checksum_info(self.hdrlen) + + if checksumInfo is not None: + (cbegin, cpos) = checksumInfo + checkbytes= fletcher16_checkbytes(pdu[cbegin:], (cpos - cbegin)) + pdu = pdu[:cpos] + checkbytes + pdu[cpos+2:] + + return pdu + + +class _ISIS_PduBase(Packet): + def checksum_info(self, hdrlen): + checksumPosition= hdrlen + for tlv in self.tlvs: + if isinstance(tlv, ISIS_ChecksumTlv): + checksumPosition+= 2 + return (0, checksumPosition) + else: + checksumPosition+= len(tlv) + + return None + + def guess_payload_class(self, p): + return conf.padding_layer + + +class _ISIS_PduLengthField(FieldLenField): + def __init__(self): + FieldLenField.__init__(self, "pdulength", None, length_of= "tlvs", adjust= lambda pkt,x: x + pkt.underlayer.hdrlen) + + +class _ISIS_TlvListField(PacketListField): + def __init__(self): + PacketListField.__init__(self, "tlvs", [], _ISIS_GuessTlvClass, count_from= None, length_from= lambda pkt: pkt.pdulength - pkt.underlayer.hdrlen) + +class _ISIS_LAN_HelloBase(_ISIS_PduBase): + fields_desc= [ + ISIS_CircuitTypeField(), + ISIS_SystemIdField("sourceid", "0102.0304.0506"), + ShortField("holdingtime", 30), + _ISIS_PduLengthField(), + ByteField("priority", 1), + ISIS_NodeIdField("lanid", "0000.0000.0000.00"), + _ISIS_TlvListField() + ] + +class ISIS_L1_LAN_Hello(_ISIS_LAN_HelloBase): + name= "ISIS L1 LAN Hello PDU" + +class ISIS_L2_LAN_Hello(_ISIS_LAN_HelloBase): + name= "ISIS L2 LAN Hello PDU" + + +class ISIS_P2P_Hello(_ISIS_PduBase): + name= "ISIS Point-to-Point Hello PDU" + + fields_desc= [ + ISIS_CircuitTypeField(), + ISIS_SystemIdField("sourceid", "0102.0304.0506"), + ShortField("holdingtime", 30), + _ISIS_PduLengthField(), + ByteField("localcircuitid", 0), + _ISIS_TlvListField() + ] + + +class _ISIS_LSP_Base(_ISIS_PduBase): + fields_desc= [ + _ISIS_PduLengthField(), + ShortField("lifetime", 1199), + ISIS_LspIdField("lspid", "0102.0304.0506.00-00"), + XIntField("seqnum", 0x00000001), + XShortField("checksum", None), + FlagsField("typeblock", 0x03, 8, ["L1", "L2", "OL", "ADef", "ADel", "AExp", "AErr", "P"]), + _ISIS_TlvListField() + ] + + def checksum_info(self, hdrlen): + if self.checksum is not None: + return None + + return (12, 24) + +def _lsp_answers(lsp, other, clsname): + # TODO + return 0 + +class ISIS_L1_LSP(_ISIS_LSP_Base): + name= "ISIS L1 Link State PDU" + + def answers(self, other): + return _lsp_answers(self, other, "ISIS_L1_PSNP") + +class ISIS_L2_LSP(_ISIS_LSP_Base): + name= "ISIS L2 Link State PDU" + + def answers(self, other): + return _lsp_answers(self, other, "ISIS_L2_PSNP") + + +class _ISIS_CSNP_Base(_ISIS_PduBase): + fields_desc= [ + _ISIS_PduLengthField(), + ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), + ISIS_LspIdField("startlspid", "0000.0000.0000.00-00"), + ISIS_LspIdField("endlspid", "FFFF.FFFF.FFFF.FF-FF"), + _ISIS_TlvListField() + ] + +def _snp_answers(snp, other, clsname): + # TODO + return 0 + +class ISIS_L1_CSNP(_ISIS_CSNP_Base): + name= "ISIS L1 Complete Sequence Number Packet" + + def answers(self, other): + return _snp_answers(self, other, "ISIS_L1_LSP") + +class ISIS_L2_CSNP(_ISIS_CSNP_Base): + name= "ISIS L2 Complete Sequence Number Packet" + + def answers(self, other): + return _snp_answers(self, other, "ISIS_L2_LSP") + +class _ISIS_PSNP_Base(_ISIS_PduBase): + fields_desc= [ + _ISIS_PduLengthField(), + ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), + _ISIS_TlvListField() + ] + +class ISIS_L1_PSNP(_ISIS_PSNP_Base): + name= "ISIS L1 Partial Sequence Number Packet" + + def answers(self, other): + return _snp_answers(self, other, "ISIS_L1_LSP") + +class ISIS_L2_PSNP(_ISIS_PSNP_Base): + name= "ISIS L2 Partial Sequence Number Packet" + + def answers(self, other): + return _snp_answers(self, other, "ISIS_L2_LSP") + +register_cln_protocol(0x83, ISIS_CommonHdr) +bind_layers(ISIS_CommonHdr, ISIS_L1_LAN_Hello, hdrlen= 27, pdutype= 15) +bind_layers(ISIS_CommonHdr, ISIS_L2_LAN_Hello, hdrlen= 27, pdutype= 16) +bind_layers(ISIS_CommonHdr, ISIS_P2P_Hello, hdrlen= 20, pdutype= 17) +bind_layers(ISIS_CommonHdr, ISIS_L1_LSP, hdrlen= 27, pdutype= 18) +bind_layers(ISIS_CommonHdr, ISIS_L2_LSP, hdrlen= 27, pdutype= 20) +bind_layers(ISIS_CommonHdr, ISIS_L1_CSNP, hdrlen= 33, pdutype= 24) +bind_layers(ISIS_CommonHdr, ISIS_L2_CSNP, hdrlen= 33, pdutype= 25) +bind_layers(ISIS_CommonHdr, ISIS_L1_PSNP, hdrlen= 17, pdutype= 26) +bind_layers(ISIS_CommonHdr, ISIS_L2_PSNP, hdrlen= 17, pdutype= 27) \ No newline at end of file diff --git a/scapy/contrib/ospf.py b/scapy/contrib/ospf.py index a6422bd8bcaae7f8a291394eadd7c5af1ff2599e..362e467edb51469e340f071306d5c25b56b055d6 100644 --- a/scapy/contrib/ospf.py +++ b/scapy/contrib/ospf.py @@ -206,40 +206,7 @@ _OSPF_LSclasses = {1: "OSPF_Router_LSA", def ospf_lsa_checksum(lsa): - """ Fletcher checksum for OSPF LSAs, returned as a 2 byte string. - - Give the whole LSA packet as argument. - For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. - """ - # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/> - - CHKSUM_OFFSET = 16 - - if len(lsa) < CHKSUM_OFFSET: - raise Exception("LSA Packet too short (%s bytes)" % len(lsa)) - - c0 = c1 = 0 - # Calculation is done with checksum set to zero - lsa = lsa[:CHKSUM_OFFSET] + "\x00\x00" + lsa[CHKSUM_OFFSET + 2:] - for char in lsa[2:]: # leave out age - c0 += ord(char) - c1 += c0 - - c0 %= 255 - c1 %= 255 - - x = ((len(lsa) - CHKSUM_OFFSET - 1) * c0 - c1) % 255 - - if (x <= 0): - x += 255 - - y = 510 - c0 - x - - if (y > 255): - y -= 255 - #checksum = (x << 8) + y - - return chr(x) + chr(y) + return fletcher16_checkbytes("\x00\x00" + lsa[2:], 16) # leave out age class OSPF_LSA_Hdr(Packet): @@ -456,69 +423,6 @@ class OSPF_LSAck(Packet): #------------------------------------------------------------------------------ # OSPFv3 #------------------------------------------------------------------------------ -# TODO: Add length_from / adjust functionality to IP6Field and remove this class -class OspfIP6Field(StrField, IP6Field): - """ - Special IP6Field for prefix fields in OSPFv3 LSAs - """ - - def __init__(self, name, default, length=None, length_from=None): - StrField.__init__(self, name, default) - self.length_from = length_from - if length is not None: - self.length_from = lambda pkt, length = length: length - - def any2i(self, pkt, x): - return IP6Field.any2i(self, pkt, x) - - def i2repr(self, pkt, x): - return IP6Field.i2repr(self, pkt, x) - - def h2i(self, pkt, x): - return IP6Field.h2i(self, pkt, x) - - def i2m(self, pkt, x): - x = inet_pton(socket.AF_INET6, x) - l = self.length_from(pkt) - l = self.prefixlen_to_bytelen(l) - - return x[:l] - - def m2i(self, pkt, x): - l = self.length_from(pkt) - - prefixlen = self.prefixlen_to_bytelen(l) - if l > 128: - warning("OspfIP6Field: Prefix length is > 128. Dissection of this packet will fail") - else: - pad = "\x00" * (16 - prefixlen) - x += pad - - return inet_ntop(socket.AF_INET6, x) - - def prefixlen_to_bytelen(self, l): - if l <= 32: - return 4 - elif l <= 64: - return 8 - elif l <= 96: - return 12 - else: - return 16 - - def i2len(self, pkt, x): - l = self.length_from(pkt) - l = self.prefixlen_to_bytelen(l) - - return l - - def getfield(self, pkt, s): - l = self.length_from(pkt) - l = self.prefixlen_to_bytelen(l) - - return s[l:], self.m2i(pkt, s[:l]) - - class OSPFv3_Hdr(Packet): name = "OSPFv3 Header" fields_desc = [ByteField("version", 3), @@ -678,10 +582,10 @@ class OSPFv3_Inter_Area_Prefix_LSA(OSPF_BaseLSA): ShortField("len", None), ByteField("reserved", 0), X3BytesField("metric", 10), - ByteField("prefixlen", 64), + FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), OSPFv3PrefixOptionsField(), ShortField("reserved2", 0), - OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen)] + IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen)] class OSPFv3_Inter_Area_Router_LSA(OSPF_BaseLSA): @@ -694,6 +598,8 @@ class OSPFv3_Inter_Area_Router_LSA(OSPF_BaseLSA): XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), + OSPFv3OptionsField(), + ByteField("reserved2", 0), X3BytesField("metric", 1), IPField("router", "2.2.2.2")] @@ -709,10 +615,10 @@ class OSPFv3_AS_External_LSA(OSPF_BaseLSA): ShortField("len", None), FlagsField("flags", 0, 8, ["T", "F", "E"]), X3BytesField("metric", 20), - ByteField("prefixlen", 64), + FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), OSPFv3PrefixOptionsField(), ShortEnumField("reflstype", 0, _OSPFv3_LStypes), - OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen), + IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen), ConditionalField(IP6Field("fwaddr", "::"), lambda pkt: pkt.flags & 0x02 == 0x02), ConditionalField(IntField("tag", 0), lambda pkt: pkt.flags & 0x01 == 0x01), ConditionalField(IPField("reflsid", 0), lambda pkt: pkt.reflstype != 0)] @@ -725,10 +631,10 @@ class OSPFv3_Type_7_LSA(OSPFv3_AS_External_LSA): class OSPFv3_Prefix_Item(Packet): name = "OSPFv3 Link Prefix Item" - fields_desc = [ByteField("prefixlen", 64), + fields_desc = [FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), OSPFv3PrefixOptionsField(), ShortField("metric", 10), - OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen)] + IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen)] def extract_padding(self, s): return "", s @@ -746,7 +652,7 @@ class OSPFv3_Link_LSA(OSPF_BaseLSA): ByteField("prio", 1), OSPFv3OptionsField(), IP6Field("lladdr", "fe80::"), - IntField("prefixes", 0), + FieldLenField("prefixes", None, count_of="prefixlist", fmt="I"), PacketListField("prefixlist", None, OSPFv3_Prefix_Item, count_from = lambda pkt: pkt.prefixes)] @@ -760,7 +666,7 @@ class OSPFv3_Intra_Area_Prefix_LSA(OSPF_BaseLSA): XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), - ShortField("prefixes", 0), + FieldLenField("prefixes", None, count_of="prefixlist", fmt="H"), ShortEnumField("reflstype", 0, _OSPFv3_LStypes), IPField("reflsid", "0.0.0.0"), IPField("refadrouter", "0.0.0.0"), diff --git a/scapy/fields.py b/scapy/fields.py index b4b69aac6a4ead1086a36f15bbaff0b41e1592c1..50eebd0da51063855fa1bea252ff4c405944c543 100644 --- a/scapy/fields.py +++ b/scapy/fields.py @@ -412,7 +412,10 @@ class PacketListField(PacketField): def i2len(self, pkt, val): return sum( len(p) for p in val ) def do_copy(self, x): - return map(lambda p:p.copy(), x) + if x is None: + return None + else: + return map(lambda p:p.copy(), x) def getfield(self, pkt, s): c = l = None if self.length_from is not None: @@ -509,16 +512,25 @@ class StrLenField(StrField): def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) + +class BoundStrLenField(StrLenField): + def __init__(self,name, default, minlen= 0, maxlen= 255, fld=None, length_from=None): + StrLenField.__init__(self, name, default, fld, length_from) + self.minlen= minlen + self.maxlen= maxlen + + def randval(self): + return RandBin(RandNum(self.minlen, self.maxlen)) class FieldListField(Field): islist=1 def __init__(self, name, default, field, length_from=None, count_from=None): if default is None: default = [] # Create a new list for each instance + self.field = field Field.__init__(self, name, default) self.count_from = count_from self.length_from = length_from - self.field = field def i2count(self, pkt, val): if type(val) is list: @@ -533,9 +545,11 @@ class FieldListField(Field): return val def any2i(self, pkt, x): if type(x) is not list: - return [x] + return [self.field.any2i(pkt, x)] else: - return x + return map(lambda e, pkt=pkt: self.field.any2i(pkt, e), x) + def i2repr(self, pkt, x): + return map(lambda e, pkt=pkt: self.field.i2repr(pkt,e), x) def addfield(self, pkt, s, val): val = self.i2m(pkt, val) for v in val: @@ -916,3 +930,76 @@ class FixedPointField(BitField): return int_part+frac_part def i2repr(self, pkt, val): return self.i2h(pkt, val) + + +# Base class for IPv4 and IPv6 Prefixes inspired by IPField and IP6Field. +# Machine values are encoded in a multiple of wordbytes bytes. +class _IPPrefixFieldBase(Field): + def __init__(self, name, default, wordbytes, maxbytes, aton, ntoa, length_from): + self.wordbytes= wordbytes + self.maxbytes= maxbytes + self.aton= aton + self.ntoa= ntoa + Field.__init__(self, name, default, "%is" % self.maxbytes) + self.length_from= length_from + + def _numbytes(self, pfxlen): + wbits= self.wordbytes * 8 + return ((pfxlen + (wbits - 1)) / wbits) * self.wordbytes + + def h2i(self, pkt, x): + # "fc00:1::1/64" -> ("fc00:1::1", 64) + [pfx,pfxlen]= x.split('/') + self.aton(pfx) # check for validity + return (pfx, int(pfxlen)) + + + def i2h(self, pkt, x): + # ("fc00:1::1", 64) -> "fc00:1::1/64" + (pfx,pfxlen)= x + return "%s/%i" % (pfx,pfxlen) + + def i2m(self, pkt, x): + # ("fc00:1::1", 64) -> ("\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64) + (pfx,pfxlen)= x + s= self.aton(pfx); + return (s[:self._numbytes(pfxlen)], pfxlen) + + def m2i(self, pkt, x): + # ("\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64) -> ("fc00:1::1", 64) + (s,pfxlen)= x + + if len(s) < self.maxbytes: + s= s + ("\0" * (self.maxbytes - len(s))) + return (self.ntoa(s), pfxlen) + + def any2i(self, pkt, x): + if x is None: + return (self.ntoa("\0"*self.maxbytes), 1) + + return self.h2i(pkt,x) + + def i2len(self, pkt, x): + (_,pfxlen)= x + return pfxlen + + def addfield(self, pkt, s, val): + (rawpfx,pfxlen)= self.i2m(pkt,val) + fmt= "!%is" % self._numbytes(pfxlen) + return s+struct.pack(fmt, rawpfx) + + def getfield(self, pkt, s): + pfxlen= self.length_from(pkt) + numbytes= self._numbytes(pfxlen) + fmt= "!%is" % numbytes + return s[numbytes:], self.m2i(pkt, (struct.unpack(fmt, s[:numbytes])[0], pfxlen)) + + +class IPPrefixField(_IPPrefixFieldBase): + def __init__(self, name, default, wordbytes=1, length_from= None): + _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 4, inet_aton, inet_ntoa, length_from) + + +class IP6PrefixField(_IPPrefixFieldBase): + def __init__(self, name, default, wordbytes= 1, length_from= None): + _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 16, lambda a: inet_pton(socket.AF_INET6, a), lambda n: inet_ntop(socket.AF_INET6, n), length_from) \ No newline at end of file diff --git a/scapy/layers/clns.py b/scapy/layers/clns.py new file mode 100644 index 0000000000000000000000000000000000000000..37a2f51748d2f6f608416fb7b66c1d4a1c008603 --- /dev/null +++ b/scapy/layers/clns.py @@ -0,0 +1,86 @@ +""" + CLNS Extension + ~~~~~~~~~~~~~~~~~~~~~ + + :copyright: 2014 BENOCS GmbH, Berlin (Germany) + :author: Marcel Patzlaff, mpatzlaff@benocs.com + :license: GPLv2 + + This module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + :description: + + This module provides a layer and registration function for + OSI Connectionless-mode Network Services (such as IS-IS). + + :TODO: + + - rework this if a better way is found/implemented to bind + protocols such as IS-IS (or if IS-IS remains the sole CLN + protocol) + +""" +import struct + +from scapy.config import conf +from scapy.fields import ByteEnumField, PacketField +from scapy.layers.l2 import LLC +from scapy.packet import Packet, bind_layers + + +network_layer_protocol_ids= { + 0x00 : "Null", + 0x08 : "Q.933", + 0x80 : "IEEE SNAP", + 0x81 : "ISO 8438 CLNP", + 0x82 : "ISO 9542 ES-IS", + 0x83 : "ISO 10589 IS-IS", + 0x8E : "IPv6", + 0xB0 : "FRF.9", + 0xB1 : "FRF.12", + 0xC0 : "TRILL", + 0xC1 : "IEEE 802.aq", + 0xCC : "IPv4", + 0xCF : "PPP" +} + + +_cln_protocols= {} + +class _GenericClnsPdu(Packet): + name= "Generic CLNS PDU" + fields_desc= [ + ByteEnumField("nlpid", 0x00, network_layer_protocol_ids), + PacketField("rawdata", None, conf.raw_layer) + ] + + +class ConnectionlessNetworkService(Packet): + name= "Connectionless-mode Network Service" + + def guess_payload_class(self, p): + cls= conf.raw_layer + + if len(p) >= 1: + nlpid = struct.unpack("!B", p[0])[0] + cls= _cln_protocols.get(nlpid, _GenericClnsPdu) + + return cls + +@conf.commands.register +def register_cln_protocol(nlpid, cln_protocol_class): + if nlpid is None or cln_protocol_class is None: + return + + _cln_protocols[nlpid]= cln_protocol_class + + +bind_layers(LLC, ConnectionlessNetworkService, dsap=0xfe, ssap=0xfe, ctrl=3) \ No newline at end of file diff --git a/scapy/utils.py b/scapy/utils.py index 07b00ab4e877f86c1693e3de390af24632300510..e4613094b8949e1054c785dc3ab56987c8c915c5 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -237,6 +237,60 @@ else: s = ~s return (((s>>8)&0xff)|s<<8) & 0xffff + +def _fletcher16(charbuf): + # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/> + c0 = c1 = 0 + for char in charbuf: + c0 += ord(char) + c1 += c0 + + c0 %= 255 + c1 %= 255 + return (c0,c1) + +@conf.commands.register +def fletcher16_checksum(binbuf): + """ Calculates Fletcher-16 checksum of the given buffer. + + Note: + If the buffer contains the two checkbytes derived from the Fletcher-16 checksum + the result of this function has to be 0. Otherwise the buffer has been corrupted. + """ + (c0,c1)= _fletcher16(binbuf) + return (c1 << 8) | c0 + + +@conf.commands.register +def fletcher16_checkbytes(binbuf, offset): + """ Calculates the Fletcher-16 checkbytes returned as 2 byte binary-string. + + Including the bytes into the buffer (at the position marked by offset) the + global Fletcher-16 checksum of the buffer will be 0. Thus it is easy to verify + the integrity of the buffer on the receiver side. + + For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. + """ + + # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/> + if len(binbuf) < offset: + raise Exception("Packet too short for checkbytes %d" % len(binbuf)) + + binbuf = binbuf[:offset] + "\x00\x00" + binbuf[offset + 2:] + (c0,c1)= _fletcher16(binbuf) + + x = ((len(binbuf) - offset - 1) * c0 - c1) % 255 + + if (x <= 0): + x += 255 + + y = 510 - c0 - x + + if (y > 255): + y -= 255 + return chr(x) + chr(y) + + def warning(x): log_runtime.warning(x)