Esempio n. 1
0
    def __parse(self):
        self.parsed = True

        try:
            # Check last two bytes of byte payload - if they are set to zero we don't have any path attributes
            if struct.unpack("!H", self.payload[-2:])[0] == 0:
                self.path_attributes_length = 0

            # Unpack the length of withdrawn routes field and add 2 bytes to the current byte marker position
            self.withdrawn_routes_length = struct.unpack(
                "!H", self.payload[:2])[0]
            current_byte_position = 2

            # Start parsing withdrawn routes
            if self.withdrawn_routes_length is not 0:
                continue_loop = True

                # Loop through withdrawals
                while continue_loop:
                    # First of all we need to parse the length of the withdrawn prefix. Depending on the prefix length
                    # we can determine the length following prefix itself
                    prefix_length_bytes = self.payload[
                        current_byte_position:current_byte_position + 1]
                    prefix_length = struct.unpack("!B", prefix_length_bytes)[0]
                    current_byte_position += 1

                    if 0 <= prefix_length <= 8:
                        # Length of prefix field: 1 Byte
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 1]
                        current_byte_position += 1

                    elif 9 <= prefix_length <= 16:
                        # Length of prefix field: 2 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 2]
                        current_byte_position += 2

                    elif 17 <= prefix_length <= 24:
                        # Length of prefix field: 3 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 3]
                        current_byte_position += 3

                    elif 25 <= prefix_length:
                        # Length of prefix field: 4 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 4]
                        current_byte_position += 4

                    else:
                        self.error = True
                        raise BGPWithdrawnPrefixError(
                            "can't match prefix length.")

                    # Add BGPRoute object with information about the withdrawn route to list
                    self.withdrawn_routes.append(
                        BGPRoute.from_binary(prefix_bytes,
                                             prefix_length_bytes))

                    # Check if we are at the end of the payload
                    if self.withdrawn_routes_length <= current_byte_position:
                        # If yes we need to stop the iteration
                        continue_loop = False

            # Second step: Continue with the path attributes
            if self.path_attributes_length is None:
                # First of all get the attributes length field and update the current byte position
                self.path_attributes_length = struct.unpack(
                    "!H",
                    self.payload[current_byte_position:current_byte_position +
                                 2])[0]
                current_byte_position += 2

            # Now we have a correct path_attributes_length stored. If this length is zero we don't need to do anything
            if self.path_attributes_length is not 0:
                continue_loop = True

                # Loop through path attributes
                while continue_loop:
                    # Now comes a tricky part of UPDATE message parsing. Each path attribute has a flag bitfield.
                    # One of those flags is called 'extended length'. If it's set to 1 the following attribute fields
                    # are 2 bytes long. But if it's set to zero it's just 1 byte long ...

                    # So first of all: Flag parsing!
                    attribute_flags = BGPUpdateFlags(
                        struct.unpack(
                            "!B", self.payload[
                                current_byte_position:current_byte_position +
                                1])[0])
                    current_byte_position += 1

                    if attribute_flags.length:
                        # We got an extended length flag
                        attribute_fields = struct.unpack(
                            "!BH", self.payload[
                                current_byte_position:current_byte_position +
                                3])
                        current_byte_position += 3
                    else:
                        # We got a normal length flag
                        attribute_fields = struct.unpack(
                            "!BB", self.payload[
                                current_byte_position:current_byte_position +
                                2])
                        current_byte_position += 2

                    # Finally assign the variables
                    attribute_type = attribute_fields[0]
                    attribute_length = attribute_fields[1]

                    # Now we are using the factory pattern again to determine
                    # which kind of attribute we have to add the list
                    self.path_attributes.append(
                        BGPPathAttribute.factory(
                            attribute_type, self.payload[
                                current_byte_position:current_byte_position +
                                attribute_length], attribute_flags))

                    # Add length of attribute to position pointer
                    current_byte_position += attribute_length

                    # Check if there are further path attributes to parse
                    if current_byte_position >= self.path_attributes_length:
                        continue_loop = False

            # Third step: NLRIs
            if len(self.payload) > (self.path_attributes_length + 4 +
                                    self.withdrawn_routes_length):

                continue_loop = True

                # The four bytes that get added to the attributes and routes length are the two two-byte fields
                # for path attribute length and withdrawn routes length
                current_byte_position = self.path_attributes_length + 4 + self.withdrawn_routes_length

                while continue_loop:
                    # First of all we have to check the prefix length as byte-length of the following
                    # prefix depends on its prefix length (This is a 1-byte-field)
                    prefix_length_bytes = self.payload[
                        current_byte_position:current_byte_position + 1]
                    prefix_length = struct.unpack("!B", prefix_length_bytes)[0]
                    current_byte_position += 1

                    if 0 <= prefix_length <= 8:
                        # Length of prefix field: 1 Byte
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 1]
                        current_byte_position += 1
                    elif 9 <= prefix_length <= 16:
                        # Length of prefix field: 2 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 2]
                        current_byte_position += 2
                    elif 17 <= prefix_length <= 24:
                        # Length of prefix field: 3 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 3]
                        current_byte_position += 3
                    elif 25 <= prefix_length:
                        # Length of prefix field: 4 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 4]
                        current_byte_position += 4
                    else:
                        self.error = True
                        raise BGPWithdrawnPrefixError(
                            "can't match prefix length.")

                    try:
                        self.nlri.append(
                            BGPRoute.from_binary(prefix_bytes,
                                                 prefix_length_bytes))
                    except BGPError as e:
                        raise BGPNLRIError(
                            "can't append NLRI to message (error: " + str(e) +
                            ")")

                    if current_byte_position >= len(self.payload):
                        continue_loop = False

            # Determine sub-type for filtering
            # Using bit flags for easier assignment
            if len(self.nlri) > 0:
                self.subtype = (self.subtype | BGPStatics.UPDATE_TYPE_ANNOUNCE)

            if len(self.withdrawn_routes) > 0:
                self.subtype = (self.subtype
                                | BGPStatics.UPDATE_TYPE_WITHDRAWAL)

        except BGPWithdrawnPrefixError as p:
            self.error = True
            logging.info(p)
        except Exception as e:
            self.error = True

        self.error = False
