def unpack(cls, data, length=0, capability={}): """unpack bgp message based on its type """ if len(data) < cls.HDR_LEN: # Every BGP message is at least 19 octets. The message is # uncompleted or the rest hasn't arrived yet. raise MessageUncompleted( message= "The message is uncompleted or the rest hasn't arrived yet.") # unpack the message header # check whether the first 16 octets of the data consist of the # BGP marker (all bits one) marker, length, msg_type = struct.unpack('!16sHB', data[:cls.HDR_LEN]) if marker != cls.MARKER: raise BGPNotification(1, 1) if length < cls.HDR_LEN or length > cls.MAX_LEN: raise BGPNotification(1, 2) # Bad Message Length if len(data) < length: raise MessageUncompleted() if msg_type not in cls.registered_message: raise BGPNotification(1, 3) # Bad Message type msg_body = data[cls.HDR_LEN:length] klass = cls.registered_message[msg_type].unpack(data=msg_body, length=length, capability=capability) return klass
def unpack(cls, data, length, capability): open_msg = dict() try: open_msg['version'], open_msg['asn'], open_msg['hold-time'] = struct.unpack('!BHH', data[:5]) except: raise BGPNotification(1, 2) if open_msg['version'] != cls.VERSION: # BGP-4 raise BGPNotification(2, 1) if open_msg['asn'] == 0: raise BGPNotification(2, 2) if isinstance(open_msg['asn'], float): tmp = str(open_msg['asn']).split('.') open_msg['asn'] = 65536 * (int(tmp[0])) + int(tmp[1]) try: open_msg['bgp-id'] = IPAddress.unpack(data[5:9]) except: raise BGPNotification(2, 3) opt_para_len = struct.unpack('!B', data[9:10]) if opt_para_len: open_msg['capabilities'] = Capabilities.unpack(data[10:]).value return cls(value=open_msg, length=length)
def unpack(cls, data, capability): """ parse bgp local preference attribute :param data: raw binary data """ if len(data) != 4: raise BGPNotification(3, 5) return cls(value=struct.unpack('!I', data)[0])
def pack(cls, data, capability): try: return cls(value=data, hex_value=b''.join([ IPAddress.pack(cluster_id) for cluster_id in data ])) except: raise BGPNotification(3, 5)
def unpack(cls, data, capability): """ Parse originator id :param data: """ if len(data) != 4: raise BGPNotification(3, 5) return cls(value=IPAddress.unpack(data))
def unpack(cls, data, capability): """ parse bgp ATOMIC_AGGREGATE attribute :param data: """ if len(data) == 1: return cls(value=struct.unpack('!B', data)[0]) else: raise BGPNotification(3, 5)
def unpack(cls, data, capability): """ Parse BGP nexthop. :param data: raw binary data """ if len(data) % 4 == 0: next_hop = IPAddress.unpack(data) return cls(value=next_hop) else: raise BGPNotification(3, 5)
def pack(cls, data, capability={}): """pack message data into binary data. """ msg_type = data.get('type') if msg_type not in cls.registered_message: raise BGPNotification(1, 3) msg_body = cls.registered_message[msg_type].pack(data=data.get('msg'), capability=capability) return cls(value=data.get('msg'), hex_value=cls.pack_header(msg_type, msg_body.hex_value))
def unpack(cls, data, capability): """ Parse culster list :param data """ cluster_list = [] if len(data) % 4 != 0: raise BGPNotification(3, 5) while data: cluster_list.append(IPAddress.unpack(data[0:4])) data = data[4:] return cls(value=cluster_list)
def pack(cls, data, capability): try: if capability.get('asn4'): return cls(value=data, hex_value=struct.pack('!I', data[0]) + IPAddress.pack(data[1])) else: return cls(value=data, hex_value=struct.pack('!H', data[0]) + IPAddress.pack(data[1])) except: raise BGPNotification(3, 5)
def pack(cls, data, capability): community_hex = b'' for community in data: if community.upper() in WELL_KNOW_COMMUNITY_STR_2_INT: value = WELL_KNOW_COMMUNITY_STR_2_INT[community.upper()] community_hex += struct.pack('!I', value) else: try: value = community.split(':') value = int(value[0]) * 16 * 16 * 16 * 16 + int(value[1]) community_hex += struct.pack('!I', value) except Exception: raise BGPNotification(3, 5) return cls(value=data, hex_value=community_hex)
def unpack(cls, data, capability): """ Parse AS PATH attributes. :param data: raw binary balue """ # Loop over all path segments aspath = [] while len(data) > 0: seg_type, length = struct.unpack('!BB', data[:2]) if seg_type not in [cls.AS_SET, cls.AS_SEQUENCE, cls.AS_CONFED_SEQUENCE, cls.AS_CONFED_SET]: raise BGPNotification(3, 11) try: if capability.get('asn4'): segment = list(struct.unpack('!%dI' % length, data[2:2 + length * 4])) data = data[2 + length * 4:] else: segment = list(struct.unpack('!%dH' % length, data[2:2 + length * 2])) data = data[2 + length * 2:] except Exception: raise BGPNotification(3, 11) aspath.append((seg_type, segment)) return cls(value=aspath)
def unpack(cls, data, capability): """ Parse Aggregator attributes. :param data: raw binary data """ try: if not capability.get('asn4'): asn = struct.unpack('!H', data[:2])[0] aggregator = IPAddress.unpack(data[2:]) else: asn = struct.unpack('!I', data[:4])[0] aggregator = IPAddress.unpack(data[4:]) except Exception: raise BGPNotification(3, 5) return cls(value=[asn, aggregator])
def unpack_nlri(data, addpath=False): """ Parses an RFC4271 encoded blob of BGP prefixes into a list :param data: hex data :param addpath: support addpath or not :return: prefix_list """ prefixes = [] postfix = data while len(postfix) > 0: # for python2 and python3 if addpath: path_id = struct.unpack('!I', postfix[0:4])[0] postfix = postfix[4:] if isinstance(postfix[0], int): prefix_len = postfix[0] else: prefix_len = ord(postfix[0:1]) if prefix_len > 32: raise BGPNotification(3, 10, 'Prefix Length larger than 32') octet_len, remainder = int(prefix_len / 8), prefix_len % 8 if remainder > 0: # prefix length doesn't fall on octet boundary octet_len += 1 tmp = postfix[1:octet_len + 1] # for python2 and python3 if isinstance(postfix[0], int): prefix_data = [i for i in tmp] else: prefix_data = [ord(i[0:1]) for i in tmp] # Zero the remaining bits in the last octet if it didn't fall # on an octet boundary if remainder > 0: prefix_data[-1] &= 255 << (8 - remainder) prefix_data = prefix_data + list(str(0)) * 4 prefix = "%s.%s.%s.%s" % (tuple( prefix_data[0:4])) + '/' + str(prefix_len) if not addpath: prefixes.append(prefix) else: prefixes.append({'prefix': prefix, 'path_id': path_id}) # Next prefix postfix = postfix[octet_len + 1:] return prefixes
def unpack(cls, data, capability): community = [] if data: try: length = len(data) / 2 value_list = list(struct.unpack('!%dH' % length, data)) while value_list: value_type = value_list[ 0] * 16 * 16 * 16 * 16 + value_list[1] if value_type in WELL_KNOW_COMMUNITY_INT_2_STR: community.append( WELL_KNOW_COMMUNITY_INT_2_STR[value_type]) else: community.append("%s:%s" % (value_list[0], value_list[1])) value_list = value_list[2:] except Exception: raise BGPNotification(3, 5) return cls(value=community)
def pack(cls, data, capability): as_path_raw = b'' for segment in data: as_seg_raw = b'' seg_type = segment[0] as_path_list = segment[1] if seg_type not in [cls.AS_SET, cls.AS_SEQUENCE, cls.AS_CONFED_SET, cls.AS_CONFED_SEQUENCE]: raise BGPNotification(3, 11) as_count = 0 for asn in as_path_list: if capability.get('asn4'): as_seg_raw += struct.pack('!I', asn) else: as_seg_raw += struct.pack('!H', asn) as_count += 1 as_path_raw += struct.pack('!B', seg_type) + struct.pack('!B', as_count) + as_seg_raw return cls(value=data, hex_value=as_path_raw)
def pack(cls, data, capability): if data not in range(0, 65536): raise BGPNotification(3, 5) return cls(value=data, hex_value=struct.pack('!I', data))
def unpack(cls, data, capability): if len(data) != 4: raise BGPNotification(3, 5) return cls(value=struct.unpack('!I', data)[0])
def pack(cls, data, capability): try: return cls(data, IPAddress.pack(data)) except: raise BGPNotification(3, 5)
def unpack(cls, data, capability): origin = struct.unpack('!B', data)[0] if origin not in [cls.IGP, cls.EGP, cls.INCOMPLETE]: raise BGPNotification(3, 6) return cls(value=origin)
def pack(cls, data, capability): if data not in [cls.IGP, cls.EGP, cls.INCOMPLETE]: raise BGPNotification(3, 6) return cls(data, struct.pack('!B', data))
def unpack(cls, data, length, capability): if len(data) != 0: raise BGPNotification(1, 2) return cls(value=None, length=length + cls.HDR_LEN)