You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
630 lines
24 KiB
630 lines
24 KiB
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""
|
|
DHCP handling rules are ways to record expectations for a DhcpTestServer.
|
|
|
|
When a handling rule reaches the front of the DhcpTestServer handling rule
|
|
queue, the server begins to ask the rule what it should do with each incoming
|
|
DHCP packet (in the form of a DhcpPacket). The handle() method is expected to
|
|
return a tuple (response, action) where response indicates whether the packet
|
|
should be ignored or responded to and whether the test failed, succeeded, or is
|
|
continuing. The action part of the tuple refers to whether or not the rule
|
|
should be be removed from the test server's handling rule queue.
|
|
"""
|
|
|
|
import logging
|
|
import time
|
|
|
|
from autotest_lib.client.cros import dhcp_packet
|
|
|
|
# Drops the packet and acts like it never happened.
|
|
RESPONSE_NO_ACTION = 0
|
|
# Signals that the handler wishes to send a packet.
|
|
RESPONSE_HAVE_RESPONSE = 1 << 0
|
|
# Signals that the handler wishes to be removed from the handling queue.
|
|
# The handler will be asked to generate a packet first if the handler signalled
|
|
# that it wished to do so with RESPONSE_HAVE_RESPONSE.
|
|
RESPONSE_POP_HANDLER = 1 << 1
|
|
# Signals that the handler wants to end the test on a failure.
|
|
RESPONSE_TEST_FAILED = 1 << 2
|
|
# Signals that the handler wants to end the test because it succeeded.
|
|
# Note that the failure bit has precedence over the success bit.
|
|
RESPONSE_TEST_SUCCEEDED = 1 << 3
|
|
|
|
class DhcpHandlingRule(object):
|
|
"""
|
|
DhcpHandlingRule defines an interface between the DhcpTestServer and
|
|
subclasses of DhcpHandlingRule. A handling rule at the front of the
|
|
DhcpTestServer rule queue is first asked what should be done with a packet
|
|
via handle(). handle() returns a bitfield as described above. If the
|
|
response from handle() indicates that a packet should be sent in response,
|
|
the server asks the handling rule to construct a response packet via
|
|
respond().
|
|
"""
|
|
|
|
def __init__(self, message_type, additional_options, custom_fields):
|
|
"""
|
|
|message_type| should be a MessageType, from DhcpPacket.
|
|
|additional_options| should be a dictionary that maps from
|
|
dhcp_packet.OPTION_* to values. For instance:
|
|
|
|
{dhcp_packet.OPTION_SERVER_ID : "10.10.10.1"}
|
|
|
|
These options are injected into response packets if the client requests
|
|
it. See inject_options().
|
|
"""
|
|
super(DhcpHandlingRule, self).__init__()
|
|
self._is_final_handler = False
|
|
self._logger = logging.getLogger("dhcp.handling_rule")
|
|
self._options = additional_options
|
|
self._fields = custom_fields
|
|
self._target_time_seconds = None
|
|
self._allowable_time_delta_seconds = 0.5
|
|
self._force_reply_options = []
|
|
self._message_type = message_type
|
|
self._last_warning = None
|
|
|
|
def __str__(self):
|
|
if self._last_warning:
|
|
return '%s (%s)' % (self.__class__.__name__, self._last_warning)
|
|
else:
|
|
return self.__class__.__name__
|
|
|
|
@property
|
|
def logger(self):
|
|
return self._logger
|
|
|
|
@property
|
|
def is_final_handler(self):
|
|
return self._is_final_handler
|
|
|
|
@is_final_handler.setter
|
|
def is_final_handler(self, value):
|
|
self._is_final_handler = value
|
|
|
|
@property
|
|
def options(self):
|
|
"""
|
|
Returns a dictionary that maps from DhcpPacket options to their values.
|
|
"""
|
|
return self._options
|
|
|
|
@property
|
|
def fields(self):
|
|
"""
|
|
Returns a dictionary that maps from DhcpPacket fields to their values.
|
|
"""
|
|
return self._fields
|
|
|
|
@property
|
|
def target_time_seconds(self):
|
|
"""
|
|
If this is not None, packets will be rejected if they don't fall within
|
|
|self.allowable_time_delta_seconds| seconds of
|
|
|self.target_time_seconds|. A value of None will cause this handler to
|
|
ignore the target packet time.
|
|
|
|
Defaults to None.
|
|
"""
|
|
return self._target_time_seconds
|
|
|
|
@target_time_seconds.setter
|
|
def target_time_seconds(self, value):
|
|
self._target_time_seconds = value
|
|
|
|
@property
|
|
def allowable_time_delta_seconds(self):
|
|
"""
|
|
A configurable fudge factor for |self.target_time_seconds|. If a packet
|
|
comes in at time T and:
|
|
|
|
delta = abs(T - |self.target_time_seconds|)
|
|
|
|
Then if delta < |self.allowable_time_delta_seconds|, we accept the
|
|
packet. Otherwise we either fail the test or ignore the packet,
|
|
depending on whether this packet is before or after the window.
|
|
|
|
Defaults to 0.5 seconds.
|
|
"""
|
|
return self._allowable_time_delta_seconds
|
|
|
|
@allowable_time_delta_seconds.setter
|
|
def allowable_time_delta_seconds(self, value):
|
|
self._allowable_time_delta_seconds = value
|
|
|
|
@property
|
|
def packet_is_too_late(self):
|
|
if self.target_time_seconds is None:
|
|
return False
|
|
delta = time.time() - self.target_time_seconds
|
|
logging.debug("Handler received packet %0.2f seconds from target time.",
|
|
delta)
|
|
if delta > self._allowable_time_delta_seconds:
|
|
logging.info("Packet was too late for handling (+%0.2f seconds)",
|
|
delta - self._allowable_time_delta_seconds)
|
|
return True
|
|
logging.info("Packet was not too late for handling.")
|
|
return False
|
|
|
|
@property
|
|
def packet_is_too_soon(self):
|
|
if self.target_time_seconds is None:
|
|
return False
|
|
delta = time.time() - self.target_time_seconds
|
|
logging.debug("Handler received packet %0.2f seconds from target time.",
|
|
delta)
|
|
if -delta > self._allowable_time_delta_seconds:
|
|
logging.info("Packet arrived too soon for handling: "
|
|
"(-%0.2f seconds)",
|
|
-delta - self._allowable_time_delta_seconds)
|
|
return True
|
|
logging.info("Packet was not too soon for handling.")
|
|
return False
|
|
|
|
@property
|
|
def force_reply_options(self):
|
|
return self._force_reply_options
|
|
|
|
@force_reply_options.setter
|
|
def force_reply_options(self, value):
|
|
self._force_reply_options = value
|
|
|
|
@property
|
|
def response_packet_count(self):
|
|
return 1
|
|
|
|
def emit_warning(self, warning):
|
|
"""
|
|
Log a warning, and retain that warning as |_last_warning|.
|
|
|
|
@param warning: The warning message
|
|
"""
|
|
self.logger.warning(warning)
|
|
self._last_warning = warning
|
|
|
|
def handle(self, query_packet):
|
|
"""
|
|
The DhcpTestServer will call this method to ask a handling rule whether
|
|
it wants to take some action in response to a packet. The handler
|
|
should return some combination of RESPONSE_* bits as described above.
|
|
|
|
|packet| is a valid DHCP packet, but the values of fields and presence
|
|
of options is not guaranteed.
|
|
"""
|
|
if self.packet_is_too_late:
|
|
return RESPONSE_TEST_FAILED
|
|
if self.packet_is_too_soon:
|
|
return RESPONSE_NO_ACTION
|
|
return self.handle_impl(query_packet)
|
|
|
|
def handle_impl(self, query_packet):
|
|
logging.error("DhcpHandlingRule.handle_impl() called.")
|
|
return RESPONSE_TEST_FAILED
|
|
|
|
def respond(self, query_packet):
|
|
"""
|
|
Called by the DhcpTestServer to generate a packet to send back to the
|
|
client. This method is called if and only if the response returned from
|
|
handle() had RESPONSE_HAVE_RESPONSE set.
|
|
"""
|
|
return None
|
|
|
|
def inject_options(self, packet, requested_parameters):
|
|
"""
|
|
Adds options listed in the intersection of |requested_parameters| and
|
|
|self.options| to |packet|. Also include the options in the
|
|
intersection of |self.force_reply_options| and |self.options|.
|
|
|
|
|packet| is a DhcpPacket.
|
|
|
|
|requested_parameters| is a list of options numbers as you would find in
|
|
a DHCP_DISCOVER or DHCP_REQUEST packet after being parsed by DhcpPacket
|
|
(e.g. [1, 121, 33, 3, 6, 12]).
|
|
|
|
Subclassed handling rules may call this to inject options into response
|
|
packets to the client. This process emulates a real DHCP server which
|
|
would have a pool of configuration settings to hand out to DHCP clients
|
|
upon request.
|
|
"""
|
|
for option, value in self.options.items():
|
|
if (option.number in requested_parameters or
|
|
option in self.force_reply_options):
|
|
packet.set_option(option, value)
|
|
|
|
def inject_fields(self, packet):
|
|
"""
|
|
Adds fields listed in |self.fields| to |packet|.
|
|
|
|
|packet| is a DhcpPacket.
|
|
|
|
Subclassed handling rules may call this to inject fields into response
|
|
packets to the client. This process emulates a real DHCP server which
|
|
would have a pool of configuration settings to hand out to DHCP clients
|
|
upon request.
|
|
"""
|
|
for field, value in self.fields.items():
|
|
packet.set_field(field, value)
|
|
|
|
def is_our_message_type(self, packet):
|
|
"""
|
|
Checks if the Message Type DHCP Option in |packet| matches the message
|
|
type handled by this rule. Logs a warning if the types do not match.
|
|
|
|
@param packet: a DhcpPacket
|
|
|
|
@returns True or False
|
|
"""
|
|
if packet.message_type == self._message_type:
|
|
return True
|
|
else:
|
|
self.emit_warning("Packet's message type was %s, not %s." % (
|
|
packet.message_type.name,
|
|
self._message_type.name))
|
|
return False
|
|
|
|
|
|
class DhcpHandlingRule_RespondToDiscovery(DhcpHandlingRule):
|
|
"""
|
|
This handler will accept any DISCOVER packet received by the server. In
|
|
response to such a packet, the handler will construct an OFFER packet
|
|
offering |intended_ip| from a server at |server_ip| (from the constructor).
|
|
"""
|
|
def __init__(self,
|
|
intended_ip,
|
|
server_ip,
|
|
additional_options,
|
|
custom_fields,
|
|
should_respond=True):
|
|
"""
|
|
|intended_ip| is an IPv4 address string like "192.168.1.100".
|
|
|
|
|server_ip| is an IPv4 address string like "192.168.1.1".
|
|
|
|
|additional_options| is handled as explained by DhcpHandlingRule.
|
|
"""
|
|
super(DhcpHandlingRule_RespondToDiscovery, self).__init__(
|
|
dhcp_packet.MESSAGE_TYPE_DISCOVERY, additional_options,
|
|
custom_fields)
|
|
self._intended_ip = intended_ip
|
|
self._server_ip = server_ip
|
|
self._should_respond = should_respond
|
|
|
|
def handle_impl(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received valid DISCOVERY packet. Processing.")
|
|
ret = RESPONSE_POP_HANDLER
|
|
if self.is_final_handler:
|
|
ret |= RESPONSE_TEST_SUCCEEDED
|
|
if self._should_respond:
|
|
ret |= RESPONSE_HAVE_RESPONSE
|
|
return ret
|
|
|
|
def respond(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return None
|
|
|
|
self.logger.info("Responding to DISCOVERY packet.")
|
|
response_packet = dhcp_packet.DhcpPacket.create_offer_packet(
|
|
query_packet.transaction_id,
|
|
query_packet.client_hw_address,
|
|
self._intended_ip,
|
|
self._server_ip)
|
|
requested_parameters = query_packet.get_option(
|
|
dhcp_packet.OPTION_PARAMETER_REQUEST_LIST)
|
|
if requested_parameters is not None:
|
|
self.inject_options(response_packet, requested_parameters)
|
|
self.inject_fields(response_packet)
|
|
return response_packet
|
|
|
|
|
|
class DhcpHandlingRule_RejectRequest(DhcpHandlingRule):
|
|
"""
|
|
This handler receives a REQUEST packet, and responds with a NAK.
|
|
"""
|
|
def __init__(self):
|
|
super(DhcpHandlingRule_RejectRequest, self).__init__(
|
|
dhcp_packet.MESSAGE_TYPE_REQUEST, {}, {})
|
|
self._should_respond = True
|
|
|
|
def handle_impl(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return RESPONSE_NO_ACTION
|
|
|
|
ret = RESPONSE_POP_HANDLER
|
|
if self.is_final_handler:
|
|
ret |= RESPONSE_TEST_SUCCEEDED
|
|
if self._should_respond:
|
|
ret |= RESPONSE_HAVE_RESPONSE
|
|
return ret
|
|
|
|
def respond(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return None
|
|
|
|
self.logger.info("NAKing the REQUEST packet.")
|
|
response_packet = dhcp_packet.DhcpPacket.create_nak_packet(
|
|
query_packet.transaction_id, query_packet.client_hw_address)
|
|
return response_packet
|
|
|
|
|
|
class DhcpHandlingRule_RespondToRequest(DhcpHandlingRule):
|
|
"""
|
|
This handler accepts any REQUEST packet that contains options for SERVER_ID
|
|
and REQUESTED_IP that match |expected_server_ip| and |expected_requested_ip|
|
|
respectively. It responds with an ACKNOWLEDGEMENT packet from a DHCP server
|
|
at |response_server_ip| granting |response_granted_ip| to a client at the
|
|
address given in the REQUEST packet. If |response_server_ip| or
|
|
|response_granted_ip| are not given, then they default to
|
|
|expected_server_ip| and |expected_requested_ip| respectively.
|
|
"""
|
|
def __init__(self,
|
|
expected_requested_ip,
|
|
expected_server_ip,
|
|
additional_options,
|
|
custom_fields,
|
|
should_respond=True,
|
|
response_server_ip=None,
|
|
response_granted_ip=None,
|
|
expect_server_ip_set=True):
|
|
"""
|
|
All *_ip arguments are IPv4 address strings like "192.168.1.101".
|
|
|
|
|additional_options| is handled as explained by DhcpHandlingRule.
|
|
"""
|
|
super(DhcpHandlingRule_RespondToRequest, self).__init__(
|
|
dhcp_packet.MESSAGE_TYPE_REQUEST, additional_options,
|
|
custom_fields)
|
|
self._expected_requested_ip = expected_requested_ip
|
|
self._expected_server_ip = expected_server_ip
|
|
self._should_respond = should_respond
|
|
self._granted_ip = response_granted_ip
|
|
self._server_ip = response_server_ip
|
|
self._expect_server_ip_set = expect_server_ip_set
|
|
if self._granted_ip is None:
|
|
self._granted_ip = self._expected_requested_ip
|
|
if self._server_ip is None:
|
|
self._server_ip = self._expected_server_ip
|
|
|
|
def handle_impl(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received REQUEST packet, checking fields...")
|
|
server_ip = query_packet.get_option(dhcp_packet.OPTION_SERVER_ID)
|
|
requested_ip = query_packet.get_option(dhcp_packet.OPTION_REQUESTED_IP)
|
|
server_ip_provided = server_ip is not None
|
|
if ((server_ip_provided != self._expect_server_ip_set) or
|
|
(requested_ip is None)):
|
|
self.logger.info("REQUEST packet did not have the expected "
|
|
"options, discarding.")
|
|
return RESPONSE_NO_ACTION
|
|
|
|
if server_ip_provided and server_ip != self._expected_server_ip:
|
|
self.emit_warning("REQUEST packet's server ip did not match our "
|
|
"expectations; expected %s but got %s" %
|
|
(self._expected_server_ip, server_ip))
|
|
return RESPONSE_NO_ACTION
|
|
|
|
if requested_ip != self._expected_requested_ip:
|
|
self.emit_warning("REQUEST packet's requested IP did not match "
|
|
"our expectations; expected %s but got %s" %
|
|
(self._expected_requested_ip, requested_ip))
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received valid REQUEST packet, processing")
|
|
ret = RESPONSE_POP_HANDLER
|
|
if self.is_final_handler:
|
|
ret |= RESPONSE_TEST_SUCCEEDED
|
|
if self._should_respond:
|
|
ret |= RESPONSE_HAVE_RESPONSE
|
|
return ret
|
|
|
|
def respond(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return None
|
|
|
|
self.logger.info("Responding to REQUEST packet.")
|
|
response_packet = dhcp_packet.DhcpPacket.create_acknowledgement_packet(
|
|
query_packet.transaction_id,
|
|
query_packet.client_hw_address,
|
|
self._granted_ip,
|
|
self._server_ip)
|
|
requested_parameters = query_packet.get_option(
|
|
dhcp_packet.OPTION_PARAMETER_REQUEST_LIST)
|
|
if requested_parameters is not None:
|
|
self.inject_options(response_packet, requested_parameters)
|
|
self.inject_fields(response_packet)
|
|
return response_packet
|
|
|
|
|
|
class DhcpHandlingRule_RespondToPostT2Request(
|
|
DhcpHandlingRule_RespondToRequest):
|
|
"""
|
|
This handler is a lot like DhcpHandlingRule_RespondToRequest except that it
|
|
expects request packets like those sent after the T2 deadline (see RFC
|
|
2131). This is the only time that you can find a request packet without the
|
|
SERVER_ID option. It responds to packets in exactly the same way.
|
|
"""
|
|
def __init__(self,
|
|
expected_requested_ip,
|
|
response_server_ip,
|
|
additional_options,
|
|
custom_fields,
|
|
should_respond=True,
|
|
response_granted_ip=None):
|
|
"""
|
|
All *_ip arguments are IPv4 address strings like "192.168.1.101".
|
|
|
|
|additional_options| is handled as explained by DhcpHandlingRule.
|
|
"""
|
|
super(DhcpHandlingRule_RespondToPostT2Request, self).__init__(
|
|
expected_requested_ip,
|
|
None,
|
|
additional_options,
|
|
custom_fields,
|
|
should_respond=should_respond,
|
|
response_server_ip=response_server_ip,
|
|
response_granted_ip=response_granted_ip)
|
|
|
|
def handle_impl(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received REQUEST packet, checking fields...")
|
|
if query_packet.get_option(dhcp_packet.OPTION_SERVER_ID) is not None:
|
|
self.logger.info("REQUEST packet had a SERVER_ID option, which it "
|
|
"is not expected to have, discarding.")
|
|
return RESPONSE_NO_ACTION
|
|
|
|
requested_ip = query_packet.get_option(dhcp_packet.OPTION_REQUESTED_IP)
|
|
if requested_ip is None:
|
|
self.logger.info("REQUEST packet did not have the expected "
|
|
"request ip option at all, discarding.")
|
|
return RESPONSE_NO_ACTION
|
|
|
|
if requested_ip != self._expected_requested_ip:
|
|
self.emit_warning("REQUEST packet's requested IP did not match "
|
|
"our expectations; expected %s but got %s" %
|
|
(self._expected_requested_ip, requested_ip))
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received valid post T2 REQUEST packet, processing")
|
|
ret = RESPONSE_POP_HANDLER
|
|
if self.is_final_handler:
|
|
ret |= RESPONSE_TEST_SUCCEEDED
|
|
if self._should_respond:
|
|
ret |= RESPONSE_HAVE_RESPONSE
|
|
return ret
|
|
|
|
|
|
class DhcpHandlingRule_AcceptRelease(DhcpHandlingRule):
|
|
"""
|
|
This handler accepts any RELEASE packet that contains an option for
|
|
SERVER_ID matches |expected_server_ip|. There is no response to this
|
|
packet.
|
|
"""
|
|
def __init__(self,
|
|
expected_server_ip,
|
|
additional_options,
|
|
custom_fields):
|
|
"""
|
|
All *_ip arguments are IPv4 address strings like "192.168.1.101".
|
|
|
|
|additional_options| is handled as explained by DhcpHandlingRule.
|
|
"""
|
|
super(DhcpHandlingRule_AcceptRelease, self).__init__(
|
|
dhcp_packet.MESSAGE_TYPE_RELEASE, additional_options,
|
|
custom_fields)
|
|
self._expected_server_ip = expected_server_ip
|
|
|
|
def handle_impl(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received RELEASE packet, checking fields...")
|
|
server_ip = query_packet.get_option(dhcp_packet.OPTION_SERVER_ID)
|
|
if server_ip is None:
|
|
self.logger.info("RELEASE packet did not have the expected "
|
|
"options, discarding.")
|
|
return RESPONSE_NO_ACTION
|
|
|
|
if server_ip != self._expected_server_ip:
|
|
self.emit_warning("RELEASE packet's server ip did not match our "
|
|
"expectations; expected %s but got %s" %
|
|
(self._expected_server_ip, server_ip))
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received valid RELEASE packet, processing")
|
|
ret = RESPONSE_POP_HANDLER
|
|
if self.is_final_handler:
|
|
ret |= RESPONSE_TEST_SUCCEEDED
|
|
return ret
|
|
|
|
|
|
class DhcpHandlingRule_RejectAndRespondToRequest(
|
|
DhcpHandlingRule_RespondToRequest):
|
|
"""
|
|
This handler accepts any REQUEST packet that contains options for SERVER_ID
|
|
and REQUESTED_IP that match |expected_server_ip| and |expected_requested_ip|
|
|
respectively. It responds with both an ACKNOWLEDGEMENT packet from a DHCP
|
|
server as well as a NAK, in order to simulate a network with two conflicting
|
|
servers.
|
|
"""
|
|
def __init__(self,
|
|
expected_requested_ip,
|
|
expected_server_ip,
|
|
additional_options,
|
|
custom_fields,
|
|
send_nak_before_ack):
|
|
super(DhcpHandlingRule_RejectAndRespondToRequest, self).__init__(
|
|
expected_requested_ip,
|
|
expected_server_ip,
|
|
additional_options,
|
|
custom_fields)
|
|
self._send_nak_before_ack = send_nak_before_ack
|
|
self._response_counter = 0
|
|
|
|
@property
|
|
def response_packet_count(self):
|
|
return 2
|
|
|
|
def respond(self, query_packet):
|
|
""" Respond to |query_packet| with a NAK then ACK or ACK then NAK. """
|
|
if ((self._response_counter == 0 and self._send_nak_before_ack) or
|
|
(self._response_counter != 0 and not self._send_nak_before_ack)):
|
|
response_packet = dhcp_packet.DhcpPacket.create_nak_packet(
|
|
query_packet.transaction_id, query_packet.client_hw_address)
|
|
else:
|
|
response_packet = super(DhcpHandlingRule_RejectAndRespondToRequest,
|
|
self).respond(query_packet)
|
|
self._response_counter += 1
|
|
return response_packet
|
|
|
|
|
|
class DhcpHandlingRule_AcceptDecline(DhcpHandlingRule):
|
|
"""
|
|
This handler accepts any DECLINE packet that contains an option for
|
|
SERVER_ID matches |expected_server_ip|. There is no response to this
|
|
packet.
|
|
"""
|
|
def __init__(self,
|
|
expected_server_ip,
|
|
additional_options,
|
|
custom_fields):
|
|
"""
|
|
All *_ip arguments are IPv4 address strings like "192.168.1.101".
|
|
|
|
|additional_options| is handled as explained by DhcpHandlingRule.
|
|
"""
|
|
super(DhcpHandlingRule_AcceptDecline, self).__init__(
|
|
dhcp_packet.MESSAGE_TYPE_DECLINE, additional_options,
|
|
custom_fields)
|
|
self._expected_server_ip = expected_server_ip
|
|
|
|
def handle_impl(self, query_packet):
|
|
if not self.is_our_message_type(query_packet):
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received DECLINE packet, checking fields...")
|
|
server_ip = query_packet.get_option(dhcp_packet.OPTION_SERVER_ID)
|
|
if server_ip is None:
|
|
self.logger.info("DECLINE packet did not have the expected "
|
|
"options, discarding.")
|
|
return RESPONSE_NO_ACTION
|
|
|
|
if server_ip != self._expected_server_ip:
|
|
self.emit_warning("DECLINE packet's server ip did not match our "
|
|
"expectations; expected %s but got %s" %
|
|
(self._expected_server_ip, server_ip))
|
|
return RESPONSE_NO_ACTION
|
|
|
|
self.logger.info("Received valid DECLINE packet, processing")
|
|
ret = RESPONSE_POP_HANDLER
|
|
if self.is_final_handler:
|
|
ret |= RESPONSE_TEST_SUCCEEDED
|
|
return ret
|