Esempio n. 2
0
    def __parse(self):
        self.parsed = True

        try:
            # Unpack the length of withdrawn routes field and add 2 bytes to the current byte marker position
            self.withdrawn_routes_length = struct.unpack(
                "!H", self.payload[:2])[0]
            current_byte_position = 2

            # Start parsing withdrawn routes
            if self.withdrawn_routes_length is not 0:
                continue_loop = True

                # Loop through withdrawals
                while continue_loop:
                    # AddPath assumption? look for description in the method for NLRI parsing
                    if self.flags["addpath"].get_value(
                    ) == 0:  # No AddPath messages
                        pass

                    else:
                        pathId_length_bytes = self.payload[
                            current_byte_position:current_byte_position + 4]
                        pathId = struct.unpack("!I", pathId_length_bytes)[0]

                        if self.flags["addpath"].get_value(
                        ) == 1:  # Only AddPath
                            self.add_path = True
                            self.path_id = pathId
                            current_byte_position += 4

                        else:  # Try to find out (using metric)
                            if pathId < 65536:
                                self.add_path = True
                                self.path_id = pathId
                                current_byte_position += 4
                            #else: drop the Path Id, its likely that this is not an AddPath msg

                    # First of all we need to parse the length of the withdrawn prefix. Depending on the prefix length
                    # we can determine the length following prefix itself
                    prefix_length_bytes = self.payload[
                        current_byte_position:current_byte_position + 1]
                    prefix_length = struct.unpack("!B", prefix_length_bytes)[0]
                    current_byte_position += 1

                    if prefix_length == 0:
                        prefix_bytes = prefix_length_bytes
                    elif 0 < prefix_length <= 8:
                        # Length of prefix field: 1 Byte
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 1]
                        current_byte_position += 1

                    elif 9 <= prefix_length <= 16:
                        # Length of prefix field: 2 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 2]
                        current_byte_position += 2

                    elif 17 <= prefix_length <= 24:
                        # Length of prefix field: 3 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 3]
                        current_byte_position += 3

                    elif 25 <= prefix_length:
                        # Length of prefix field: 4 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 4]
                        current_byte_position += 4

                    else:
                        self.error = True
                        raise BGPWithdrawnPrefixError(
                            "can't match prefix length.")

                    # Add BGPRoute object with information about the withdrawn route to list
                    self.withdrawn_routes.append(
                        BGPRoute.from_binary(prefix_bytes,
                                             prefix_length_bytes))

                    # Check if we are at the end of the payload
                    if self.withdrawn_routes_length <= current_byte_position:
                        # If yes we need to stop the iteration
                        continue_loop = False

            # Second step: Continue with the path attributes
            if self.path_attributes_length is None:
                # First of all get the attributes length field and update the current byte position
                self.path_attributes_length = struct.unpack(
                    "!H",
                    self.payload[current_byte_position:current_byte_position +
                                 2])[0]
                current_byte_position += 2

            # Now we have a correct path_attributes_length stored. If this length is zero we don't need to do anything
            if self.path_attributes_length is not 0:
                continue_loop = True

                # Loop through path attributes
                while continue_loop:
                    # Now comes a tricky part of UPDATE message parsing. Each path attribute has a flag bitfield.
                    # One of those flags is called 'extended length'. If it's set to 1 the following attribute fields
                    # are 2 bytes long. But if it's set to zero it's just 1 byte long ...

                    # So first of all: Flag parsing!
                    attribute_flags = BGPUpdateFlags(
                        struct.unpack(
                            "!B", self.payload[
                                current_byte_position:current_byte_position +
                                1])[0])
                    current_byte_position += 1

                    if attribute_flags.length:
                        # We got an extended length flag
                        attribute_fields = struct.unpack(
                            "!BH", self.payload[
                                current_byte_position:current_byte_position +
                                3])
                        current_byte_position += 3
                    else:
                        # We got a normal length flag
                        attribute_fields = struct.unpack(
                            "!BB", self.payload[
                                current_byte_position:current_byte_position +
                                2])
                        current_byte_position += 2

                    # Finally assign the variables
                    attribute_type = attribute_fields[0]
                    attribute_length = attribute_fields[1]

                    # Now we are using the factory pattern again to determine
                    # which kind of attribute we have to add the list
                    self.path_attributes.append(
                        BGPPathAttribute.factory(
                            attribute_type, self.payload[
                                current_byte_position:current_byte_position +
                                attribute_length], attribute_flags))

                    # Add length of attribute to position pointer
                    current_byte_position += attribute_length

                    # Check if there are further path attributes to parse
                    if current_byte_position >= self.path_attributes_length:
                        continue_loop = False

            # Third step: NLRIs
            if len(self.payload) > (self.path_attributes_length + 4 +
                                    self.withdrawn_routes_length):

                continue_loop = True

                # The four bytes that get added to the attributes and routes length are the two two-byte fields
                # for path attribute length and withdrawn routes length
                current_byte_position = self.path_attributes_length + 4 + self.withdrawn_routes_length

                while continue_loop:
                    """
                    The Following is a Fix for missing Add_Path feature.
                    Due to the lack of a definition for this case, we need depend on the users decision.
                    See RFC 7911 Chapter 6 p.5 (22.07.2020).

                    In most cases, the pathId is lower than 2**16. Also it is uncommon,
                    that one BGP UPDATE message contains the 0.0.0.0/0 prefix 2 times.
                    This leads to the following metric if the user sets the add_path_flag to 2.
                    """
                    # AddPath assumption?
                    if self.flags["addpath"].get_value(
                    ) == 0:  # No AddPath messages
                        pass

                    else:
                        pathId_length_bytes = self.payload[
                            current_byte_position:current_byte_position + 4]
                        pathId = struct.unpack("!I", pathId_length_bytes)[0]

                        if self.flags["addpath"].get_value(
                        ) == 1:  # Only AddPath
                            self.add_path = True
                            self.path_id = pathId
                            current_byte_position += 4

                        else:  # Try to find out (using metric)
                            if pathId < 65536:
                                self.add_path = True
                                self.path_id = pathId
                                current_byte_position += 4
                            #else: drop the Path Id, its likely that this is not an AddPath msg

                    # First of all we have to check the prefix length as byte-length of the following
                    # prefix depends on its prefix length (This is a 1-byte-field)
                    prefix_length_bytes = self.payload[
                        current_byte_position:current_byte_position + 1]
                    prefix_length = struct.unpack("!B", prefix_length_bytes)[0]
                    current_byte_position += 1

                    if prefix_length == 0:  #0.0.0.0/0
                        prefix_bytes = prefix_length_bytes
                    elif 0 <= prefix_length <= 8:
                        # Length of prefix field: 1 Byte
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 1]
                        current_byte_position += 1
                    elif 9 <= prefix_length <= 16:
                        # Length of prefix field: 2 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 2]
                        current_byte_position += 2
                    elif 17 <= prefix_length <= 24:
                        # Length of prefix field: 3 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 3]
                        current_byte_position += 3
                    elif 25 <= prefix_length:
                        # Length of prefix field: 4 Bytes
                        prefix_bytes = self.payload[
                            current_byte_position:current_byte_position + 4]
                        current_byte_position += 4
                    else:
                        self.error = True
                        raise BGPWithdrawnPrefixError(
                            "can't match prefix length.")

                    try:
                        self.nlri.append(
                            BGPRoute.from_binary(prefix_bytes,
                                                 prefix_length_bytes))
                    except BGPError as e:
                        raise BGPNLRIError(
                            "can't append NLRI to message (error: " + str(e) +
                            ")")

                    if current_byte_position >= len(self.payload):
                        continue_loop = False

            # Determine sub-type for filtering
            # Using bit flags for easier assignment
            if len(self.nlri) > 0:
                self.subtype = (self.subtype | BGPStatics.UPDATE_TYPE_ANNOUNCE)

            if len(self.withdrawn_routes) > 0:
                self.subtype = (self.subtype
                                | BGPStatics.UPDATE_TYPE_WITHDRAWAL)

        except BGPWithdrawnPrefixError as p:
            self.error = True
            logging.info(p)
        except Exception as e:
            self.error = True

        self.error = False