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 = (ordinal(data[0]) & 0x0F, ordinal(data[1])) if community in ExtendedCommunity.registered_extended: return ExtendedCommunity.registered_extended[community].unpack( data) return ExtendedCommunity(data)
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 = ordinal(data[6]) data = data[7:length+4] return klass(afi,safi,data) elif decode == 'query': afi = unpack('!H',data[4:6])[0] safi = ordinal(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 = ordinal(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_nlri(cls, afi, safi, bgp, action, addpath): length, bgp = ordinal(bgp[0]), bgp[1:] if length & 0xF0 == 0xF0: # bigger than 240 extra, bgp = ordinal(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 = ordinal(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 = ordinal(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 test_nlri(self): components = { 'destination': Flow4Destination(IPv4.pton("192.0.2.0"), 24), 'source': Flow4Source(IPv4.pton("10.1.2.0"), 24), 'anyport_1': FlowAnyPort(NumericOperator.EQ | NumericOperator.GT, 25), 'anyport_2': FlowAnyPort(NumericOperator.EQ | NumericOperator.LT, 80), } messages = { 'destination': [0x01, 0x18, 0xc0, 0x00, 0x02], 'source': [0x02, 0x18, 0x0a, 0x01, 0x02], 'anyport_1': [0x04, 0x43, 0x19], 'anyport_2': [0x85, 0x50], } flow = Flow() message = b"" for key in ['destination', 'source', 'anyport_1', 'anyport_2']: flow.add(components[key]) message += data_from_body(messages[key]) message = character(len(message)) + message # policy.add(to_FlowAction(65000,False,False)) flow = flow.pack() if message[0] != flow[0]: self.fail('size mismatch %s %s\n' % (ordinal(flow[0]), ordinal(message[0]))) if len(flow) != ordinal(flow[0]) + 1: self.fail('invalid size for 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 = ordinal(data[6]) data = data[7:length + 4] return klass(afi, safi, data) elif decode == 'query': afi = unpack('!H', data[4:6])[0] safi = ordinal(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 = ordinal(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 _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 = ordinal(data[0]) slen = ordinal(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 (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 = (ordinal(data[0]) & 0x0F,ordinal(data[1])) if community in ExtendedCommunity.registered_extended: klass = ExtendedCommunity.registered_extended[community] instance = klass.unpack(data) instance.klass = klass return instance return ExtendedCommunity(data)
def flag_attribute_content (data): flag = Attribute.Flag(ordinal(data[0])) attr = Attribute.CODE(ordinal(data[1])) if flag & Attribute.Flag.EXTENDED_LENGTH: length = unpack('!H',data[2:4])[0] return flag, attr, data[4:length+4] else: length = ordinal(data[2]) return flag, attr, data[3:length+3]
def flag_attribute_content(data): flag = Attribute.Flag(ordinal(data[0])) attr = Attribute.CODE(ordinal(data[1])) if flag & Attribute.Flag.EXTENDED_LENGTH: length = unpack('!H', data[2:4])[0] return flag, attr, data[4:length + 4] else: length = ordinal(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))) ld = ordinal(data[1]) boundary = ld+2 if len(data) < boundary: raise Notify(2,0,"Bad length for OPEN %s (buffer underrun) %s" % (name,Capability.hex(data))) key = ordinal(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 = ordinal(data[0]) slen = ordinal(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(cls, data): datalen = len(data) rd = RouteDistinguisher.unpack(data[:8]) esi = ESI.unpack(data[8:18]) etag = EthernetTag.unpack(data[18:22]) maclength = ordinal(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 = ordinal(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 = ordinal(bgp[0]) length = ordinal(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 = ordinal(bgp[0]) length = ordinal(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): length,bgp = ordinal(bgp[0]),bgp[1:] if length & 0xF0 == 0xF0: # bigger than 240 extra,bgp = ordinal(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 = ordinal(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) else: end = False while not end: byte,bgp = ordinal(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)) return nlri, bgp+over
def json (self): h = 0x00 for byte in self.community: h <<= 8 h += ordinal(byte) s = self.klass.__repr__(self) if self.klass else '' return '{ "value": %s, "string": "%s" }' % (h,s)
def spaced(value): even = None for v in value: if even is False: yield ' ' yield '%02X' % ordinal(v) even = not even
def json(self): h = 0x00 for byte in self.community: h <<= 8 h += ordinal(byte) s = self.klass.__repr__(self) if self.klass else '' return '{ "value": %ld, "string": "%s" }' % (h, s)
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))) ld = ordinal(data[1]) boundary = ld+2 if len(data) < boundary: raise Notify(2,0,"Bad length for OPEN %s (buffer underrun) %s" % (name,Capability.hex(data))) key = ordinal(data[0]) value = data[2:boundary] rest = data[boundary:] return key,value,rest capabilities = Capabilities() option_len = ordinal(data[0]) if not option_len: return capabilities data = data[1:option_len+1] while data: key,value,data = _key_values('parameter',data) # Parameters 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 = ordinal(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 spaced (value): even = None for v in value: if even is False: yield ' ' yield '%02X' % ordinal(v) even = not even
def decode (afi,bgp): mask = ordinal(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_nlri (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, ordinal(RTC.resetFlags(packedRT[0]))) + packedRT[1:] return pack("!B",0)
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(ordinal(_)) 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(ordinal(_)) for _ in data])) communities.add(Community.unpack(data[:4],negotiated)) data = data[4:] return communities
def __repr__(self): if self.klass: return self.klass.__repr__(self) h = 0x00 for byte in self.community: h <<= 8 h += ordinal(byte) return "0x%016X" % h
def __repr__ (self): if self.klass: return self.klass.__repr__(self) h = 0x00 for byte in self.community: h <<= 8 h += ordinal(byte) return "0x%016X" % h
def decode(afi, bgp): mask = ordinal(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 unpack (cls, data): rd = RouteDistinguisher.unpack(data[:8]) etag = EthernetTag.unpack(data[8:12]) iplen = ordinal(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 send(self, raw): if self.neighbor.api.get( 'send-%s' % Message.CODE.short(ordinal(raw[18])), False): message = Update.unpack_message(raw[19:], self.negotiated) self._to_api('send', message, raw) for boolean in self.connection.writer(raw): yield boolean
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 = ordinal(bgp[0]) bgp = bgp[1:] _, rd_size = Family.size.get((afi, safi), (0, 0)) rd_mask = rd_size * 8 if safi.has_label(): labels = [] while mask - rd_mask >= 24: label = int(unpack('!L', character(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 rd_size: mask -= rd_mask # the route distinguisher rd = bgp[:rd_size] bgp = bgp[rd_size:] 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 send (self, raw): code = 'send-%s' % Message.CODE.short(ordinal(raw[18])) self.peer.stats[code] = self.peer.stats.get(code,0) + 1 if self.neighbor.api.get(code,False): message = Update.unpack_message(raw[19:],self.negotiated) self._to_api('send',message,raw) for boolean in self.connection.writer(raw): yield boolean
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 = ordinal(data[3]) instance.add_path(afi,safi,sr) data = data[4:] return instance
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 = ordinal(data[3]) instance.add_path(afi, safi, sr) data = data[4:] return instance
def json(self): if self.capability in Capability.CODE.reserved: iana = 'reserved' elif self.capability in Capability.CODE.unassigned: iana = 'unassigned' else: iana = 'unknown' raw = ''.join('%02X' % ordinal(_) for _ in self.data) return '{ "name": "unknown", "iana": "%s", "value": %d, "raw": "%s" }' % ( iana, self.capability, raw)
def unpack(cls, data): rd = RouteDistinguisher.unpack(data[:8]) etag = EthernetTag.unpack(data[8:12]) iplen = ordinal(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 split (last): if Attribute.CODE.INTERNAL_SPLIT not in last.attributes: yield last return # ignore if the request is for an aggregate, or the same size mask = last.nlri.cidr.mask cut = last.attributes[Attribute.CODE.INTERNAL_SPLIT] if mask >= cut: yield last return # calculate the number of IP in the /<size> of the new route increment = pow(2,last.nlri.afi.mask() - cut) # how many new routes are we going to create from the initial one number = pow(2,cut - last.nlri.cidr.mask) # convert the IP into a integer/long ip = 0 for c in last.nlri.cidr.ton(): ip <<= 8 ip += ordinal(c) afi = last.nlri.afi safi = last.nlri.safi # Really ugly klass = last.nlri.__class__ nexthop = last.nlri.nexthop if safi.has_path(): path_info = last.nlri.path_info if safi.has_label(): labels = last.nlri.labels if safi.has_rd(): rd = last.nlri.rd # XXX: Looks weird to set and then set to None, check last.nlri.cidr.mask = cut last.nlri = None # generate the new routes for _ in range(number): # update ip to the next route, this recalculate the "ip" field of the Inet class nlri = klass(afi,safi,OUT.ANNOUNCE) nlri.cidr = CIDR(pack_int(afi,ip),cut) nlri.nexthop = nexthop # nexthop can be NextHopSelf if safi.has_path(): nlri.path_info = path_info if safi.has_label(): nlri.labels = labels if safi.has_rd(): nlri.rd = rd # next ip ip += increment yield Change(nlri,last.attributes)
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 = ordinal(bgp[0]) bgp = bgp[1:] _, rd_size = Family.size.get((afi, safi), (0, 0)) rd_mask = rd_size * 8 if safi.has_label(): labels = [] while mask - rd_mask >= 24: label = int(unpack('!L',character(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 rd_size: mask -= rd_mask # the route distinguisher rd = bgp[:rd_size] bgp = bgp[rd_size:] 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 = ordinal(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): communities = ExtendedCommunitiesIPv6() while data: if data and len(data) < 20: raise Notify( 3, 1, 'could not decode ipv6 extended community %s' % str([hex(ordinal(_)) for _ in data])) communities.add(ExtendedCommunityIPv6.unpack( data[:20], negotiated)) data = data[20:] return communities
def _read_open (self): wait = environment.settings().bgp.openwait opentimer = ReceiveTimer(self.proto.connection.session,wait,1,1,'waited for open too long, we do not like stuck in active') # Only yield if we have not the open, otherwise the reactor can run the other connection # which would be bad as we need to do the collission check without going to the other peer for message in self.proto.read_open(self.neighbor.peer_address.top()): opentimer.check_ka(message) # XXX: FIXME: change the whole code to use the ord and not the chr version # Only yield if we have not the open, otherwise the reactor can run the other connection # which would be bad as we need to do the collission check if ordinal(message.TYPE) == Message.CODE.NOP: yield ACTION.NOW yield message
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 = ordinal(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 = ordinal(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(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(ordinal(_)) 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_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 = ordinal(data[3]) families.append((afi,safi,flag_family)) data = data[4:] return instance.set(restart_flag,restart_time,families)
def __init__(self, code, subcode, data=b'', parse_data=True): self.code = code self.subcode = subcode if not parse_data: self.data = data return if not (code, subcode) in [(6, 2), (6, 4)]: self.data = data if not len( [_ for _ in str(data) if _ not in string.printable]) else hexbytes(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 = ordinal(data[0]) data = data[1:] if shutdown_length == 0: self.data = b"empty Shutdown Communication." # move offset past length field return if len(data) < shutdown_length: self.data = b"invalid Shutdown Communication (buffer underrun) length : %i [%s]" % ( shutdown_length, hexstring(data)) return if shutdown_length > 128: self.data = b"invalid Shutdown Communication (too large) length : %i [%s]" % ( shutdown_length, hexstring(data)) return try: self.data = b'Shutdown Communication: "%s"' % \ data[:shutdown_length].decode('utf-8').replace('\r',' ').replace('\n',' ') except UnicodeDecodeError: self.data = b"invalid Shutdown Communication (invalid UTF-8) length : %i [%s]" % ( shutdown_length, hexstring(data)) return trailer = data[shutdown_length:] if trailer: self.data += b", trailing data: " + hexstring(trailer)
def unpack(cls, data): rd = RouteDistinguisher.unpack(data[:8]) esi = ESI.unpack(data[8:18]) iplen = ordinal(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_message (cls, data, _=None): version = ordinal(data[0]) if version != 4: # Only version 4 is supported nowdays.. raise Notify(2,1,bytes_ascii(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 _read_open (self): wait = environment.settings().bgp.openwait opentimer = ReceiveTimer(self.proto.connection.session,wait,1,1,'waited for open too long, we do not like stuck in active') # Only yield if we have not the open, otherwise the reactor can run the other connection # which would be bad as we need to do the collission check without going to the other peer for message in self.proto.read_open(self.neighbor.peer_address.top()): opentimer.check_ka(message) # XXX: FIXME: change the whole code to use the ord and not the chr version # Only yield if we have not the open, otherwise the reactor can run the other connection # which would be bad as we need to do the collission check if ordinal(message.TYPE) == Message.CODE.NOP: # If a peer does not reply to OPEN message, or not enough bytes # yielding ACTION.NOW can cause ExaBGP to busy spin trying to # read from peer. See GH #723 . yield ACTION.LATER yield message
def check_message (neighbor, message): message = message.replace(':','') raw = concat_bytes_i(character(int(_,16)) for _ in (message[i*2:(i*2)+2] for i in range(len(message)//2))) if raw.startswith(b'\xff'*16): kind = ordinal(raw[18]) # XXX: FIXME: check size # size = (ordinal(raw[16]) << 16) + (ordinal(raw[17])) if kind == 1: return check_open(neighbor,raw[18:]) elif kind == 2: return check_update(neighbor,raw) elif kind == 3: return check_notification(raw) else: return check_update(neighbor,raw)
def __init__ (self, code, subcode, data=b'', parse_data=True): self.code = code self.subcode = subcode if not parse_data: self.data = data return if not (code, subcode) in [(6, 2), (6, 4)]: self.data = data if not len([_ for _ in str(data) if _ not in string.printable]) else hexbytes(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 = ordinal(data[0]) data = data[1:] if shutdown_length == 0: self.data = b"empty Shutdown Communication." # move offset past length field return if len(data) < shutdown_length: self.data = b"invalid Shutdown Communication (buffer underrun) length : %i [%s]" % (shutdown_length, hexstring(data)) return if shutdown_length > 128: self.data = b"invalid Shutdown Communication (too large) length : %i [%s]" % (shutdown_length, hexstring(data)) return try: self.data = b'Shutdown Communication: "%s"' % \ data[:shutdown_length].decode('utf-8').replace('\r',' ').replace('\n',' ') except UnicodeDecodeError: self.data = b"invalid Shutdown Communication (invalid UTF-8) length : %i [%s]" % (shutdown_length, hexstring(data)) return trailer = data[shutdown_length:] if trailer: self.data += b", trailing data: " + hexstring(trailer)
def unpack_nlri (cls, afi, safi, bgp, action, addpath): length = ordinal(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:]