def unpack_message(cls, data, negotiated): # pylint: disable=W0613 what = Type(unpack('!H', data[0:2])[0]) length = unpack('!H', data[2:4])[0] decode, klass = cls.registered_operational.get(what, ('unknown', None)) if decode == 'advisory': afi = unpack('!H', data[4:6])[0] safi = ord_(data[6]) data = data[7:length + 4] return klass(afi, safi, data) elif decode == 'query': afi = unpack('!H', data[4:6])[0] safi = ord_(data[6]) routerid = RouterID.unpack(data[7:11]) sequence = unpack('!L', data[11:15])[0] return klass(afi, safi, routerid, sequence) elif decode == 'counter': afi = unpack('!H', data[4:6])[0] safi = ord_(data[6]) routerid = RouterID.unpack(data[7:11]) sequence = unpack('!L', data[11:15])[0] counter = unpack('!L', data[15:19])[0] return klass(afi, safi, routerid, sequence, counter) else: print('ignoring ATM this kind of message')
def unpack_message (cls, data, negotiated): # pylint: disable=W0613 what = Type(unpack('!H',data[0:2])[0]) length = unpack('!H',data[2:4])[0] decode,klass = cls.registered_operational.get(what,('unknown',None)) if decode == 'advisory': afi = unpack('!H',data[4:6])[0] safi = ord_(data[6]) data = data[7:length+4] return klass(afi,safi,data) elif decode == 'query': afi = unpack('!H',data[4:6])[0] safi = ord_(data[6]) routerid = RouterID.unpack(data[7:11]) sequence = unpack('!L',data[11:15])[0] return klass(afi,safi,routerid,sequence) elif decode == 'counter': afi = unpack('!H',data[4:6])[0] safi = ord_(data[6]) routerid = RouterID.unpack(data[7:11]) sequence = unpack('!L',data[11:15])[0] counter = unpack('!L',data[15:19])[0] return klass(afi,safi,routerid,sequence,counter) else: print('ignoring ATM this kind of message')
def unpack(data, negotiated=None): # 30/02/12 Quagga communities for soo and rt are not transitive when 4360 says they must be, hence the & 0x0FFF community = (ord_(data[0]) & 0x0F, ord_(data[1])) if community in ExtendedCommunity.registered_extended: return ExtendedCommunity.registered_extended[community].unpack( data) return ExtendedCommunity(data)
def _new_aspaths(cls, data, asn4, klass=None): as_set = [] as_seq = [] as_cset = [] as_cseq = [] backup = data unpacker = { False: '!H', True: '!L', } size = { False: 2, True: 4, } as_choice = { ASPath.AS_SEQUENCE: as_seq, ASPath.AS_SET: as_set, ASPath.AS_CONFED_SEQUENCE: as_cseq, ASPath.AS_CONFED_SET: as_cset, } upr = unpacker[asn4] length = size[asn4] try: while data: stype = ord_(data[0]) slen = ord_(data[1]) if stype not in (ASPath.AS_SET, ASPath.AS_SEQUENCE, ASPath.AS_CONFED_SEQUENCE, ASPath.AS_CONFED_SET): raise Notify(3, 11, 'invalid AS Path type sent %d' % stype) end = 2 + (slen * length) sdata = data[2:end] data = data[end:] # Eat the data and ignore it if the ASPath attribute is know known asns = as_choice.get(stype, []) for _ in range(slen): asn = unpack(upr, sdata[:length])[0] asns.append(ASN(asn)) sdata = sdata[length:] except IndexError: raise Notify(3, 11, 'not enough data to decode AS_PATH or AS4_PATH') except error: # struct raise Notify(3, 11, 'not enough data to decode AS_PATH or AS4_PATH') if klass: return klass(as_seq, as_set, as_cseq, as_cset, backup) return cls(as_seq, as_set, as_cseq, as_cset, backup)
def flag_attribute_content (data): flag = Attribute.Flag(ord_(data[0])) attr = Attribute.CODE(ord_(data[1])) if flag & Attribute.Flag.EXTENDED_LENGTH: length = unpack('!H',data[2:4])[0] return flag, attr, data[4:length+4] else: length = ord_(data[2]) return flag, attr, data[3:length+3]
def flag_attribute_content(data): flag = Attribute.Flag(ord_(data[0])) attr = Attribute.CODE(ord_(data[1])) if flag & Attribute.Flag.EXTENDED_LENGTH: length = unpack('!H', data[2:4])[0] return flag, attr, data[4:length + 4] else: length = ord_(data[2]) return flag, attr, data[3:length + 3]
def _key_values (name, data): if len(data) < 2: raise Notify(2,0,"Bad length for OPEN %s (<2) %s" % (name,Capability.hex(data))) l = ord_(data[1]) boundary = l+2 if len(data) < boundary: raise Notify(2,0,"Bad length for OPEN %s (buffer underrun) %s" % (name,Capability.hex(data))) key = ord_(data[0]) value = data[2:boundary] rest = data[boundary:] return key,value,rest
def _new_aspaths (cls, data, asn4, klass=None): as_set = [] as_seq = [] as_cset = [] as_cseq = [] backup = data unpacker = { False: '!H', True: '!L', } size = { False: 2, True: 4, } as_choice = { ASPath.AS_SEQUENCE: as_seq, ASPath.AS_SET: as_set, ASPath.AS_CONFED_SEQUENCE: as_cseq, ASPath.AS_CONFED_SET: as_cset, } upr = unpacker[asn4] length = size[asn4] try: while data: stype = ord_(data[0]) slen = ord_(data[1]) if stype not in (ASPath.AS_SET, ASPath.AS_SEQUENCE, ASPath.AS_CONFED_SEQUENCE, ASPath.AS_CONFED_SET): raise Notify(3,11,'invalid AS Path type sent %d' % stype) end = 2+(slen*length) sdata = data[2:end] data = data[end:] # Eat the data and ignore it if the ASPath attribute is know known asns = as_choice.get(stype,[]) for _ in range(slen): asn = unpack(upr,sdata[:length])[0] asns.append(ASN(asn)) sdata = sdata[length:] except IndexError: raise Notify(3,11,'not enough data to decode AS_PATH or AS4_PATH') except error: # struct raise Notify(3,11,'not enough data to decode AS_PATH or AS4_PATH') if klass: return klass(as_seq,as_set,as_cseq,as_cset,backup) return cls(as_seq,as_set,as_cseq,as_cset,backup)
def unpack_nlri (cls, afi, safi, bgp, action, addpath): length,bgp = ord_(bgp[0]),bgp[1:] if length & 0xF0 == 0xF0: # bigger than 240 extra,bgp = ord_(bgp[0]),bgp[1:] length = ((length & 0x0F) << 16) + extra if length > len(bgp): raise Notify(3,10,'invalid length at the start of the the flow') over = bgp[length:] bgp = bgp[:length] nlri = cls(afi,safi,action) if safi == SAFI.flow_vpn: nlri.rd = RouteDistinguisher(bgp[:8]) bgp = bgp[8:] seen = [] while bgp: what,bgp = ord_(bgp[0]),bgp[1:] if what not in decode.get(afi,{}): raise Notify(3,10,'unknown flowspec component received for address family %d' % what) seen.append(what) if sorted(seen) != seen: raise Notify(3,10,'components are not sent in the right order %s' % seen) decoded = decode[afi][what] klass = factory[afi][what] if decoded == 'prefix': adding,bgp = klass.make(bgp) if not nlri.add(adding): raise Notify(3,10,'components are incompatible (two sources, two destinations, mix ipv4/ipv6) %s' % seen) # logger.parser(LazyFormat("added flow %s (%s) payload " % (klass.NAME,adding),bgp[:-len(left)])) else: end = False while not end: byte,bgp = ord_(bgp[0]),bgp[1:] end = CommonOperator.eol(byte) operator = CommonOperator.operator(byte) length = CommonOperator.length(byte) value,bgp = bgp[:length],bgp[length:] adding = klass.decoder(value) nlri.add(klass(operator,adding)) # logger.parser(LazyFormat("added flow %s (%s) operator %d len %d payload " % (klass.NAME,adding,byte,length),value)) return nlri, bgp+over
def unpack(cls, data): datalen = len(data) rd = RouteDistinguisher.unpack(data[:8]) esi = ESI.unpack(data[8:18]) etag = EthernetTag.unpack(data[18:22]) maclength = ord_(data[22]) if (maclength > 48 or maclength < 0): raise Notify(3, 5, 'invalid MAC Address length in %s' % cls.NAME) end = 23 + 6 # MAC length MUST be 6 mac = MACQUAL.unpack(data[23:end]) length = ord_(data[end]) iplen = length / 8 if datalen in [33, 36]: # No IP information (1 or 2 labels) iplenUnpack = 0 if iplen != 0: raise Notify( 3, 5, "IP length is given as %d, but current MAC route has no IP information" % iplen) elif datalen in [37, 40]: # Using IPv4 addresses (1 or 2 labels) iplenUnpack = 4 if (iplen > 32 or iplen < 0): raise Notify( 3, 5, "IP field length is given as %d, but current MAC route is IPv4 and valus is out of range" % iplen) elif datalen in [49, 52]: # Using IPv6 addresses (1 or 2 labels) iplenUnpack = 16 if (iplen > 128 or iplen < 0): raise Notify( 3, 5, "IP field length is given as %d, but current MAC route is IPv6 and valus is out of range" % iplen) else: raise Notify( 3, 5, "Data field length is given as %d, but does not match one of the expected lengths" % datalen) payload = data[end + 1:end + 1 + iplenUnpack] if payload: ip = IP.unpack(data[end + 1:end + 1 + iplenUnpack]) else: ip = None label = Labels.unpack(data[end + 1 + iplenUnpack:end + 1 + iplenUnpack + 3]) return cls(rd, esi, etag, mac, maclength, label, ip, data)
def unpack_nlri(cls, afi, safi, bgp, action, addpath): code = ord_(bgp[0]) length = ord_(bgp[1]) if code in cls.registered_evpn: klass = cls.registered_evpn[code].unpack(bgp[2:length + 2]) else: klass = GenericEVPN(code, bgp[2:length + 2]) klass.CODE = code klass.action = action klass.addpath = addpath return klass, bgp[length + 2:]
def unpack_nlri (cls, afi, safi, bgp, action, addpath): code = ord_(bgp[0]) length = ord_(bgp[1]) if code in cls.registered_evpn: klass = cls.registered_evpn[code].unpack(bgp[2:length+2]) else: klass = GenericEVPN(code,bgp[2:length+2]) klass.CODE = code klass.action = action klass.addpath = addpath return klass,bgp[length+2:]
def unpack (data): def _key_values (name, data): if len(data) < 2: raise Notify(2,0,"Bad length for OPEN %s (<2) %s" % (name,Capability.hex(data))) l = ord_(data[1]) boundary = l+2 if len(data) < boundary: raise Notify(2,0,"Bad length for OPEN %s (buffer underrun) %s" % (name,Capability.hex(data))) key = ord_(data[0]) value = data[2:boundary] rest = data[boundary:] return key,value,rest capabilities = Capabilities() option_len = ord_(data[0]) if not option_len: return capabilities data = data[1:option_len+1] while data: key,value,data = _key_values('parameter',data) # Paramaters must only be sent once. if key == Parameter.AUTHENTIFICATION_INFORMATION: raise Notify(2,5) if key == Parameter.CAPABILITIES: while value: capability,capv,value = _key_values('capability',value) capabilities[capability] = Capability.unpack(capability,capabilities,capv) else: raise Notify(2,0,'Unknow OPEN parameter %s' % hex(key)) return capabilities
def unpack (cls, exdata): data = exdata # Get the data length to understand if addresses are IPv4 or IPv6 datalen = len(data) rd = RouteDistinguisher.unpack(data[:8]) data = data[8:] esi = ESI.unpack(data[:10]) data = data[10:] etag = EthernetTag.unpack(data[:4]) data = data[4:] iplen = ord_(data[0]) data = data[1:] if datalen == (26 + 8): # Using IPv4 addresses ip = IP.unpack(data[:4]) data = data[4:] gwip = IP.unpack(data[:4]) data = data[4:] elif datalen == (26 + 32): # Using IPv6 addresses ip = IP.unpack(data[:16]) data = data[16:] gwip = IP.unpack(data[:16]) data = data[16:] else: raise Notify(3,5,"Data field length is given as %d, but EVPN route currently support only IPv4 or IPv6(34 or 58)" % datalen) label = Labels.unpack(data[:3]) return cls(rd,esi,etag,label,ip,iplen,gwip,exdata)
def unpack (data, negotiated): communities = ExtendedCommunities() while data: if data and len(data) < 8: raise Notify(3,1,'could not decode extended community %s' % str([hex(ord_(_)) for _ in data])) communities.add(ExtendedCommunity.unpack(data[:8],negotiated)) data = data[8:] return communities
def decode (afi,bgp): mask = ord_(bgp[0]) size = CIDR.size(mask) if len(bgp) < size+1: raise Notify(3,10,'could not decode CIDR') return bgp[1:size+1] + padding(IP.length(afi)-size), mask
def pack (self, negotiated=None): # XXX: no support for addpath yet # We reset ext com flag bits from the first byte in the packed RT # because in an RTC route these flags never appear. if self.rt: packedRT = self.rt.pack() return pack("!BLB", len(self), self.origin, ord_(RTC.resetFlags(packedRT[0]))) + packedRT[1:] return pack("!B",0)
def unpack (cls, data): rd = RouteDistinguisher.unpack(data[:8]) etag = EthernetTag.unpack(data[8:12]) iplen = ord_(data[12]) if iplen not in (4*8,16*8): raise Exception("IP len is %d, but EVPN route currently support only IPv4" % iplen) ip = IP.unpack(data[13:13+iplen//8]) return cls(rd,etag,ip,data)
def pack(self, negotiated=None): # XXX: no support for addpath yet # We reset ext com flag bits from the first byte in the packed RT # because in an RTC route these flags never appear. if self.rt: packedRT = self.rt.pack() return pack("!BLB", len(self), self.origin, ord_(RTC.resetFlags(packedRT[0]))) + packedRT[1:] return pack("!B", 0)
def unpack_capability (instance, data, capability=None): # pylint: disable=W0613 # XXX: FIXME: should check that we have not yet seen the capability while data: afi = AFI.unpack(data[:2]) safi = SAFI.unpack(data[2]) sr = ord_(data[3]) instance.add_path(afi,safi,sr) data = data[4:] return instance
def unpack(data, negotiated): communities = ExtendedCommunities() while data: if data and len(data) < 8: raise Notify( 3, 1, 'could not decode extended community %s' % str([hex(ord_(_)) for _ in data])) communities.add(ExtendedCommunity.unpack(data[:8], negotiated)) data = data[8:] return communities
def unpack(data, negotiated): communities = Communities() while data: if data and len(data) < 4: raise Notify( 3, 1, 'could not decode community %s' % str([hex(ord_(_)) for _ in data])) communities.add(Community.unpack(data[:4], negotiated)) data = data[4:] return communities
def unpack_nlri(cls, afi, safi, bgp, action, addpath): nlri = cls(afi, safi, action) if addpath: nlri.path_info = PathInfo(bgp[:4]) bgp = bgp[4:] mask = ord_(bgp[0]) bgp = bgp[1:] if cls.has_label(): labels = [] while bgp and mask >= 8: label = int(unpack('!L', chr_(0) + bgp[:3])[0]) bgp = bgp[3:] mask -= 24 # 3 bytes # The last 4 bits are the bottom of Stack # The last bit is set for the last label labels.append(label >> 4) # This is a route withdrawal if label == 0x800000 and action == IN.WITHDRAWN: break # This is a next-hop if label == 0x000000: break if label & 1: break nlri.labels = Labels(labels) if cls.has_rd(): mask -= 8 * 8 # the 8 bytes of the route distinguisher rd = bgp[:8] bgp = bgp[8:] nlri.rd = RouteDistinguisher(rd) if mask < 0: raise Notify(3, 10, 'invalid length in NLRI prefix') if not bgp and mask: raise Notify( 3, 10, 'not enough data for the mask provided to decode the NLRI') size = CIDR.size(mask) if len(bgp) < size: raise Notify( 3, 10, 'could not decode route with AFI %d sand SAFI %d' % (afi, safi)) network, bgp = bgp[:size], bgp[size:] nlri.cidr = CIDR(network + padding(IP.length(afi) - size), mask) return nlri, bgp
def unpack (data, negotiated): large_communities = LargeCommunities() while data: if data and len(data) < 12: raise Notify(3,1,'could not decode large community %s' % str([hex(ord_(_)) for _ in data])) lc = LargeCommunity.unpack(data[:12],negotiated) data = data[12:] if lc in large_communities.communities: continue large_communities.add(lc) return large_communities
def unpack (cls, data): rd = RouteDistinguisher.unpack(data[:8]) esi = ESI.unpack(data[8:18]) iplen = ord_(data[18]) if iplen not in (32,128): raise Notify(3,5,"IP length field is given as %d in current Segment, expecting 32 (IPv4) or 128 (IPv6) bits" % iplen) ip = IP.unpack(data[19:19+(iplen/8)]) return cls(rd,esi,ip,data)
def unpack(data, negotiated): large_communities = LargeCommunities() while data: if data and len(data) < 12: raise Notify( 3, 1, 'could not decode large community %s' % str([hex(ord_(_)) for _ in data])) lc = LargeCommunity.unpack(data[:12], negotiated) data = data[12:] if lc in large_communities.communities: continue large_communities.add(lc) return large_communities
def unpack (cls, data): datalen = len(data) rd = RouteDistinguisher.unpack(data[:8]) esi = ESI.unpack(data[8:18]) etag = EthernetTag.unpack(data[18:22]) maclength = ord_(data[22]) if (maclength > 48 or maclength < 0): raise Notify(3,5,'invalid MAC Address length in %s' % cls.NAME) end = 23 + 6 # MAC length MUST be 6 mac = MACQUAL.unpack(data[23:end]) length = ord_(data[end]) iplen = length / 8 if datalen in [33,36]: # No IP information (1 or 2 labels) iplenUnpack = 0 if iplen != 0: raise Notify(3,5,"IP length is given as %d, but current MAC route has no IP information" % iplen) elif datalen in [37, 40]: # Using IPv4 addresses (1 or 2 labels) iplenUnpack = 4 if (iplen > 32 or iplen < 0): raise Notify(3,5,"IP field length is given as %d, but current MAC route is IPv4 and valus is out of range" % iplen) elif datalen in [49, 52]: # Using IPv6 addresses (1 or 2 labels) iplenUnpack = 16 if (iplen > 128 or iplen < 0): raise Notify(3,5,"IP field length is given as %d, but current MAC route is IPv6 and valus is out of range" % iplen) else: raise Notify(3,5,"Data field length is given as %d, but does not match one of the expected lengths" % datalen) payload = data[end+1:end+1+iplenUnpack] if payload: ip = IP.unpack(data[end+1:end+1+iplenUnpack]) else: ip = None label = Labels.unpack(data[end+1+iplenUnpack:end+1+iplenUnpack+3]) return cls(rd,esi,etag,mac,maclength,label,ip,data)
def unpack_nlri (cls, afi, safi, bgp, action, addpath): nlri = cls(afi,safi,action) if addpath: nlri.path_info = PathInfo(bgp[:4]) bgp = bgp[4:] mask = ord_(bgp[0]) bgp = bgp[1:] if cls.has_label(): labels = [] while bgp and mask >= 8: label = int(unpack('!L',chr_(0) + bgp[:3])[0]) bgp = bgp[3:] mask -= 24 # 3 bytes # The last 4 bits are the bottom of Stack # The last bit is set for the last label labels.append(label >> 4) # This is a route withdrawal if label == 0x800000 and action == IN.WITHDRAWN: break # This is a next-hop if label == 0x000000: break if label & 1: break nlri.labels = Labels(labels) if cls.has_rd(): mask -= 8*8 # the 8 bytes of the route distinguisher rd = bgp[:8] bgp = bgp[8:] nlri.rd = RouteDistinguisher(rd) if mask < 0: raise Notify(3,10,'invalid length in NLRI prefix') if not bgp and mask: raise Notify(3,10,'not enough data for the mask provided to decode the NLRI') size = CIDR.size(mask) if len(bgp) < size: raise Notify(3,10,'could not decode route with AFI %d and SAFI %d' % (afi,safi)) network,bgp = bgp[:size],bgp[size:] nlri.cidr = CIDR(network + padding(IP.length(afi)-size),mask) return nlri,bgp
def unpack(cls, data): rd = RouteDistinguisher.unpack(data[:8]) esi = ESI.unpack(data[8:18]) iplen = ord_(data[18]) if iplen not in (32, 128): raise Notify( 3, 5, "IP field length is given as %d in current Segment, expecting 32 (IPv4) or 128 (IPv6) bits" % iplen) ip = IP.unpack(data[19:19 + (iplen / 8)]) return cls(rd, esi, ip, data)
def unpack_capability (instance, data, capability=None): # pylint: disable=W0613 # XXX: FIXME: should raise if instance was already setup restart = unpack('!H',data[:2])[0] restart_flag = restart >> 12 restart_time = restart & Graceful.TIME_MASK data = data[2:] families = [] while data: afi = AFI.unpack(data[:2]) safi = SAFI.unpack(data[2]) flag_family = ord_(data[3]) families.append((afi,safi,flag_family)) data = data[4:] return instance.set(restart_flag,restart_time,families)
def unpack_message (cls, data, _=None): version = ord_(data[0]) if version != 4: # Only version 4 is supported nowdays.. raise Notify(2,1,data[0]) asn = unpack('!H',data[1:3])[0] hold_time = unpack('!H',data[3:5])[0] numeric = unpack('!L',data[5:9])[0] router_id = "%d.%d.%d.%d" % (numeric >> 24,(numeric >> 16) & 0xFF,(numeric >> 8) & 0xFF,numeric & 0xFF) return cls( Version(version), ASN(asn), HoldTime(hold_time), RouterID(router_id), Capabilities.unpack(data[9:]) )
def unpack_nlri(cls, afi, safi, bgp, action, addpath): length = ord_(bgp[0]) if length == 0: return cls(afi, safi, action, ASN(0), None), bgp[1:] if length < 8 * 4: raise Exception("incorrect RT lenght: %d (should be >=32,<=96)" % length) # We are reseting the flags on the RouteTarget extended # community, because they do not make sense for an RTC route return cls(afi, safi, action, ASN(unpack('!L', bgp[1:5])[0]), RouteTarget.unpack(RTC.resetFlags(bgp[5]) + bgp[6:13])), bgp[13:]
def __init__(self, code, subcode, data=b''): self.code = code self.subcode = subcode if not (code, subcode) in [(6, 2), (6, 4)]: self.data = data if not len( [_ for _ in data if _ not in string.printable]) else hexstring(data) return if len(data) == 0: # shutdown without shutdown communication (the old fashioned way) self.data = b'' return # draft-ietf-idr-shutdown or the peer was using 6,2 with data shutdown_length = ord_(data[0]) data = data[1:] if shutdown_length == 0: self.data = "empty Shutdown Communication." # move offset past length field return if len(data) < shutdown_length: self.data = "invalid Shutdown Communication (buffer underrun) length : %i [%s]" % ( shutdown_length, hexstring(data)) return if shutdown_length > 128: self.data = "invalid Shutdown Communication (too large) length : %i [%s]" % ( shutdown_length, hexstring(data)) return try: self.data = 'Shutdown Communication: "%s"' % \ data[:shutdown_length].decode('utf-8').replace('\r',' ').replace('\n',' ') except UnicodeDecodeError: self.data = "invalid Shutdown Communication (invalid UTF-8) length : %i [%s]" % ( shutdown_length, hexstring(data)) return trailer = data[shutdown_length:] if trailer: self.data += ", trailing data: " + hexstring(trailer)
def unpack_nlri (cls, afi, safi, bgp, action, addpath): length = ord_(bgp[0]) if length == 0: return cls(afi,safi,action,ASN(0),None),bgp[1:] if length < 8*4: raise Exception("incorrect RT length: %d (should be >=32,<=96)" % length) # We are reseting the flags on the RouteTarget extended # community, because they do not make sense for an RTC route return cls( afi, safi, action, ASN(unpack('!L', bgp[1:5])[0]), RouteTarget.unpack( RTC.resetFlags(bgp[5])+bgp[6:13] ) ),bgp[13:]
def __init__ (self, code, subcode, data=b''): self.code = code self.subcode = subcode if not (code, subcode) in [(6, 2), (6, 4)]: self.data = data if not len([_ for _ in data if _ not in string.printable]) else hexstring(data) return if len(data) == 0: # shutdown without shutdown communication (the old fashioned way) self.data = b'' return # draft-ietf-idr-shutdown or the peer was using 6,2 with data shutdown_length = ord_(data[0]) data = data[1:] if shutdown_length == 0: self.data = "empty Shutdown Communication." # move offset past length field return if len(data) < shutdown_length: self.data = "invalid Shutdown Communication (buffer underrun) length : %i [%s]" % (shutdown_length, hexstring(data)) return if shutdown_length > 128: self.data = "invalid Shutdown Communication (too large) length : %i [%s]" % (shutdown_length, hexstring(data)) return try: self.data = 'Shutdown Communication: "%s"' % \ data[:shutdown_length].decode('utf-8').replace('\r',' ').replace('\n',' ') except UnicodeDecodeError: self.data = "invalid Shutdown Communication (invalid UTF-8) length : %i [%s]" % (shutdown_length, hexstring(data)) return trailer = data[shutdown_length:] if trailer: self.data += ", trailing data: " + hexstring(trailer)
def unpack(cls, exdata): data = exdata # Get the data length to understand if addresses are IPv4 or IPv6 datalen = len(data) rd = RouteDistinguisher.unpack(data[:8]) data = data[8:] esi = ESI.unpack(data[:10]) data = data[10:] etag = EthernetTag.unpack(data[:4]) data = data[4:] iplen = ord_(data[0]) data = data[1:] if datalen == (26 + 8): # Using IPv4 addresses ip = IP.unpack(data[:4]) data = data[4:] gwip = IP.unpack(data[:4]) data = data[4:] elif datalen == (26 + 32): # Using IPv6 addresses ip = IP.unpack(data[:16]) data = data[16:] gwip = IP.unpack(data[:16]) data = data[16:] else: raise Notify( 3, 5, "Data field length is given as %d, but EVPN route currently support only IPv4 or IPv6(34 or 58)" % datalen) label = Labels.unpack(data[:3]) return cls(rd, esi, etag, label, ip, iplen, gwip, exdata)
def __str__ (self): return "evpn:%s:%s" % (self.registered_evpn.get(self.CODE,self).SHORT_NAME.lower(),'0x' + ''.join('%02x' % ord_(_) for _ in self._packed))
def unpack (data, negotiated=None): # 30/02/12 Quagga communities for soo and rt are not transitive when 4360 says they must be, hence the & 0x0FFF community = (ord_(data[0]) & 0x0F,ord_(data[1])) if community in ExtendedCommunity.registered_extended: return ExtendedCommunity.registered_extended[community].unpack(data) return ExtendedCommunity(data)
def __repr__(self): return '0x' + ''.join('%02x' % ord_(_) for _ in self.data)
def __str__(self): return ':'.join('%02X' % ord_(_) for _ in self._packed)
def unpack (cls, data, negotiated): return cls(ord_(data),data)
def resetFlags(char): return chr_( ord_(char) & ~(Attribute.Flag.TRANSITIVE | Attribute.Flag.OPTIONAL))
def hex(data): return '0x' + b''.join('%02x' % ord_(_) for _ in data)
def resetFlags(char): return chr_(ord_(char) & ~(Attribute.Flag.TRANSITIVE | Attribute.Flag.OPTIONAL))
def __str__(self): return "evpn:%s:%s" % (self.registered_evpn.get( self.CODE, self).SHORT_NAME.lower(), '0x' + ''.join('%02x' % ord_(_) for _ in self._packed))
def prettytunnel (self): return "0x" + ''.join('%02X' % ord_(_) for _ in self.tunnel) if self.tunnel else ''
def _raw (self): return ''.join('%02X' % ord_(_) for _ in self.pack())
def __repr__ (self): return '0x' + ''.join('%02x' % ord_(_) for _ in self.data)
def json (self): h = 0x00 for byte in self.community: h <<= 8 h += ord_(byte) return "%ld" % h
def unpack (cls, data, negotiated): nlris = [] # -- Reading AFI/SAFI afi,safi = unpack('!HB',data[:3]) offset = 3 # we do not want to accept unknown families if negotiated and (afi,safi) not in negotiated.families: raise Notify(3,0,'presented a non-negotiated family %d/%d' % (afi,safi)) # -- Reading length of next-hop len_nh = ord_(data[offset]) offset += 1 rd = 0 # check next-hop size if afi == AFI.ipv4: if safi in (SAFI.unicast,SAFI.multicast): if len_nh != 4: raise Notify(3,0,'invalid ipv4 unicast/multicast next-hop length %d expected 4' % len_nh) elif safi in (SAFI.mpls_vpn,): if len_nh != 12: raise Notify(3,0,'invalid ipv4 mpls_vpn next-hop length %d expected 12' % len_nh) rd = 8 elif safi in (SAFI.flow_ip,): if len_nh not in (0,4): raise Notify(3,0,'invalid ipv4 flow_ip next-hop length %d expected 4' % len_nh) elif safi in (SAFI.flow_vpn,): if len_nh not in (0,4): raise Notify(3,0,'invalid ipv4 flow_vpn next-hop length %d expected 4' % len_nh) elif safi in (SAFI.rtc,): if len_nh not in (4,16): raise Notify(3,0,'invalid ipv4 rtc next-hop length %d expected 4' % len_nh) elif afi == AFI.ipv6: if safi in (SAFI.unicast,): if len_nh not in (16,32): raise Notify(3,0,'invalid ipv6 unicast next-hop length %d expected 16 or 32' % len_nh) elif safi in (SAFI.mpls_vpn,): if len_nh not in (24,40): raise Notify(3,0,'invalid ipv6 mpls_vpn next-hop length %d expected 24 or 40' % len_nh) rd = 8 elif safi in (SAFI.flow_ip,): if len_nh not in (0,16,32): raise Notify(3,0,'invalid ipv6 flow_ip next-hop length %d expected 0, 16 or 32' % len_nh) elif safi in (SAFI.flow_vpn,): if len_nh not in (0,16,32): raise Notify(3,0,'invalid ipv6 flow_vpn next-hop length %d expected 0, 16 or 32' % len_nh) elif afi == AFI.l2vpn: if len_nh != 4: Notify(3,0,'invalid l2vpn next-hop length %d expected 4' % len_nh) elif afi == AFI.bgpls: if len_nh != 4: Notify(3,0,'invalid bgpls next-hop length %d expected 4' % len_nh) size = len_nh - rd # XXX: FIXME: GET IT FROM CACHE HERE ? nhs = data[offset+rd:offset+rd+size] nexthops = [nhs[pos:pos+16] for pos in range(0,len(nhs),16)] # chech the RD is well zero if rd and sum([int(ord_(_)) for _ in data[offset:8]]) != 0: raise Notify(3,0,"MP_REACH_NLRI next-hop's route-distinguisher must be zero") offset += len_nh # Skip a reserved bit as somone had to bug us ! reserved = ord_(data[offset]) offset += 1 if reserved != 0: raise Notify(3,0,'the reserved bit of MP_REACH_NLRI is not zero') # Is the peer going to send us some Path Information with the route (AddPath) addpath = negotiated.addpath.receive(afi,safi) # Reading the NLRIs data = data[offset:] if not data: raise Notify(3,0,'No data to decode in an MPREACHNLRI but it is not an EOR %d/%d' % (afi,safi)) while data: if nexthops: for nexthop in nexthops: nlri,left = NLRI.unpack_nlri(afi,safi,data,IN.ANNOUNCED,addpath) nlri.nexthop = NextHop.unpack(nexthop) nlris.append(nlri) else: nlri,left = NLRI.unpack_nlri(afi,safi,data,IN.ANNOUNCED,addpath) nlris.append(nlri) if left == data: raise RuntimeError("sub-calls should consume data") # logger.parser(LazyFormat("parsed announce mp nlri %s payload " % nlri,data[:length])) data = left return cls(afi,safi,nlris)
def __repr__ (self): h = 0x00 for byte in self.community: h <<= 8 h += ord_(byte) return "0x%016X" % h
def _raw(self): return ''.join('%02X' % ord_(_) for _ in self.pack())
def unpack (cls, data): return cls(':'.join('%02X' % ord_(_) for _ in data[:6]),data[:6])
def __str__ (self): return ':'.join('%02X' % ord_(_) for _ in self._packed)
def unpack(cls, data, negotiated): if data: raise Notify( 3, 2, 'invalid ATOMIC_AGGREGATE %s' % [hex(ord_(_)) for _ in data]) return cls()