def test_absent_option_echo_request(self): relayed_solicit_message = RelayForwardMessage( hop_count=1, link_address=IPv6Address('2001:db8:ffff:1::1'), peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'), options=[ RelayMessageOption(relayed_message=SolicitMessage( transaction_id=bytes.fromhex('f350d6'), options=[ ElapsedTimeOption(elapsed_time=0), ClientIdOption(duid=LinkLayerDUID(hardware_type=1, link_layer_address=bytes.fromhex('3431c43cb2f1'))), IANAOption(iaid=bytes.fromhex('c43cb2f1')), ], )), EchoRequestOption(requested_options=[OPTION_SUBSCRIBER_ID]), UnknownOption(option_type=65535), InterfaceIdOption(interface_id=b'Fa2/3'), RemoteIdOption(enterprise_number=9, remote_id=bytes.fromhex('020023000001000a0003000100211c7d486e')), ] ) bundle = TransactionBundle(incoming_message=relayed_solicit_message, received_over_multicast=True) self.message_handler.handle(bundle, StatisticsSet()) self.assertIsInstance(bundle.outgoing_message, RelayReplyMessage) self.assertEqual(len(bundle.outgoing_message.options), 2) self.assertIsInstance(bundle.outgoing_message.options[0], InterfaceIdOption) self.assertIsInstance(bundle.outgoing_message.options[1], RelayMessageOption)
def setUp(self): self.bundle = TransactionBundle(relayed_solicit_message, received_over_multicast=False) self.shallow_bundle = TransactionBundle(solicit_message, received_over_multicast=True) self.deep_bundle = TransactionBundle(RelayForwardMessage( hop_count=0, link_address=IPv6Address('2001:db8:ffff:2::1'), peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'), options=[ RelayMessageOption(relayed_message=relayed_solicit_message), ]), received_over_multicast=False, marks=['some', 'marks']) self.ia_bundle = TransactionBundle(SolicitMessage(options=[ IANAOption(b'0001'), IANAOption(b'0002'), IATAOption(b'0003'), IATAOption(b'0004'), IAPDOption(b'0005'), IAPDOption(b'0006'), ]), received_over_multicast=False) self.option_handlers = [ InterfaceIdOptionHandler(), ]
def setUp(self): self.relayed_solicit_message = RelayForwardMessage( hop_count=1, link_address=IPv6Address('2001:db8:ffff:1::1'), peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'), options=[ RelayMessageOption(relayed_message=RelayForwardMessage( hop_count=0, link_address=IPv6Address('::'), peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'), options=[ RelayMessageOption(relayed_message=SolicitMessage( transaction_id=bytes.fromhex('f350d6'), options=[ ElapsedTimeOption(elapsed_time=0), ClientIdOption(duid=LinkLayerDUID(hardware_type=1, link_layer_address=bytes.fromhex('3431c43cb2f1'))), IANAOption(iaid=bytes.fromhex('c43cb2f1')), IAPDOption(iaid=bytes.fromhex('c43cb2f1')), OptionRequestOption(requested_options=[ OPTION_DNS_SERVERS, ]), ], )), InterfaceIdOption(interface_id=b'Fa2/3'), RemoteIdOption(enterprise_number=9, remote_id=bytes.fromhex('020023000001000a0003000100211c7d486e')), ]) ), InterfaceIdOption(interface_id=b'Gi0/0/0'), RemoteIdOption(enterprise_number=9, remote_id=bytes.fromhex('020000000000000a0003000124e9b36e8100')), RelayIdOption(duid=LinkLayerDUID(hardware_type=1, link_layer_address=bytes.fromhex('121212121212'))), ], )
def test_test_wrong_message(self): with self.assertRaisesRegex(ValueError, 'must be an IPv6 DHCP message'): LQRelayDataOption( peer_address=IPv6Address('2001:db8::2'), relay_message=None ).validate() with self.assertRaisesRegex(ValueError, 'cannot contain'): # noinspection PyTypeChecker LQRelayDataOption( peer_address=IPv6Address('2001:db8::2'), relay_message=SolicitMessage() ).validate()
def setUp(self): # The following attributes must be overruled by child classes # The basics are tested with a simple SolicitMessage self.packet_fixture = bytes.fromhex( '01' # message_type '58595a' # transaction_id '0001' # option_type: OPTION_CLIENTID '0015' # option_length '0002' # duid_type: DUID_EN '00009d10' # enterprise_number '444843504b6974556e697454657374' # "DHCPKitUnitTest" '0008' # option_type: OPTION_ELAPSED_TIME '0002' # option_length '0000') # elapsed_time self.message_fixture = SolicitMessage( transaction_id=b'XYZ', options=[ ClientIdOption(duid=EnterpriseDUID( enterprise_number=40208, identifier=b'DHCPKitUnitTest')), ElapsedTimeOption(elapsed_time=0) ]) self.parse_packet()
encapsulated-options options associated with this Softwire46 Lightweight 4over6 domain. The encapsulated-options field conveys options specific to the OPTION_S46_CONT_LW option. Currently, there are two options specified: OPTION_S46_V4V6BIND and OPTION_S46_BR. There MUST be at most one OPTION_S46_V4V6BIND option and at least one OPTION_S46_BR option. """ option_type = OPTION_S46_CONT_LW # Register where these options may occur SolicitMessage.add_may_contain(S46ContainerOption) AdvertiseMessage.add_may_contain(S46ContainerOption) RequestMessage.add_may_contain(S46ContainerOption) ConfirmMessage.add_may_contain(S46ContainerOption) RenewMessage.add_may_contain(S46ContainerOption) RebindMessage.add_may_contain(S46ContainerOption) ReleaseMessage.add_may_contain(S46ContainerOption) ReplyMessage.add_may_contain(S46ContainerOption) S46RuleOption.add_may_contain(S46PortParametersOption) S46V4V6BindingOption.add_may_contain(S46PortParametersOption) S46MapEContainerOption.add_may_contain(S46RuleOption, min_occurrence=1) S46MapEContainerOption.add_may_contain(S46BROption, min_occurrence=1) S46MapEContainerOption.add_may_contain(S46PortParametersOption)
peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'), options=[ RelayMessageOption(relayed_message=SolicitMessage( transaction_id=bytes.fromhex('f350d6'), options=[ ElapsedTimeOption(elapsed_time=0), ClientIdOption(duid=LinkLayerDUID( hardware_type=1, link_layer_address=bytes.fromhex('3431c43cb2f1'))), RapidCommitOption(), IANAOption(iaid=bytes.fromhex('c43cb2f1')), IAPDOption(iaid=bytes.fromhex('c43cb2f1'), options=[ IAPrefixOption( prefix=IPv6Network('::/0')), ]), ReconfigureAcceptOption(), OptionRequestOption(requested_options=[ OPTION_DNS_SERVERS, OPTION_NTP_SERVER, OPTION_SNTP_SERVERS, OPTION_IA_PD, OPTION_IA_NA, OPTION_VENDOR_OPTS, OPTION_SOL_MAX_RT, OPTION_INF_MAX_RT, ]), VendorClassOption(enterprise_number=872), ], )), InterfaceIdOption(interface_id=b'Fa2/3'), RemoteIdOption(enterprise_number=9,
return my_offset def save(self) -> bytes: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ self.validate() options_buffer = bytearray() for option in self.options: options_buffer.extend(option.save()) buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(options_buffer))) buffer.extend(options_buffer) return buffer # Register where these options may occur SolicitMessage.add_may_contain(NTPServersOption) AdvertiseMessage.add_may_contain(NTPServersOption) RequestMessage.add_may_contain(NTPServersOption) RenewMessage.add_may_contain(NTPServersOption) RebindMessage.add_may_contain(NTPServersOption) InformationRequestMessage.add_may_contain(NTPServersOption) ReplyMessage.add_may_contain(NTPServersOption) NTPServersOption.add_may_contain(NTPSubOption, 1)
""" Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(self.sip_servers) * 16)) for address in self.sip_servers: buffer.extend(address.packed) return buffer # Register where these options may occur SolicitMessage.add_may_contain(SIPServersDomainNameListOption) AdvertiseMessage.add_may_contain(SIPServersDomainNameListOption) RequestMessage.add_may_contain(SIPServersDomainNameListOption) RenewMessage.add_may_contain(SIPServersDomainNameListOption) RebindMessage.add_may_contain(SIPServersDomainNameListOption) InformationRequestMessage.add_may_contain(SIPServersDomainNameListOption) ReplyMessage.add_may_contain(SIPServersDomainNameListOption) SolicitMessage.add_may_contain(SIPServersAddressListOption) AdvertiseMessage.add_may_contain(SIPServersAddressListOption) RequestMessage.add_may_contain(SIPServersAddressListOption) RenewMessage.add_may_contain(SIPServersAddressListOption) RebindMessage.add_may_contain(SIPServersAddressListOption) InformationRequestMessage.add_may_contain(SIPServersAddressListOption) ReplyMessage.add_may_contain(SIPServersAddressListOption)
def save(self) -> Union[bytes, bytearray]: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ domain_buffer = encode_domain_list(self.search_list) buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(domain_buffer))) buffer.extend(domain_buffer) return buffer # Register where these options may occur SolicitMessage.add_may_contain(RecursiveNameServersOption) AdvertiseMessage.add_may_contain(RecursiveNameServersOption) RequestMessage.add_may_contain(RecursiveNameServersOption) RenewMessage.add_may_contain(RecursiveNameServersOption) RebindMessage.add_may_contain(RecursiveNameServersOption) InformationRequestMessage.add_may_contain(RecursiveNameServersOption) ReplyMessage.add_may_contain(RecursiveNameServersOption) SolicitMessage.add_may_contain(DomainSearchListOption) AdvertiseMessage.add_may_contain(DomainSearchListOption) RequestMessage.add_may_contain(DomainSearchListOption) RenewMessage.add_may_contain(DomainSearchListOption) RebindMessage.add_may_contain(DomainSearchListOption) InformationRequestMessage.add_may_contain(DomainSearchListOption) ReplyMessage.add_may_contain(DomainSearchListOption)
my_offset += name_len if my_offset != max_offset: raise ValueError( 'Option length does not match the length of the included fqdn') return my_offset def save(self) -> Union[bytes, bytearray]: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ fqdn_buffer = encode_domain(self.fqdn) buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(fqdn_buffer))) buffer.extend(fqdn_buffer) return buffer # Update the messages where this option may appear SolicitMessage.add_may_contain(AFTRNameOption, 0, 1) AdvertiseMessage.add_may_contain(AFTRNameOption, 0, 1) RequestMessage.add_may_contain(AFTRNameOption, 0, 1) RenewMessage.add_may_contain(AFTRNameOption, 0, 1) RebindMessage.add_may_contain(AFTRNameOption, 0, 1) InformationRequestMessage.add_may_contain(AFTRNameOption, 0, 1) ReplyMessage.add_may_contain(AFTRNameOption, 0, 1)
Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ self.validate() buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(self.sip_servers) * 16)) for address in self.sip_servers: buffer.extend(address.packed) return buffer # Register where these options may occur SolicitMessage.add_may_contain(SIPServersDomainNameListOption) AdvertiseMessage.add_may_contain(SIPServersDomainNameListOption) RequestMessage.add_may_contain(SIPServersDomainNameListOption) RenewMessage.add_may_contain(SIPServersDomainNameListOption) RebindMessage.add_may_contain(SIPServersDomainNameListOption) InformationRequestMessage.add_may_contain(SIPServersDomainNameListOption) ReplyMessage.add_may_contain(SIPServersDomainNameListOption) SolicitMessage.add_may_contain(SIPServersAddressListOption) AdvertiseMessage.add_may_contain(SIPServersAddressListOption) RequestMessage.add_may_contain(SIPServersAddressListOption) RenewMessage.add_may_contain(SIPServersAddressListOption) RebindMessage.add_may_contain(SIPServersAddressListOption) InformationRequestMessage.add_may_contain(SIPServersAddressListOption) ReplyMessage.add_may_contain(SIPServersAddressListOption)
:return: The buffer with the data from this element """ self.validate() options_buffer = bytearray() for option in self.options: options_buffer.extend(option.save()) buffer = bytearray() buffer.extend(pack('!HHIIB', self.option_type, len(options_buffer) + 25, self.preferred_lifetime, self.valid_lifetime, self.prefix.prefixlen)) buffer.extend(self.prefix.network_address.packed) buffer.extend(options_buffer) return buffer # Register where these options may occur SolicitMessage.add_may_contain(IAPDOption) AdvertiseMessage.add_may_contain(IAPDOption) RequestMessage.add_may_contain(IAPDOption) RenewMessage.add_may_contain(IAPDOption) RebindMessage.add_may_contain(IAPDOption) ReleaseMessage.add_may_contain(IAPDOption) ReplyMessage.add_may_contain(IAPDOption) IAPDOption.add_may_contain(IAPrefixOption) IAPDOption.add_may_contain(StatusCodeOption, 0, 1) IAPrefixOption.add_may_contain(StatusCodeOption, 0, 1)
max_offset = option_len + header_offset # The option_len field counts bytes *after* the header fields domain_name_len, self.domain_name = parse_domain_bytes(buffer, offset=offset + my_offset, length=option_len - 1, allow_relative=True) my_offset += domain_name_len if my_offset != max_offset: raise ValueError('Option length does not match the combined length of the included domain name') return my_offset def save(self) -> Union[bytes, bytearray]: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ domain_buffer = encode_domain(self.domain_name) buffer = bytearray() buffer.extend(pack('!HHB', self.option_type, 1 + len(domain_buffer), self.flags)) buffer.extend(domain_buffer) return buffer SolicitMessage.add_may_contain(ClientFQDNOption, 0, 1) AdvertiseMessage.add_may_contain(ClientFQDNOption, 0, 1) RequestMessage.add_may_contain(ClientFQDNOption, 0, 1) RenewMessage.add_may_contain(ClientFQDNOption, 0, 1) RebindMessage.add_may_contain(ClientFQDNOption, 0, 1) ReplyMessage.add_may_contain(ClientFQDNOption, 0, 1)
""" Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ self.validate() domain_buffer = encode_domain_list(self.search_list) buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(domain_buffer))) buffer.extend(domain_buffer) return buffer SolicitMessage.add_may_contain(RecursiveNameServersOption, 0, 1) AdvertiseMessage.add_may_contain(RecursiveNameServersOption, 0, 1) RequestMessage.add_may_contain(RecursiveNameServersOption, 0, 1) RenewMessage.add_may_contain(RecursiveNameServersOption, 0, 1) RebindMessage.add_may_contain(RecursiveNameServersOption, 0, 1) InformationRequestMessage.add_may_contain(RecursiveNameServersOption, 0, 1) ReplyMessage.add_may_contain(RecursiveNameServersOption, 0, 1) SolicitMessage.add_may_contain(DomainSearchListOption, 0, 1) AdvertiseMessage.add_may_contain(DomainSearchListOption, 0, 1) RequestMessage.add_may_contain(DomainSearchListOption, 0, 1) RenewMessage.add_may_contain(DomainSearchListOption, 0, 1) RebindMessage.add_may_contain(DomainSearchListOption, 0, 1) InformationRequestMessage.add_may_contain(DomainSearchListOption, 0, 1) ReplyMessage.add_may_contain(DomainSearchListOption, 0, 1)
def test_bad_response(self): self.bundle.response = SolicitMessage() with self.assertLogs() as cm: self.assertIsNone(self.bundle.outgoing_message) self.assertEqual(len(cm.output), 1) self.assertRegex(cm.output[0], 'server should not send')
max_offset = option_len + header_offset # The option_len field counts bytes *after* the header fields while max_offset > my_offset: address = IPv6Address(buffer[offset + my_offset:offset + my_offset + 16]) self.sntp_servers.append(address) my_offset += 16 return my_offset def save(self) -> Union[bytes, bytearray]: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(self.sntp_servers) * 16)) for address in self.sntp_servers: buffer.extend(address.packed) return buffer # Register where these options may occur SolicitMessage.add_may_contain(SNTPServersOption) AdvertiseMessage.add_may_contain(SNTPServersOption) RequestMessage.add_may_contain(SNTPServersOption) RenewMessage.add_may_contain(SNTPServersOption) RebindMessage.add_may_contain(SNTPServersOption) InformationRequestMessage.add_may_contain(SNTPServersOption) ReplyMessage.add_may_contain(SNTPServersOption)
:return: The buffer with the data from this element """ options_buffer = bytearray() for option in self.options: options_buffer.extend(option.save()) buffer = bytearray() buffer.extend( pack('!HHIIB', self.option_type, len(options_buffer) + 25, self.preferred_lifetime, self.valid_lifetime, self.prefix.prefixlen)) buffer.extend(self.prefix.network_address.packed) buffer.extend(options_buffer) return buffer # Register where these options may occur SolicitMessage.add_may_contain(IAPDOption) AdvertiseMessage.add_may_contain(IAPDOption) RequestMessage.add_may_contain(IAPDOption) ConfirmMessage.add_may_contain(IAPDOption) RenewMessage.add_may_contain(IAPDOption) RebindMessage.add_may_contain(IAPDOption) ReleaseMessage.add_may_contain(IAPDOption) ReplyMessage.add_may_contain(IAPDOption) IAPDOption.add_may_contain(IAPrefixOption) IAPDOption.add_may_contain(StatusCodeOption, 0, 1) IAPrefixOption.add_may_contain(StatusCodeOption, 0, 1)
return my_offset def save(self) -> Union[bytes, bytearray]: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ buffer = bytearray() buffer.extend(pack('!HH', self.option_type, len(self.timezone))) buffer.extend(self.timezone.encode('ascii')) return buffer SolicitMessage.add_may_contain(PosixTimezoneOption, 0, 1) AdvertiseMessage.add_may_contain(PosixTimezoneOption, 0, 1) RequestMessage.add_may_contain(PosixTimezoneOption, 0, 1) RenewMessage.add_may_contain(PosixTimezoneOption, 0, 1) RebindMessage.add_may_contain(PosixTimezoneOption, 0, 1) InformationRequestMessage.add_may_contain(PosixTimezoneOption, 0, 1) ReplyMessage.add_may_contain(PosixTimezoneOption, 0, 1) SolicitMessage.add_may_contain(TZDBTimezoneOption, 0, 1) AdvertiseMessage.add_may_contain(TZDBTimezoneOption, 0, 1) RequestMessage.add_may_contain(TZDBTimezoneOption, 0, 1) RenewMessage.add_may_contain(TZDBTimezoneOption, 0, 1) RebindMessage.add_may_contain(TZDBTimezoneOption, 0, 1) InformationRequestMessage.add_may_contain(TZDBTimezoneOption, 0, 1) ReplyMessage.add_may_contain(TZDBTimezoneOption, 0, 1)