def split(data): length = len(data) len_withdrawn = unpack('!H', data[0:2])[0] withdrawn = data[2:len_withdrawn + 2] if len(withdrawn) != len_withdrawn: raise Notify( 3, 1, 'invalid withdrawn routes length, not enough data available') start_attributes = len_withdrawn + 4 len_attributes = unpack('!H', data[len_withdrawn + 2:start_attributes])[0] start_announced = len_withdrawn + len_attributes + 4 attributes = data[start_attributes:start_announced] announced = data[start_announced:] if len(attributes) != len_attributes: raise Notify( 3, 1, 'invalid total path attribute length, not enough data available' ) if 2 + len_withdrawn + 2 + len_attributes + len(announced) != length: raise Notify( 3, 1, 'error in BGP message length, not enough data for the size announced' ) return withdrawn, attributes, announced
def read_message(self): for length, msg, header, body, notify in self.connection.reader(): if notify: if self.neighbor.api['receive-packets']: self.peer.reactor.processes.receive( self.peer, msg, header, body) if self.neighbor.api[Message.ID.NOTIFICATION]: self.peer.reactor.processes.notification( self.peer, notify.code, notify.subcode, str(notify)) # XXX: is notify not already Notify class ? raise Notify(notify.code, notify.subcode, str(notify)) if not length: yield _NOP if self.neighbor.api[ 'receive-packets'] and not self.neighbor.api['consolidate']: self.peer.reactor.processes.receive(self.peer, msg, header, body) if msg == Message.ID.UPDATE and not self.neighbor.api[ 'receive-parsed'] and not self.log_routes: yield _UPDATE return self.logger.message(self.me('<< %s' % Message.ID.name(msg))) try: message = Message.unpack_message(msg, body, self.negotiated) except (KeyboardInterrupt, SystemExit, Notify): raise except Exception, e: self.logger.message( self.me('Could not decode message %s' % Capability.hex(msg))) self.logger.message(self.me('%s' % str(e))) raise Notify( 2, 0, 'can not decode update message %s' % Capability.hex(msg))
def packed_attributes(self, negotiated, maximum=Negotiated.FREE_SIZE): if not self.nlris: return # we changed the API to nrli.pack from addpath to negotiated but not pack itself mpurnlri = [] for nlri in self.nlris: if nlri.family() != self.family( ): # nlri is not part of specified family continue mpurnlri.append(nlri.pack(negotiated)) payload = self.afi.pack() + self.safi.pack() header_length = len(payload) for nlri in mpurnlri: if self._len(payload + nlri) > maximum: if len(payload) == header_length or len(payload) > maximum: raise Notify( 6, 0, 'attributes size is so large we can not even pack on MPURNLRI' ) yield self._attribute(payload) payload = self.afi.pack() + self.safi.pack() + nlri continue payload = payload + nlri if len(payload) == header_length or len(payload) > maximum: raise Notify( 6, 0, 'attributes size is so large we can not even pack on MPURNLRI') yield self._attribute(payload)
def packed_attributes (self, negotiated, maximum=Negotiated.FREE_SIZE): if not self.nlris: return # addpath = negotiated.addpath.send(self.afi,self.safi) # nexthopself = negotiated.nexthopself(self.afi) mpnlri = {} for nlri in self.nlris: if nlri.family() != self.family(): # nlri is not part of specified family continue if nlri.nexthop is NoNextHop: # EOR and Flow may not have any next_hop nexthop = b'' else: _,rd_size = Family.size.get(self.family(),(0,0)) nh_rd = character(0)*rd_size if rd_size else b'' nexthop = nh_rd + nlri.nexthop.ton(negotiated,nlri.afi) # mpunli[nexthop] = nlri mpnlri.setdefault(nexthop,[]).append(nlri.pack(negotiated)) for nexthop,nlris in six.iteritems(mpnlri): payload = concat_bytes(self.afi.pack(), self.safi.pack(), character(len(nexthop)), nexthop, character(0)) header_length = len(payload) for nlri in nlris: if self._len(payload + nlri) > maximum: if len(payload) == header_length or len(payload) > maximum: raise Notify(6, 0, 'attributes size is so large we can not even pack on MPRNLRI') yield self._attribute(payload) payload = concat_bytes(self.afi.pack(), self.safi.pack(), character(len(nexthop)), nexthop, character(0), nlri) continue payload = concat_bytes(payload, nlri) if len(payload) == header_length or len(payload) > maximum: raise Notify(6, 0, 'attributes size is so large we can not even pack on MPRNLRI') yield self._attribute(payload)
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]) # XXX: FIXME: check the length of data if option_len: data = data[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_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 = Flow(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_nlri(cls, afi, safi, bgp, action, addpath): nlri = cls(afi, safi, action) if addpath: nlri.path_info = PathInfo(bgp[:4]) bgp = bgp[4:] mask = 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', bytes([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 family %s (AFI %d) %s (SAFI %d)' % (AFI(afi), int(afi), SAFI(safi), int(safi))) network, bgp = bgp[:size], bgp[size:] nlri.cidr = CIDR(network + bytes(IP.length(afi) - size), mask) return nlri, bgp
def packed_attributes(self, negotiated, maximum=Negotiated.FREE_SIZE): if not self.nlris: return # addpath = negotiated.addpath.send(self.afi,self.safi) # nexthopself = negotiated.nexthopself(self.afi) mpnlri = {} for nlri in self.nlris: if nlri.family() != self.family( ): # nlri is not part of specified family continue if nlri.nexthop is NoNextHop: # EOR and Flow may not have any next_hop nexthop = b'' else: _, rd_size = Family.size.get(self.family(), (0, 0)) nh_rd = bytes([0]) * rd_size if rd_size else b'' try: nexthop = nh_rd + nlri.nexthop.ton(negotiated, nlri.afi) except TypeError: # we could not match "next-hop self" with the BGP AFI of the BGP sesion # attempting invalid IPv4 next-hop (0.0.0.0) to try to not kill the session # and preserve some form of backward compatibility (for some vendors) # the next-hop may have been IPv6 but not valided as the RFC says # # An UPDATE message that carries no NLRI, other than the one encoded in # the MP_REACH_NLRI attribute, SHOULD NOT carry the NEXT_HOP attribute. # If such a message contains the NEXT_HOP attribute, the BGP speaker # that receives the message SHOULD ignore this attribute. # # Some vendors may have therefore not valided the next-hop # and accepted invalid IPv6 next-hop in the past nexthop = bytes([0]) * 4 # mpunli[nexthop] = nlri mpnlri.setdefault(nexthop, []).append(nlri.pack(negotiated)) for nexthop, nlris in mpnlri.items(): payload = self.afi.pack() + self.safi.pack() + bytes( [len(nexthop)]) + nexthop + bytes([0]) header_length = len(payload) for nlri in nlris: if self._len(payload + nlri) > maximum: if len(payload) == header_length or len(payload) > maximum: raise Notify( 6, 0, 'attributes size is so large we can not even pack on MPRNLRI' ) yield self._attribute(payload) payload = self.afi.pack() + self.safi.pack() + bytes( [len(nexthop)]) + nexthop + bytes([0]) + nlri continue payload = payload + nlri if len(payload) == header_length or len(payload) > maximum: raise Notify( 6, 0, 'attributes size is so large we can not even pack on MPRNLRI' ) yield self._attribute(payload)
def unpack_message(cls, data, _): try: afi, reserved, safi = unpack('!HBB', data) except error: raise Notify(7, 1, 'invalid route-refresh message') if reserved not in (0, 1, 2): raise Notify(7, 2, 'invalid route-refresh message subtype') return RouteRefresh(afi, safi, reserved)
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 = data[0] slen = 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 packed_attributes(self, negotiated, maximum=Negotiated.FREE_SIZE): if not self.nlris: return # addpath = negotiated.addpath.send(self.afi,self.safi) # nexthopself = negotiated.nexthopself(self.afi) mpnlri = {} for nlri in self.nlris: if nlri.family() != self.family( ): # nlri is not part of specified family continue if nlri.nexthop is NoNextHop: # EOR and Flow may not have any next_hop nexthop = '' else: # we do not want a next_hop attribute packed (with the _attribute()) but just the next_hop itself if nlri.safi.has_rd(): # .packed and not .pack() nexthop = chr(0) * 8 + nlri.nexthop.ton( negotiated, nlri.afi) else: # .packed and not .pack() nexthop = nlri.nexthop.ton(negotiated, nlri.afi) # mpunli[nexthop] = nlri mpnlri.setdefault(nexthop, []).append(nlri.pack(negotiated)) for nexthop, nlris in mpnlri.iteritems(): payload = ''.join([ self.afi.pack(), self.safi.pack(), chr(len(nexthop)), nexthop, chr(0) ]) header_length = len(payload) for nlri in nlris: if self._len(payload + nlri) > maximum: if len(payload) == header_length or len(payload) > maximum: raise Notify( 6, 0, 'attributes size is so large we can not even pack on MPRNLRI' ) yield self._attribute(payload) payload = ''.join([ self.afi.pack(), self.safi.pack(), chr(len(nexthop)), nexthop, chr(0), nlri ]) continue payload = ''.join([payload, nlri]) if len(payload) == header_length or len(payload) > maximum: raise Notify( 6, 0, 'attributes size is so large we can not even pack on MPRNLRI' ) yield self._attribute(payload)
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:] padding = '\0' * (IP.length(afi) - size) nlri.cidr = CIDR(network + padding, mask) return nlri, bgp
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 = 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 = 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 _nlri(afi, safi, bgp, action, addpath): labels = [] rd = '' if addpath: path_identifier = bgp[:4] bgp = bgp[4:] else: path_identifier = None mask = ord(bgp[0]) bgp = bgp[1:] if SAFI(safi).has_label(): 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 if SAFI(safi).has_rd(): mask -= 8 * 8 # the 8 bytes of the route distinguisher rd = bgp[:8] bgp = bgp[8:] 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:] padding = '\0' * (IP.length(afi) - size) prefix = network + padding return labels, rd, path_identifier, mask, size, prefix, bgp
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 tick(self, message=_NOP, ignore=_NOP.TYPE): if message.TYPE != ignore: self.last_read = time.time() if self.holdtime: left = int(self.last_read + self.holdtime - time.time()) self.logger.timers( self.me('Receive Timer %d second(s) left' % left)) if left <= 0: raise Notify(self.code, self.subcode, self.message) elif message.TYPE == KeepAlive.TYPE: raise Notify(2, 6, 'Holdtime is zero and we got a keepalive message')
def _nlrifactory(afi, safi, bgp, action): labels = [] rd = '' mask = ord(bgp[0]) bgp = bgp[1:] if SAFI(safi).has_label(): 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 if SAFI(safi).has_rd(): mask -= 8 * 8 # the 8 bytes of the route distinguisher rd = bgp[:8] bgp = bgp[8:] 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 = mask_to_bytes.get(mask, None) if size is None: raise Notify(3, 10, 'invalid netmask found when decoding NLRI') 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:] padding = '\0' * (NLRI.length[afi] - size) prefix = network + padding return labels, rd, mask, size, prefix, bgp
def check_ka(self, message=_NOP, ignore=_NOP.TYPE): if message.TYPE != ignore: self.last_read = time.time() if self.holdtime: left = int(self.last_read + self.holdtime - time.time()) self.logger.timers( self.me('Receive Timer %d second(s) left' % left)) if left <= 0: raise Notify(self.code, self.subcode, self.message) elif message.TYPE == KeepAlive.TYPE: raise Notify( 2, 6, 'Negotiated holdtime was zero, it was invalid to send us a keepalive messages' )
def __new_aspaths(cls, data, asn4, klass=None): as_set = [] as_seq = [] backup = data unpacker = { False: '!H', True: '!L', } size = { False: 2, True: 4, } as_choice = { ASPath.AS_SEQUENCE: as_seq, ASPath.AS_SET: as_set, } 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): raise Notify(3, 11, 'invalid AS Path type sent %d' % stype) end = 2 + (slen * length) sdata = data[2:end] data = data[end:] asns = as_choice[stype] for i 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, backup) return cls(as_seq, as_set, backup)
def read_message(self): # This will always be defined by the loop but scope leaking upset scrutinizer/pylint msg_id = None packets = self.neighbor.api['receive-packets'] consolidate = self.neighbor.api['receive-consolidate'] parsed = self.neighbor.api['receive-parsed'] for length, msg_id, header, body, notify in self.connection.reader(): if notify: if self.neighbor.api['receive-%d' % Message.CODE.NOTIFICATION]: if packets and not consolidate: self.peer.reactor.processes.packets( self.peer.neighbor, 'receive', msg_id, header, body) if not packets or consolidate: header = '' body = '' self.peer.reactor.processes.notification( self.peer.neighbor, 'receive', notify.code, notify.subcode, str(notify), header, body) # XXX: is notify not already Notify class ? raise Notify(notify.code, notify.subcode, str(notify)) if not length: yield _NOP if packets and not consolidate: self.peer.reactor.processes.packets(self.peer.neighbor, 'receive', msg_id, header, body) if msg_id == Message.CODE.UPDATE: if not parsed and not self.log_routes: yield _UPDATE return self.logger.message(self.me('<< %s' % Message.CODE.name(msg_id))) try: message = Message.unpack(msg_id, body, self.negotiated) except (KeyboardInterrupt, SystemExit, Notify): raise except Exception, exc: self.logger.message( self.me('Could not decode message "%d"' % msg_id)) self.logger.message(self.me('%s' % str(exc))) self.logger.message(traceback.format_exc()) raise Notify(1, 0, 'can not decode update message of type "%d"' % msg_id)
def unpack (cls,data,length): igpflags = ['D', 'N', 'L', 'P'] if length > 1: raise Notify(3,5, "IGP Flags TLV length too large") else: flag_array = binascii.b2a_hex(data[0]) hex_rep = hex(int(flag_array, 16)) bit_array = BitArray(hex_rep) valid_flags = [''.join(item)+'0000' for item in itertools.product('01', repeat=4)] valid_flags.append('0000') if bit_array.bin in valid_flags: flags = dict(zip(igpflags, bit_array.bin)) return cls(igpflags=flags) else: raise Notify(3,5, "Invalid IGP flags mask")
def unpack (cls,data,length): mpls_mask = ['LDP', 'RSVP-TE', 'RSV', 'RSV', 'RSV', 'RSV', 'RSV', 'RSV'] if length > 1: raise Notify(3,5, "LINK TLV length too large") else: flag_array = binascii.b2a_hex(data[0]) hex_rep = hex(int(flag_array, 16)) bit_array = BitArray(hex_rep) valid_flags = [''.join(item)+'000000' for item in itertools.product('01', repeat=2)] valid_flags.append('0000') if bit_array.bin in valid_flags: flags = dict(zip(mpls_mask, bit_array.bin)) return cls(mplsflags=flags) else: raise Notify(3,5, "Invalid MPLS flags mask")
def __call__ (self): # True if we need or are trying # False if we do not need to send one try: return self._generator.next() except StopIteration: raise Notify(4,0,'could not send keepalive')
def _keepalive (self, proto): need_ka = False generator = None while True: # SEND KEEPALIVES need_ka |= self.send_timer.need_ka() if need_ka: if not generator: generator = proto.new_keepalive() need_ka = False if not generator: yield False continue try: # try to close the generator and raise a StopIteration in one call generator.next() generator.next() # still running yield True except NetworkError: raise Notify(4,0,'problem with network while trying to send keepalive') except StopIteration: generator = None yield False
def unpack(cls, data): # Extract node capability flags flags = cls.unpack_flags(data[0:1]) # Move pointer past flags and reserved bytes data = data[2:] sids = [] while data: # Range Size: 3 octet value indicating the number of labels in # the range. range_size = unpack('!L', bytes([0]) + data[:3])[0] # SID/Label: If length is set to 3, then the 20 rightmost bits # represent a label. If length is set to 4, then the value # represents a 32 bit SID. t, l = unpack('!HH', data[3:7]) if t != 1161: raise Notify(3, 5, "Invalid sub-TLV type: {}".format(t)) if l == 3: sids.append([ range_size, unpack('!I', bytes([0]) + data[7:l + 7])[0] & 0xfffff ]) elif l == 4: # XXX: really we are reading 7+ but then re-parsing it again ?? sids.append([range_size, unpack('!I', data[7:l + 7])[0]]) data = data[l + 7:] return cls(flags, sids)
def pack(self, negotiated=None): ordered_rules = [] # the order is a RFC requirement for ID in sorted(self.rules.keys()): rules = self.rules[ID] # for each component get all the operation to do # the format use does not prevent two opposing rules meaning that no packet can ever match for rule in rules: rule.operations &= (CommonOperator.EOL ^ 0xFF) rules[-1].operations |= CommonOperator.EOL # and add it to the last rule if ID not in (FlowDestination.ID, FlowSource.ID): ordered_rules.append(chr(ID)) ordered_rules.append(''.join(rule.pack() for rule in rules)) components = self.rd.pack() + ''.join(ordered_rules) l = len(components) if l < 0xF0: return "%s%s" % (chr(l), components) if l < 0x0FFF: return "%s%s" % (pack('!H', l | 0xF000), components) raise Notify( 3, 0, "my administrator attempted to announce a Flow Spec rule larger than encoding allows, protecting the innocent the only way I can" )
def unpack(cls, data, length): if length != 4: raise Notify(3, 5, "Unable to decode attribute. Incorrect Size") else: b = BitArray(bytes=data) colormask = b.unpack('uintbe:32') return cls(colormask=colormask)
def pack(self, addpath=None): ordered_rules = [] # the order is a RFC requirement for ID in sorted(self.rules.keys()): rules = self.rules[ID] # for each component get all the operation to do # the format use does not prevent two opposing rules meaning that no packet can ever match for rule in rules: rule.operations &= (CommonOperator.EOL ^ 0xFF) rules[-1].operations |= CommonOperator.EOL # and add it to the last rule if ID not in (FlowDestination.ID, FlowSource.ID): ordered_rules.append(chr(ID)) ordered_rules.append(''.join(rule.pack() for rule in rules)) components = ''.join(ordered_rules) if self.safi == SAFI.flow_vpn: components = self.rd.pack() + components l = len(components) if l < 0xF0: data = "%s%s" % (chr(l), components) elif l < 0x0FFF: data = "%s%s" % (pack('!H', l | 0xF000), components) else: raise Notify( 3, 0, "rule too big for NLRI - how to handle this - does this work ?" ) # data = "%s" % chr(0) return data
def unpack(cls, data, negotiated): try: if cls.cached: if data == cls.previous: return cls.cached # # This code may mess with the cached data # elif cls.previous and data.startswith(cls.previous): # attributes = Attributes() # for key in cls.cached: # attributes[key] = cls.cached[key] # attributes.parse(data[len(cls.previous):],negotiated) else: attributes = cls().parse(data, negotiated) else: attributes = cls().parse(data, negotiated) if Attribute.CODE.AS_PATH in attributes and Attribute.CODE.AS4_PATH in attributes: attributes.merge_attributes() if Attribute.CODE.MP_REACH_NLRI not in attributes and Attribute.CODE.MP_UNREACH_NLRI not in attributes: cls.previous = data cls.cached = attributes else: cls.previous = '' cls.cached = None return attributes except IndexError: raise Notify(3, 2, data)
def add(self, attribute, _=None): # we return None as attribute if the unpack code must not generate them if attribute is None: return self._str = '' self._json = '' # XXX: FIXME: I am not sure anymore that more than one of each is possible if attribute.ID in Attributes.MULTIPLE: # deadcode: setdefault does not seem to exist anywhere ? (TM) self.setdefault(attribute.ID, []).append(attribute) # elif attribute.ID in (Attribute.CODE.COMMUNITY, Attribute.CODE.EXTENDED_COMMUNITY): # if attribute.ID not in self: # self[attribute.ID] = Attribute.klass(attribute.ID,attribute.FLAG)() # self[attribute.ID].add(attribute) elif attribute.ID in self: # For flows we can add extended-communities using special keywords and extended-community # This allows this trick if attribute.ID != Attribute.CODE.EXTENDED_COMMUNITY: raise Notify( 3, 0, 'multiple attribute for %s' % str(Attribute.CODE(attribute.ID))) for community in attribute.communities: self[attribute.ID].add(community) else: self[attribute.ID] = attribute