def parse(cls, value, asn4=False): """ Parse AS PATH attributes. :param asn4: 4 bytes asn or not :param value: raw binary balue """ # Loop over all path segments aspath = [] while len(value) > 0: seg_type, length = struct.unpack('!BB', value[:2]) if seg_type not in [ cls.AS_SET, cls.AS_SEQUENCE, cls.AS_CONFED_SEQUENCE, cls.AS_CONFED_SET ]: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_MALFORMED_ASPATH, data=repr(value)) try: if asn4: segment = list( struct.unpack('!%dI' % length, value[2:2 + length * 4])) value = value[2 + length * 4:] else: segment = list( struct.unpack('!%dH' % length, value[2:2 + length * 2])) value = value[2 + length * 2:] except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data='') aspath.append((seg_type, segment)) return aspath
def construct(cls, value): try: if netaddr.IPAddress(value).version == 4: ip_addr_raw = netaddr.IPAddress(value).packed return struct.pack( '!B', cls.FLAG) + struct.pack('!B', cls.ID) + struct.pack( '!B', len(ip_addr_raw)) + ip_addr_raw else: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_INVALID_NEXTHOP, data=value) except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_INVALID_NEXTHOP, data=value)
def parse(cls, value): if len(value) % 4 == 0: next_hop = str(socket.inet_ntoa(struct.unpack("!I", value[0:4]))) return next_hop else: # Error process raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value)
def construct(cls, value): if value not in [cls.IGP, cls.EGP, cls.INCOMPLETE]: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_INVALID_ORIGIN, data='') length = 1 return struct.pack('!B', cls.FLAG) + struct.pack( '!B', cls.ID) + struct.pack('!B', length) + struct.pack( '!B', value)
def construct(cls, value): cluster_raw = b'' try: for cluster in value: cluster_raw += netaddr.IPAddress(cluster).packed return struct.pack("!B", cls.FLAG) + struct.pack('!B', cls.ID)+ struct.pack("!B", len(cluster_raw)) + cluster_raw except Exception: raise excep.UpdateMessageError(sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN,data=struct.pack('B', cls.FLAG))
def parse(cls, value): cluster_list = [] if len(value) % 4 != 0: raise excep.UpdateMessageError(sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN,data=repr(value)) while value: cluster_list.append(str(netaddr.IPAddress(struct.unpack('!I', value[0:4])[0]))) value = value[4:] return cluster_list
def construct(cls, value): try: return struct.pack('!B', cls.FLAG) + struct.pack( '!B', cls.ID) + struct.pack('!B', 4) + struct.pack( '!I', int(netaddr.IPAddress(ip))) except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value)
def parse(cls, value): """ parse bgp local preference attribute :param value: raw binary value """ try: return struct.unpack('!I', value)[0] except: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value)
def construct(cls, value): """ encode bgp local preference attribute :param value: interger value """ try: return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) + struct.pack('!B', 4) + struct.pack('!I', value) except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN,data='')
def parse(cls, value, asn4=False): if not asn4: try: asn = struct.unpack('!H', value[:2])[0] aggregator = str(netaddr.IPAddress(struct.unpack('!I', value[2:])[0])) except Exception: raise excep.UpdateMessageError(sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN,data=value) else: # 4 bytes ASN try: asn = struct.unpack('!I', value[:4])[0] aggregator = str(netaddr.IPAddress(struct.unpack('!I', value[4:])[0])) except Exception: raise excep.UpdateMessageError(sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN,data=value) return asn, aggregator
def construct(cls, value, asn4=False): try: if asn4: agg_raw = struct.pack('!I', value[0]) + netaddr.IPAddress(value[1]).packed else: agg_raw = struct.pack('!H', value[0]) + netaddr.IPAddress(value[1]).packed return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) + struct.pack('!B', len(agg_raw)) + agg_raw except Exception: raise excep.UpdateMessageError(sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN,data=value)
def parse(cls, value): """ parse bgp ATOMIC_AGGREGATE attribute :param value: """ if not value: return value else: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_OPTIONAL_ATTR, data=value)
def construct(cls, value): """construct a ATOMIC_AGGREGATE path attribute :param value: """ if value: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_OPTIONAL_ATTR, data='') else: value = 0 return struct.pack('!B', cls.FLAG) + struct.pack( '!B', cls.ID) + struct.pack('!B', value)
def construct(cls, value, asn4=False): """ Construct AS PATH. :param asn4: 4byte asn :param value: """ # value example # [(2, [3257, 31027, 34848, 21465])], or [(3, [64606]), (2, [64624, 65515])] flags = cls.FLAG as_path_raw = b'' if value != 0: for segment in value: 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 ]: assert excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_MALFORMED_ASPATH, data='') if asn4: # 4 bytes asn encode as_count = 0 for asn in as_path_list: as_count += 1 as_seg_raw += struct.pack('!I', asn) else: # 2 bytes asn encode as_count = 0 for asn in as_path_list: as_count += 1 as_seg_raw += struct.pack('!H', asn) as_path_raw += struct.pack('!B', seg_type) + struct.pack( '!B', as_count) + as_seg_raw if len(as_path_raw) > 255: flags += AttributeFlag.EXTENDED_LENGTH return struct.pack( '!B', flags) + struct.pack('!B', cls.ID) + struct.pack( '!H', len(as_path_raw)) + as_path_raw else: return struct.pack( '!B', flags) + struct.pack('!B', cls.ID) + struct.pack( '!B', len(as_path_raw)) + as_path_raw else: length = 0 return struct.pack('!B', flags) + struct.pack( '!B', cls.ID) + struct.pack('!B', length)
def parse_prefix_list(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]) if prefix_len > 32: LOG.warning('Prefix Length larger than 32') raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_INVALID_NETWORK_FIELD, data=repr(data)) 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) 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 parse(cls, value): if len(value) != 4: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value) return str(socket.inet_ntoa(struct.unpack("!I", value[0:4])))
def parse(cls, value): if len(value) % 8 != 0: raise excep.UpdateMessageError(sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN,data=value) ext_community = [] while value: comm_type, subtype = struct.unpack('!BB', value[0:2]) value_tmp = value[2:8] comm_code = comm_type * 256 + subtype if comm_code == bgp_cons.BGP_EXT_COM_RT_0: # Route Target, Format AS(2bytes):AN(4bytes) asn, an = struct.unpack('!HI', value_tmp) ext_community.append([bgp_cons.BGP_EXT_COM_RT_0, '%s:%s' % (asn, an)]) elif comm_code == bgp_cons.BGP_EXT_COM_RT_1: # Route Target,Format IPv4 address(4bytes):AN(2bytes) ipv4 = str(netaddr.IPAddress(struct.unpack('!I', value_tmp[0:4])[0])) an = struct.unpack('!H', value_tmp[4:])[0] ext_community.append([bgp_cons.BGP_EXT_COM_RT_1, '%s:%s' % (ipv4, an)]) elif comm_code == bgp_cons.BGP_EXT_COM_RT_2: # Route Target,Format AS(4bytes):AN(2bytes) asn, an = struct.unpack('!IH', value_tmp) ext_community.append([bgp_cons.BGP_EXT_COM_RT_2, '%s:%s' % (asn, an)]) elif comm_code == bgp_cons.BGP_EXT_COM_RO_0: # Route Origin,Format AS(2bytes):AN(4bytes) asn, an = struct.unpack('!HI', value_tmp) ext_community.append([bgp_cons.BGP_EXT_COM_RO_0, '%s:%s' % (asn, an)]) elif comm_code == bgp_cons.BGP_EXT_COM_RO_1: # Route Origin,Format IP address:AN(2bytes) ipv4 = str(netaddr.IPAddress(struct.unpack('!I', value_tmp[0:4])[0])) an = struct.unpack('!H', value_tmp[4:])[0] ext_community.append([bgp_cons.BGP_EXT_COM_RO_1, '%s:%s' % (ipv4, an)]) elif comm_code == bgp_cons.BGP_EXT_COM_RO_2: # Route Origin,Format AS(2bytes):AN(4bytes) asn, an = struct.unpack('!IH', value_tmp) ext_community.append([bgp_cons.BGP_EXT_COM_RO_2, '%s:%s' % (asn, an)]) elif comm_code == bgp_cons.BGP_EXT_REDIRECT_NH: ipv4 = str(netaddr.IPAddress(int(binascii.b2a_hex(value_tmp[0:4]), 16))) copy_flag = struct.unpack('!H', value_tmp[4:])[0] ext_community.append([bgp_cons.BGP_EXT_REDIRECT_NH, ipv4, copy_flag]) elif comm_code == bgp_cons.BGP_EXT_TRA_RATE: asn, rate = struct.unpack('!Hf', value_tmp) ext_community.append([bgp_cons.BGP_EXT_TRA_RATE, '%s:%s' % (asn, int(rate))]) """ # BGP Flow spec elif comm_code == bgp_cons.BGP_EXT_REDIRECT_NH: ipv4 = str(netaddr.IPAddress(int(binascii.b2a_hex(value_tmp[0:4]), 16))) copy_flag = struct.unpack('!H', value_tmp[4:])[0] ext_community.append([bgp_cons.BGP_EXT_REDIRECT_NH, ipv4, copy_flag]) elif comm_code == bgp_cons.BGP_EXT_TRA_RATE: asn, rate = struct.unpack('!Hf', value_tmp) ext_community.append([bgp_cons.BGP_EXT_TRA_RATE, '%s:%s' % (asn, int(rate))]) elif comm_code == bgp_cons.BGP_EXT_TRA_ACTION: bit_value = parse_bit(value_tmp[-1]) ext_community.append([bgp_cons.BGP_EXT_TRA_ACTION, {'S': bit_value['6'], 'T': bit_value['7']}]) elif comm_code == bgp_cons.BGP_EXT_REDIRECT_VRF: asn, an = struct.unpack('!HI', value_tmp) ext_community.append([bgp_cons.BGP_EXT_REDIRECT_VRF, '%s:%s' % (asn, an)]) elif comm_code == bgp_cons.BGP_EXT_TRA_MARK: mark = struct.unpack('!B', value_tmp[-1])[0] ext_community.append([bgp_cons.BGP_EXT_TRA_MARK, mark]) else: ext_community.append([bgp_cons.BGP_EXT_COM_UNKNOW, repr(value_tmp)]) LOG.warn('unknow bgp extended community, type=%s, value=%s', comm_code, repr(value_tmp)) """ value = value[8:] return ext_community
def parse_attributes(data, asn4=False): """ Parses an RFC4271 encoded blob of BGP attributes into a list :param data: :param asn4: support 4 bytes asn or not :return: """ attributes = {} postfix = data while len(postfix) > 0: try: flags, type_code = struct.unpack('!BB', postfix[:2]) if flags & AttributeFlag.EXTENDED_LENGTH: attr_len = struct.unpack('!H', postfix[2:4])[0] attr_value = postfix[4:4 + attr_len] postfix = postfix[4 + attr_len:] # Next attribute else: # standard 1-octet length if isinstance(postfix[2], int): attr_len = postfix[2] else: attr_len = ord(postfix[2]) attr_value = postfix[3:3 + attr_len] postfix = postfix[3 + attr_len:] # Next attribute except Exception as e: LOG.error(e) error_str = traceback.format_exc() LOG.debug(error_str) raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_MALFORMED_ATTR_LIST, data='') if type_code == bgp_cons.BGPTYPE_ORIGIN: decode_value = Origin.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_AS_PATH: decode_value = ASPath.parse(value=attr_value, asn4=asn4) elif type_code == bgp_cons.BGPTYPE_NEXT_HOP: decode_value = NextHop.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_MULTI_EXIT_DISC: decode_value = MED.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_LOCAL_PREF: decode_value = LocalPreference.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_ATOMIC_AGGREGATE: decode_value = AtomicAggregate.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_AGGREGATOR: decode_value = Aggregator.parse(value=attr_value, asn4=asn4) elif type_code == bgp_cons.BGPTYPE_COMMUNITIES: decode_value = Community.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_ORIGINATOR_ID: decode_value = OriginatorID.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_CLUSTER_LIST: decode_value = ClusterList.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_NEW_AS_PATH: decode_value = ASPath.parse(value=attr_value, asn4=True) elif type_code == bgp_cons.BGPTYPE_NEW_AGGREGATOR: decode_value = Aggregator.parse(value=attr_value, asn4=True) elif type_code == bgp_cons.BGPTYPE_MP_REACH_NLRI: decode_value = MpReachNLRI.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_MP_UNREACH_NLRI: decode_value = MpUnReachNLRI.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_EXTENDED_COMMUNITY: decode_value = ExtCommunity.parse(value=attr_value) else: decode_value = repr(attr_value) attributes[type_code] = decode_value return attributes
def parse(cls, value): origin = struct.unpack('!B', value)[0] if orgin not in [cls.IGP, cls.EGP, cls.INCOMPLETE]: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_INVALID_ORIGIN, data=value) return origin