def ParseBmpHeaderV3(header, verbose=False): """Parse a BMP V3 header. Args: header: array containing BMP message header. verbose: be chatty, or not. Returns: An int indicating the type of message that follows the header, and a list of strings to print. Raises: ValueError: an unexpected value was found in the message """ indent_str = indent.IndentLevel(indent.BMP_HEADER_INDENT) print_msg = [] version = 3 msg_length = struct.unpack(">L", header[0:4]) msg_type = header[4] # Decide what to format as text # print_msg.append( "%sBMP version %d type %s length %d\n" % (indent_str, version, MSG_TYPE_STR[msg_type], msg_length[0])) # Return the message type so the caller can decide what to do next, # and the list of strings representing the collected message. # return msg_type, msg_length[0], print_msg
def ParseBmpPeerUp(message, peer_flags, verbose=False): """Parse a BMP V3 Peer Up message. Args: header: array containing BMP peer up message. peer_flags: from the per-peer header. verbose: be chatty, or not. Returns: A list of strings to print. Raises: ValueError: an unexpected value was found in the message """ indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT) print_msg = [] offset = 0 if peer_flags & PEER_FLAG_IPV6: loc_addr = socket.inet_ntop(socket.AF_INET6, message[offset:offset + 16]) else: loc_addr = socket.inet_ntop(socket.AF_INET, message[offset + 12:offset + 16]) offset += 16 loc_port, rem_port = struct.unpack_from(">HH", message, offset) print_msg.append("%sloc_addr %s, loc_port %d, rem_port %d\n" % (indent_str, loc_addr, loc_port, rem_port)) return print_msg
def CollectBmpPeerUp(sock, verbose=False): """Collect a BMP Peer Up message. Args: sock: socket from which to read. verbose: be chatty, or not. Returns: nothing Raises: ValueError: an unexpected value was found in the message """ indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT) print_msg = [] # collect a per peer header # per_peer_header = CollectBytes(sock, BMP.PER_PEER_HEADER_LEN_V3) peer_flags, msg_text = BMP.ParseBmpPerPeerHeaderV3(per_peer_header, verbose=verbose) print_msg += "".join(msg_text) # collect local address, local and remote ports # peer_up_msg = CollectBytes(sock, BMP.PEER_UP_LEN) if verbose: msg_text = BMP.ParseBmpPeerUp(peer_up_msg, peer_flags, verbose=verbose) print_msg += "".join(msg_text) # sent BGP OPEN message # sent_header = CollectBytes(sock, BGP.HEADER_LEN) length, msg_type, hdr_text = BGP.ParseBgpHeader(sent_header, verbose=verbose) assert msg_type == BGP.OPEN print_msg += "".join(hdr_text) sent_open = CollectBytes(sock, length) sent_text = BGP.ParseBgpOpen(sent_open, length) print_msg += "".join(sent_text) # received BGP OPEN message # recv_header = CollectBytes(sock, BGP.HEADER_LEN) length, msg_type, hdr_text = BGP.ParseBgpHeader(recv_header, verbose=verbose) assert msg_type == BGP.OPEN print_msg += "".join(hdr_text) recv_open = CollectBytes(sock, length) recv_text = BGP.ParseBgpOpen(recv_open, length) print_msg += "".join(recv_text) # Return list of strings representing collected message. # return print_msg
def ParseBgpHeader(header, verbose=False): """Parse a BGP header into text, see RFC4271 section 4.1. Args: header: a buffer containing a BGP message header. verbose: be chatty, or not. Returns: An int indicating the length of the rest of the BGP message, an int indication the type of the message, a list of strings to print. Raises: ValueError: an invalid value was found in the message. """ print_msg = [] indent_str = indent.IndentLevel(indent.BGP_HEADER_INDENT) try: # Verify that the marker is correct, raise a ValueError exception if # it is not. # for x in range(0, 15): if header[x] != 255: raise ValueError("BGP marker octet %d != 255" % x) # Unpack the length and type. # length, msg_type = struct.unpack(">HB", header[16:19]) if length < MIN_LENGTH or length > MAX_LENGTH: raise ValueError("BGP message length %d incorrect" % length) if msg_type not in MSG_TYPE_STR: raise ValueError("BGP message type %d unknown" % msg_type) print_msg.append("%sBGP %s" % (indent_str, MSG_TYPE_STR[msg_type])) if verbose: print_msg.append(" length %d\n" % (length - HEADER_LEN)) else: print_msg.append("\n") # Return the length of the rest of the PDU, its type, and the list # of strings representing the collected message # return length - HEADER_LEN, msg_type, print_msg # In case of any exception, dump the hex of the message to help # debug what's wrong with the message, and re-raise the exception. # except Exception, esc: if verbose: print str(esc) print DumpHexString(header, 0, HEADER_LEN) raise esc
def CollectBmpInitiation(sock, msg_length, verbose=False): """Collect a BMP Initiation message. Args: sock: socket from which to read. Returns: A list of strings. Raises: ValueError: an unexpected value was found in the message """ print_msg = [] indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT) # get the remainder of the message # init_msg_len = msg_length - BMP.HEADER_LEN_V3 init_msg_buf = CollectBytes(sock, init_msg_len) init_msg_pos = 0 while init_msg_pos < init_msg_len: info_type, info_len = struct.unpack_from(">HH", init_msg_buf, init_msg_pos) init_msg_pos += 4 info_str = init_msg_buf[init_msg_pos:init_msg_pos + info_len] init_msg_pos += info_len if info_type == BMP.INIT_INFO_TYPE_STRING: print_msg.append("%s%s: %s\n" % (indent_str, BMP.INIT_INFO_TYPE_STR[info_type], info_str.tostring())) elif info_type == BMP.INIT_INFO_TYPE_SYSDESCR: print_msg.append("%s%s: %s\n" % (indent_str, BMP.INIT_INFO_TYPE_STR[info_type], info_str.tostring())) elif info_type == BMP.INIT_INFO_TYPE_SYSNAME: print_msg.append("%s%s: %s\n" % (indent_str, BMP.INIT_INFO_TYPE_STR[info_type], info_str.tostring())) else: raise ValueError("Found unexpected Init Msg Info Type %d", info_type) # Return list of strings representing collected message. # return print_msg
def CollectBmpPeerDown(sock, verbose=False): """Collect a BMP Peer Down message. Args: sock: socket from which to read. verbose: be chatty, or not. Returns: nothing Raises: ValueError: an unexpected value was found in the message """ indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT) print_msg = [] reason_code = CollectBytes(sock, 1)[0] if reason_code in BMP.PEER_DOWN_REASON_STR: print_msg.append("%s%s\n" % (indent_str, BMP.PEER_DOWN_REASON_STR[reason_code])) # If the BMP message contains a BGP NOTIFICATION message, collect # and parse it. # if BMP.PeerDownHasBgpNotification(reason_code): # Collect and parse the BGP message header # header = CollectBytes(sock, BGP.HEADER_LEN) length, msg_type, msg_text = BGP.ParseBgpHeader(header, verbose=verbose) assert msg_type == BGP.NOTIFICATION print_msg.append("".join(msg_text)) # collect and parse the BGP message body # notification = CollectBytes(sock, length) msg_text = BGP.ParseBgpNotification(notification, length, verbose=verbose) print_msg.append("".join(msg_text)) elif DEBUG_FLAG: raise ValueError("Unknown BMP Peer Down reason %d" % reason_code) else: print_msg.append("Unknown BMP Peer Down reason %d\n" % reason_code) # Return list of strings representing collected message. # return print_msg
def ParseBmpPerPeerHeaderV3(header, verbose=False): """Parse a BMP V3 per-peer header. Args: header: array containing BMP message header. verbose: be chatty, or not. Returns: An int indicating the peer flags and a list of strings to print. Raises: ValueError: an unexpected value was found in the message """ indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT) print_msg = [] offset = 0 # peer type, flags, and the rest # peer_type, peer_flags = struct.unpack_from(">BB", header, offset) offset += 2 peer_dist = struct.unpack_from("8B", header, offset) offset += 8 if peer_flags & PEER_FLAG_IPV6: peer_address = socket.inet_ntop(socket.AF_INET6, header[offset:offset + 16]) else: peer_address = socket.inet_ntop(socket.AF_INET, header[offset + 12:offset + 16]) offset += 16 peer_as, peer_bgp_id, time_sec, time_usec = struct.unpack_from( ">LLLL", header, offset) # Decide what to format as text # print_msg.append("%sPeer Type %s, flags %d, address %s, AS %d\n" % (indent_str, PEER_TYPE_STR[peer_type], peer_flags, peer_address, peer_as)) time_str = time.strftime("%Y-%m-%d %H:%M", time.gmtime(time_sec)) time_frac = time_usec / 1000000.0 print_msg.append("%sTime %s.%s\n" % (indent_str, time_str, re.split('\.', str(time_frac))[1])) # Return the message type so the caller can decide what to do next, # and the list of strings representing the collected message. # return peer_flags, print_msg
def CollectBmpTermination(sock, msg_length, verbose=False): """Collect a BMP Termination message. Args: sock: socket from which to read. Returns: A list of strings. Raises: ValueError: an unexpected value was found in the message """ print_msg = [] indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT) # get the remainder of the message # term_msg_len = msg_length - BMP.HEADER_LEN_V3 term_msg_buf = CollectBytes(sock, term_msg_len) term_msg_pos = 0 while term_msg_pos < term_msg_len: info_type, info_len = struct.unpack_from(">HH", term_msg_buf, term_msg_pos) term_msg_pos += 4 info_str = term_msg_buf[term_msg_pos:term_msg_pos + info_len] term_msg_pos += info_len if info_type == BMP.TERM_INFO_TYPE_STRING: print_msg.append("%s%s: %s\n" % (indent_str, BMP.TERM_INFO_TYPE_STR[info_type], info_str.tostring())) elif info_type == BMP.TERM_INFO_TYPE_REASON: reason_code = struct.unpack(">H", info_str)[0] print_msg.append( "%s%s: %d (%s)\n" % (indent_str, BMP.TERM_INFO_TYPE_STR[info_type], reason_code, BMP.TERM_INFO_REASON_STR[reason_code])) else: raise ValueError("Found unexpected Init Msg Info Type %d", info_type) # Return list of strings representing collected message. # return print_msg
def ParseBmpHeaderV1(header, verbose=False): """Parse a BMP V1 header. Args: header: array containing BMP message header. verbose: be chatty, or not. Returns: An int indicating the type of message that follows the header, and a list of strings to print. Raises: ValueError: an unexpected value was found in the message """ indent_str = indent.IndentLevel(indent.BMP_HEADER_INDENT) print_msg = [] version = 1 msg_type, peer_type, peer_flags = struct.unpack(">BBB", header[0:3]) if peer_flags & PEER_FLAG_IPV6: peer_address = socket.inet_ntop(socket.AF_INET6, header[11:27]) else: peer_address = socket.inet_ntop(socket.AF_INET, header[23:27]) peer_as, time_sec = struct.unpack(">LxxxxL", header[27:39]) # If we have a version mismatch, we're pretty much done here. # if version != 1: raise ValueError("Found BMP version %d, expecting %d" % (version, 1)) # Decide what to format as text # print_msg.append( "%sBMP version %d type %s peer %s AS %d\n" % (indent_str, version, MSG_TYPE_STR[msg_type], peer_address, peer_as)) if verbose: print_msg.append("%speer_type %s" % (indent_str, PEER_TYPE_STR[peer_type])) print_msg.append(" peer_flags 0x%x" % peer_flags) print_msg.append(" router_id %s\n" % socket.inet_ntoa(header[31:34])) print_msg.append("%stime %s\n" % (indent_str, time.ctime(time_sec))) # Return the message type so the caller can decide what to do next, # and the list of strings representing the collected message. # return msg_type, print_msg
def ParseBgpNotification(notification, length, verbose=False): """Parse a BGP Notification message, see RFC4271 section 4.5. Args: notification: the body of a BGP Notification message. length: the length of the message body. verbose: be chatty, or not. Returns: A list of strings to print Raises: ValueError: a code or subcode value is invalid per RFC4271 """ print_msg = [] indent_str = indent.IndentLevel(indent.BGP_CONTENT_INDENT) code, subcode = struct.unpack(">BB", notification[0:2]) if code not in NOTIFICATION_CODE: raise ValueError("BGP NOTIFICATION code %d is invalid" % code) code_str = NOTIFICATION_CODE[code] if code in NOTIFICATION_SUBCODE: if subcode not in NOTIFICATION_SUBCODE[code]: raise ValueError("BGP NOTIFICATION code %d subcode %d is invalid" % (code, subcode)) else: subcode_str = NOTIFICATION_SUBCODE[code][subcode] print_msg.append("%sNOTIFICATION code %s subcode %s\n" % (indent_str, code_str, subcode_str)) else: print_msg.append("%sNOTIFICATION code %s\n" % (indent_str, code_str)) # If there are data bytes, convert them to text as hex digits. # if length > 2 and verbose: print_msg.append("%sNOTIFICATION data " % indent_str) print_msg.append(DumpHexString(notification, 3, length - 1)) print_msg.append("\n") # Return the list of strings representing collected message. # return print_msg
def CollectBmpStatsMsg(sock): """Collect a BMP Statistics Report message. Args: sock: socket from which to read. Returns: A list of strings. """ print_msg = [] indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT) # Find out many TLVs (Type-Length-Value) there are in the message. # stats_count_buf = CollectBytes(sock, 4) stats_count = struct.unpack(">L", stats_count_buf)[0] # Read all the TLVs. # for _ in xrange(stats_count): # Get the type and the length. # stat_type_len_buf = CollectBytes(sock, 4) stat_type, stat_len = struct.unpack(">HH", stat_type_len_buf) # Stat types through 6 are 32 bits, 7 and 8 are 64 # assert stat_type in BMP.SR_TYPE_STR stat_val_buf = CollectBytes(sock, stat_len) if stat_len is 4: stat_val = struct.unpack(">L", stat_val_buf)[0] elif stat_len is 8: stat_val = struct.unpack(">Q", stat_val_buf)[0] else: raise ValueError("Found unexpected stat_len %d in SR message", stat_len) print_msg.append("%s%d %s\n" % (indent_str, stat_val, BMP.SR_TYPE_STR[stat_type])) # Return list of strings representing collected message. # return print_msg
def ParseBgpRouteRefresh(message, length): """Parse a BGP ROUTE-REFRESH message; see RFC2918. Args: message: the body of a BGP ROUTE-REFRESH message. length: the length of the message. Returns: A list of strings to print Raises: ValueError: an unexpected value was found in the message """ print_msg = [] indent_str = indent.IndentLevel(indent.BGP_CONTENT_INDENT) offset = 0 # the length is fixed, check it # if length is not 4: raise ValueError("unexpected length %d for ROUTE-REFRESH message" % length) # ROUTE-REFRESH messages are very simple: AFI, reserved octet, SAFI. # afi, reserved, safi = struct.unpack_from(">HBB", message, offset) # validate the message parts # if afi not in AF_STR: raise ValueError("unknown AFI %d in ROUTE-REFRESH message" % afi) if reserved: raise ValueError("reserved not zero in ROUTE-REFRESH message") if safi not in MP_SAFI_STR: raise ValueError("unknown SAFI %d in ROUTE-REFRESH message" % safi) # construct and return a text representation of the message # print_msg.append("%sAFI %s SAFI %s\n" % (indent_str, AF_STR[afi], MP_SAFI_STR[safi])) return print_msg
def ParseBgpOpen(message, length): """Parse a BGP OPEN message; see RFC1771. Args: message: the body of a BGP OPEN message. length: the length of the message. Returns: A list of strings to print Raises: ValueError: an unexpected value was found in the message """ print_msg = [] indent_str = indent.IndentLevel(indent.BGP_CONTENT_INDENT) offset = 0 print_msg.append("%sbgp open parsing tbd\n" % indent_str) return print_msg
def ParseBgpUpdate(update, length, rfc4893_updates=False, verbose=False): """Parse a BGP Update message; see RFC1997, RFC2858, RFC4271 4.3, RFC4893. Args: update: the body of a BGP UPDATE message. length: the length of the message. rfc4893_updates: true if update conforms to RFC4893. verbose: be chatty, or not. Returns: A list of strings to print Raises: ValueError: an unexpected value was found in the message """ print_msg = [] indent_str = indent.IndentLevel(indent.BGP_CONTENT_INDENT) offset = 0 # First section is withdrawn routes. # withdrawn_route_len = struct.unpack_from(">H", update[0:2], offset)[0] if verbose: print_msg.append("%swithdrawn at %d length %d\n" % (indent_str, offset, withdrawn_route_len)) offset += 2 # If any withdrawn routes are present, process them. # if withdrawn_route_len: withdrawn_text = ParseBgpNlri(update, offset, offset + withdrawn_route_len, AF_IP, MP_SAFI_UNICAST) if withdrawn_text: prepend_str = "%swithdraw " % indent_str sep = "\n%s" % prepend_str print_msg.append("%s%s\n" % (prepend_str, sep.join(withdrawn_text))) offset += withdrawn_route_len # Next section is path attributes # path_attr_len = struct.unpack_from(">H", update, offset)[0] if verbose: print_msg.append("%spath attributes at %d length %d\n" % (indent_str, offset, path_attr_len)) offset += 2 # If there are path attributes present, process them. # path_attr_end = offset + path_attr_len while offset < path_attr_end: # Get flags and type code. # attr_flags, attr_type = struct.unpack_from(">BB", update, offset) # If we're being verbose, describe the details of the path attribute. # We haven't updated offset yet in order to be able to report the # offset of the path attribute section in the verbose text. # if verbose: print_msg.append("%spath attr %s at %d" % (indent_str, ATTR_TYPE_STR[attr_type], offset)) print_msg.append(" flags 0x%x (" % attr_flags) attr_list = [] if (attr_flags & ATTR_FLAG_OPTIONAL) == ATTR_FLAG_OPTIONAL: attr_list.append("optional") if (attr_flags & ATTR_FLAG_TRANSITIVE) == ATTR_FLAG_TRANSITIVE: attr_list.append("transitive") if (attr_flags & ATTR_FLAG_PARTIAL) == ATTR_FLAG_PARTIAL: attr_list.append("partial") if (attr_flags & ATTR_FLAG_EXT_LEN) == ATTR_FLAG_EXT_LEN: attr_list.append("extended-length") print_msg.append(" ".join(attr_list)) # Now increment the offset, check for extended length, and get the # length (whose size depends on the extended length flag). # offset += 2 if (attr_flags & ATTR_FLAG_EXT_LEN) == ATTR_FLAG_EXT_LEN: attr_len = struct.unpack_from(">H", update, offset)[0] offset += 2 else: attr_len = update[offset] offset += 1 # Finish up the verbose processing of the path attribute's details. # if verbose: print_msg.append(") len %d\n" % attr_len) # Now we can process the specific types of path attribute, see: # RFC4271 # RFC2858 # # ORIGIN # if attr_type == ATTR_TYPE_ORIGIN: # we know both length and possible values of the ORIGIN attribute, # raise a ValueError exception if we find something unexpected # if attr_len != 1: raise ValueError("BGP ORIGIN attr_len %d wrong, expected 1" % attr_len) if update[offset] not in ORIGIN_STR: raise ValueError("BGP ORIGIN value %d wrong" % update[offset]) print_msg.append("%s%s %s\n" % (indent_str, ATTR_TYPE_STR[attr_type], ORIGIN_STR[update[offset]])) # AS_PATH (Autonomous System path) # elif attr_type == ATTR_TYPE_AS_PATH: print_msg.append("%s%s " % (indent_str, ATTR_TYPE_STR[attr_type])) path_text = ParseBgpAsPath(update, offset, offset + attr_len, rfc4893_updates) print_msg.append("%s\n" % " ".join(path_text)) # NEXT_HOP # elif attr_type == ATTR_TYPE_NEXT_HOP: next_hop = update[offset:offset + 4] print_msg.append("%s%s %s\n" % (indent_str, ATTR_TYPE_STR[attr_type], socket.inet_ntoa(next_hop))) # MED (Multi-Exit Discriminator) # elif attr_type == ATTR_TYPE_MULTI_EXIT_DISC: med_val = struct.unpack_from(">L", update, offset)[0] print_msg.append("%s%s %d\n" % (indent_str, ATTR_TYPE_STR[attr_type], med_val)) # LOCAL_PREF (Local Preference) # elif attr_type == ATTR_TYPE_LOCAL_PREF: pref_val = struct.unpack_from(">L", update, offset)[0] print_msg.append("%s%s %d\n" % (indent_str, ATTR_TYPE_STR[attr_type], pref_val)) # ATOMIC_AGGREGATE # elif attr_type == ATTR_TYPE_ATOMIC_AGGREGATE: if attr_len: raise ValueError( "attr_len %d for ATOMIC_AGGREGATE must be zero" % attr_len) print_msg.append("%s%s\n" % (indent_str, ATTR_TYPE_STR[attr_type])) # COMMUNITIES # elif attr_type == ATTR_TYPE_COMMUNITIES: print_msg.append("%s%s " % (indent_str, ATTR_TYPE_STR[attr_type])) comm_text = ParseBgpCommunities(update, offset, offset + attr_len) print_msg.append("%s\n" % " ".join(comm_text)) # MP_REACH # elif attr_type == ATTR_TYPE_MP_REACH_NLRI: print_msg.append("%s%s\n" % (indent_str, ATTR_TYPE_STR[attr_type])) mpattr_text = [] try: mpattr_text = ParseBgpMpAttr(update, offset, offset + attr_len, True) except Exception, esc: if verbose: print "".join(print_msg), for x in range(offset, offset + attr_len): print " %02x" % update[x], mp_indent = indent.IndentLevel(indent.BGP_MPATTR_INDENT) for mpattr in mpattr_text: print_msg.append("%s%s" % (mp_indent, mpattr)) # MP_UNREACH # elif attr_type == ATTR_TYPE_MP_UNREACH_NLRI: print_msg.append("%s%s\n" % (indent_str, ATTR_TYPE_STR[attr_type])) mpattr_text = ParseBgpMpAttr(update, offset, offset + attr_len, False) mp_indent = indent.IndentLevel(indent.BGP_MPATTR_INDENT) for mpattr in mpattr_text: print_msg.append("%s%s" % (mp_indent, mpattr))