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 parse(cls, value, asn4=False): """ Parse Aggregator attributes. :param value: raw binary value :param asn4: support 4 bytes asn or not """ 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 parse(cls, value): try: afi, safi = struct.unpack('!HB', value[0:3]) except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data='') nlri_bin = value[3:] # for IPv4 if afi == afn.AFNUM_INET: # VPNv4 if safi == safn.SAFNUM_LAB_VPNUNICAST: nlri = IPv4MPLSVPN.parse(nlri_bin, iswithdraw=True) return dict(afi_safi=(afi, safi), withdraw=nlri) # BGP flow spec elif safi == safn.SAFNUM_FSPEC_RULE: # if nlri length is greater than 240 bytes, it is encoded over 2 bytes withdraw_list = [] while nlri_bin: length = ord(nlri_bin[0]) if length >> 4 == 0xf and len(nlri_bin) > 2: length = struct.unpack('!H', nlri_bin[:2])[0] nlri_tmp = nlri_bin[2:length + 2] nlri_bin = nlri_bin[length + 2:] else: nlri_tmp = nlri_bin[1:length + 1] nlri_bin = nlri_bin[length + 1:] nlri = IPv4FlowSpec.parse(nlri_tmp) if nlri: withdraw_list.append(nlri) return dict(afi_safi=(afi, safi), withdraw=withdraw_list) else: return dict(afi_safi=(afn.AFNUM_INET, safi), withdraw=repr(nlri_bin)) # for ipv6 elif afi == afn.AFNUM_INET6: # for ipv6 unicast if safi == safn.SAFNUM_UNICAST: return dict(afi_safi=(afi, safi), withdraw=IPv6Unicast.parse(nlri_data=nlri_bin)) elif safi == safn.SAFNUM_LAB_VPNUNICAST: return dict(afi_safi=(afi, safi), withdraw=IPv6MPLSVPN.parse(value=nlri_bin, iswithdraw=True)) else: return dict(afi_safi=(afi, safi), withdraw=repr(nlri_bin)) # for l2vpn elif afi == afn.AFNUM_L2VPN: # for evpn if safi == safn.SAFNUM_EVPN: return dict(afi_safi=(afi, safi), withdraw=EVPN.parse(nlri_data=nlri_bin)) else: return dict(afi_safi=(afi, safi), withdraw=repr(nlri_bin)) else: return dict(afi_safi=(afi, safi), withdraw=repr(nlri_bin))
def construct(cls, value): """ encode BGP nexthop attribute. :param value: ipv4 format string like 1.1.1.1 """ 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): """ Parse originator id :param value: """ if len(value) != 4: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value) return str(netaddr.IPAddress(int(binascii.b2a_hex(value[0:4]), 16)))
def parse(cls, value): """ parse BGP med attributes :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 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): """ encode BGP med attributes :param 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 construct(cls, value): """ construct a origin attribute :param value: 0,1,2 """ 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): """ construct a ORIGINATOR_ID path attribute :param value: ipv4 format string """ try: return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) \ + struct.pack('!B', 4) + netaddr.IPAddress(value).packed except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, 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 parse(cls, value): """ parse bgp ATOMIC_AGGREGATE attribute :param value: """ if not value: # return value # return str(value, encoding="utf-8") return bytes.decode(value) else: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_OPTIONAL_ATTR, data=value)
def parse(cls, value): """ Parse BGP nexthop. :param value: raw binary value """ if len(value) % 4 == 0: next_hop = str( netaddr.IPAddress(int(binascii.b2a_hex(value[0:4]), 16))) return next_hop else: # Error process raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value)
def parse(cls, value): """ Error process: (1) the falgs of the ORIGIN attribute must be "well-know,transitive" (2) If the ORIGIN attribute has an undefined value, then the Error Sub-code MUST be set to Invalid Origin Attribute. The Data field MUST contain the unrecognized attribute (type,length, and vlaue) :param value: raw binary value """ orgin = ord(value[0:1]) 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 orgin
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): """ Parse culster list :param 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): """ construct a CLUSTER_LIST path attribute :param 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 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])] as_path_raw = b'' 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 flags = cls.FLAG 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
def construct(cls, value): """ construct a LARGE COMMUNITY path attribute :param value: """ large_community_hex = b'' for large_community in value: try: value = large_community.split(':') for sub_value in value: large_community_hex += struct.pack('!I', int(sub_value)) except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value) return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) \ + struct.pack('!B', len(large_community_hex)) + large_community_hex
def construct(cls, value, asn4=False): """ Construct aggregator. :param value: :param asn4: """ 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 large community. :param value: """ large_community = [] if value: try: length = len(value) / 4 value_list = list(struct.unpack('!%di' % length, value)) while value_list: large_community.append( "%s:%s:%s" % (value_list[0], value_list[1], value_list[2])) value_list = value_list[3:] except Exception: raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value) return large_community
def parse(cls, value): try: afi, safi, nexthop_length = struct.unpack('!HBB', value[0:4]) nexthop_data = value[4:4 + nexthop_length] nlri_data = value[5 + nexthop_length:] except Exception: # error when lenght is wrong raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=repr(value)) if afi == afn.AFNUM_INET: if safi == safn.SAFNUM_LAB_VPNUNICAST: nlri = IPv4MPLSVPN.parse(nlri_data) return { 'afi_safi': (afi, safi), 'nexthop': nexthop_data, 'nlri': nlri }
def construct(cls, value): """ construct a COMMUNITY path attribute :param value: """ community_hex = b'' for community in value: if community.upper() in bgp_cons.WELL_KNOW_COMMUNITY_STR_2_INT: value = bgp_cons.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 excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value) return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) \ + struct.pack('!B', len(community_hex)) + community_hex
def parse(cls, value): """ parse BGP community. :param value: """ community = [] if value: try: length = len(value) / 2 value_list = list(struct.unpack('!%dH' % length, value)) while value_list: value_type = value_list[ 0] * 16 * 16 * 16 * 16 + value_list[1] if value_type in bgp_cons.WELL_KNOW_COMMUNITY_INT_2_STR: community.append( bgp_cons.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 excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=value) return community
def parse(cls, value): try: afi, safi, nexthop_length = struct.unpack('!HBB', value[0:4]) nexthop_bin = value[4:4 + nexthop_length] nlri_bin = value[5 + nexthop_length:] except Exception: # error when lenght is wrong raise excep.UpdateMessageError( sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, data=repr(value)) # Address Family IPv4 if afi == afn.AFNUM_INET: if safi == safn.SAFNUM_LAB_VPNUNICAST: # MPLS VPN # parse nexthop rd_bin = nexthop_bin[0:8] rd_type = struct.unpack('!H', rd_bin[0:2])[0] rd_value_bin = rd_bin[2:] if rd_type == 0: asn, an = struct.unpack('!HI', rd_value_bin) ipv4 = str( netaddr.IPAddress( int(binascii.b2a_hex(nexthop_bin[8:]), 16))) nexthop = {'rd': '%s:%s' % (asn, an), 'str': ipv4} # TODO(xiaoquwl) for other RD type decoding else: nexthop = repr(nexthop_bin[8:]) # parse nlri nlri = IPv4MPLSVPN.parse(nlri_bin) return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri) elif safi == safn.SAFNUM_FSPEC_RULE: # if nlri length is greater than 240 bytes, it is encoded over 2 bytes if len(nlri_bin) >= 240: nlri_bin = nlri_bin[2:] else: nlri_bin = nlri_bin[1:] nlri = IPv4FlowSpec.parse(nlri_bin) return dict(afi_safi=(afi, safi), nexthop=nexthop_bin, nlri=nlri) else: nlri = repr(nlri_bin) # # Address Family IPv6 elif afi == afn.AFNUM_INET6: # IPv6 unicast if safi == safn.SAFNUM_UNICAST: # decode nexthop # RFC 2545 # The value of the Length of Next Hop Network Address field on a # MP_REACH_NLRI attribute shall be set to 16, when only a global # address is present, or 32 if a link-local address is also included in # the Next Hop field. # # The link-local address shall be included in the Next Hop field if and # only if the BGP speaker shares a common subnet with the entity # identified by the global IPv6 address carried in the Network Address # of Next Hop field and the peer the route is being advertised to. nexthop_addrlen = 16 has_link_local = False nexthop = str( netaddr.IPAddress( int(binascii.b2a_hex(nexthop_bin[:nexthop_addrlen]), 16))) if len(nexthop_bin) == 2 * nexthop_addrlen: # has link local address has_link_local = True linklocal_nexthop = str( netaddr.IPAddress( int( binascii.b2a_hex( nexthop_bin[nexthop_addrlen:]), 16))) nlri = IPv6Unicast.parse(nlri_bin) if has_link_local: return dict(afi_safi=(afi, safi), nexthop=nexthop, linklocal_nexthop=linklocal_nexthop, nlri=nlri) else: return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri) elif safi == safn.SAFNUM_LAB_VPNUNICAST: # IPv6 MPLS VPN # parse nexthop rd_bin = nexthop_bin[0:8] rd_type = struct.unpack('!H', rd_bin[0:2])[0] rd_value_bin = rd_bin[2:] if rd_type == 0: asn, an = struct.unpack('!HI', rd_value_bin) ipv6 = str( netaddr.IPAddress( int(binascii.b2a_hex(nexthop_bin[8:]), 16))) nexthop = {'rd': '%s:%s' % (asn, an), 'str': ipv6} # TODO(xiaoquwl) for other RD type decoding else: nexthop = repr(nexthop_bin[8:]) # parse nlri nlri = IPv6MPLSVPN.parse(nlri_bin) return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri) else: return dict(afi_safi=(afi, safi), nexthop=nexthop_bin, nlri=nlri_bin) # for l2vpn elif afi == afn.AFNUM_L2VPN: if safi == safn.SAFNUM_EVPN: nexthop = str( netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin), 16))) nlri = EVPN.parse(nlri_bin) return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri) else: nlri = repr(nlri_bin) else: nlri = repr(nlri_bin) return dict(afi_safi=(afi, safi), nexthop=nexthop_bin, nlri=nlri_bin)
def parse(cls, value): """ Each Extended Community is encoded as an 8-octet quantity, as follows: - Type Field : 1 or 2 octets - Value Field : Remaining octets 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type high | Type low(*) | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Value | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Parse Extended Community attributes. :param value : value """ # devide every ext community 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 # Route Target 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)]) # Route Origin 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)]) # 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]) # Transitive Opaque elif comm_code == bgp_cons.BGP_EXT_COM_ENCAP: ext_community.append([bgp_cons.BGP_EXT_COM_ENCAP, struct.unpack('!I', value_tmp[2:])[0]]) # EVPN elif comm_code == bgp_cons.BGP_EXT_COM_EVPN_ES_IMPORT: mac = str(netaddr.EUI(int(binascii.b2a_hex(value_tmp), 16))) ext_community.append([comm_code, mac]) elif comm_code == bgp_cons.BGP_EXT_COM_EVPN_MAC_MOBIL: flag = ord(value_tmp[0:1]) seq = struct.unpack('!I', value_tmp[2:])[0] ext_community.append([comm_code, flag, seq]) elif comm_code == bgp_cons.BGP_EXT_COM_EVPN_ESI_MPLS_LABEL: flag = ord(value_tmp[0:1]) label = struct.unpack('!L', b'\00'+value_tmp[3:])[0] label >>= 4 ext_community.append([comm_code, flag, label]) elif comm_code == bgp_cons.BGP_EXT_COM_EVPN_ROUTE_MAC: ext_community.append([comm_code, str(netaddr.EUI(int(binascii.b2a_hex(value_tmp), 16)))]) 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(cls, value): """ Each Extended Community is encoded as an 8-octet quantity, as follows: - Type Field : 1 or 2 octets - Value Field : Remaining octets 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type high | Type low(*) | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Value | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Parse Extended Community attributes. :param value : value """ # devide every ext community 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 # Route Target 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))) # Route Origin 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))) 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_LINK_STATE: decode_value = LinkState.parse(value=attr_value).dict()[29] 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) elif type_code == bgp_cons.BGPTYPE_PMSI_TUNNEL: decode_value = PMSITunnel.parse(value=attr_value) else: decode_value = binascii.b2a_hex(attr_value) attributes[type_code] = decode_value return attributes
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 bgpls_pro_id = None bgpls_attr = None 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_LARGE_COMMUNITY: decode_value = LargeCommunity.parse(value=attr_value) elif type_code == bgp_cons.BGPTYPE_MP_REACH_NLRI: decode_value = MpReachNLRI.parse(value=attr_value) if decode_value['nlri'][0] and type( decode_value['nlri'][0]) is dict: if decode_value['nlri'][0].get("protocol_id"): bgpls_pro_id = decode_value['nlri'][0]["protocol_id"] 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) elif type_code == bgp_cons.BGPTYPE_PMSI_TUNNEL: decode_value = PMSITunnel.parse(value=attr_value) pmsi_hex = attr_value elif type_code == bgp_cons.BGPTYPE_LINK_STATE: if bgpls_pro_id: attributes.update( LinkState.unpack(bgpls_pro_id=bgpls_pro_id, data=attr_value).dict()) else: bgpls_attr = attr_value continue else: decode_value = binascii.b2a_hex(attr_value) attributes[type_code] = decode_value if bgpls_attr: attributes.update( LinkState.unpack(bgpls_pro_id=bgpls_pro_id, data=attr_value).dict()) evpn_overlay = EVPN.signal_evpn_overlay(attributes) if evpn_overlay['evpn'] and evpn_overlay['encap_ec']: if bgp_cons.BGPTYPE_PMSI_TUNNEL in attributes: attributes[bgp_cons.BGPTYPE_PMSI_TUNNEL] = PMSITunnel.parse( value=pmsi_hex, evpn_overlay=evpn_overlay) return attributes