11964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 21964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley# Use of this source code is governed by a BSD-style license that can be 31964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley# found in the LICENSE file. 41964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 51964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley""" 61964458a980b17c71ea10f9c00c59fe15bb83a96Christopher WileyDHCP handling rules are ways to record expectations for a DhcpTestServer. 71964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 81964458a980b17c71ea10f9c00c59fe15bb83a96Christopher WileyWhen a handling rule reaches the front of the DhcpTestServer handling rule 91964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyqueue, the server begins to ask the rule what it should do with each incoming 107d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher WileyDHCP packet (in the form of a DhcpPacket). The handle() method is expected to 111964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyreturn a tuple (response, action) where response indicates whether the packet 121964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyshould be ignored or responded to and whether the test failed, succeeded, or is 131964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileycontinuing. The action part of the tuple refers to whether or not the rule 141964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyshould be be removed from the test server's handling rule queue. 151964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley""" 161964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 171964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyimport logging 189b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wileyimport time 191964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 201964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyfrom autotest_lib.client.cros import dhcp_packet 211964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 227d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# Drops the packet and acts like it never happened. 237d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher WileyRESPONSE_NO_ACTION = 0 247d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# Signals that the handler wishes to send a packet. 257d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher WileyRESPONSE_HAVE_RESPONSE = 1 << 0 267d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# Signals that the handler wishes to be removed from the handling queue. 277d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# The handler will be asked to generate a packet first if the handler signalled 287d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# that it wished to do so with RESPONSE_HAVE_RESPONSE. 297d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher WileyRESPONSE_POP_HANDLER = 1 << 1 307d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# Signals that the handler wants to end the test on a failure. 317d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher WileyRESPONSE_TEST_FAILED = 1 << 2 327d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# Signals that the handler wants to end the test because it succeeded. 337d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley# Note that the failure bit has precedence over the success bit. 347d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher WileyRESPONSE_TEST_SUCCEEDED = 1 << 3 351964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 361964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyclass DhcpHandlingRule(object): 3730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 3830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley DhcpHandlingRule defines an interface between the DhcpTestServer and 3930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley subclasses of DhcpHandlingRule. A handling rule at the front of the 4030b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley DhcpTestServer rule queue is first asked what should be done with a packet 417d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley via handle(). handle() returns a bitfield as described above. If the 427d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley response from handle() indicates that a packet should be sent in response, 437d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley the server asks the handling rule to construct a response packet via 447d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley respond(). 4530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 467d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley 478962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal def __init__(self, message_type, additional_options, custom_fields): 4830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 498962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal |message_type| should be a MessageType, from DhcpPacket. 5030b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |additional_options| should be a dictionary that maps from 5130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley dhcp_packet.OPTION_* to values. For instance: 5230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 5330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley {dhcp_packet.OPTION_SERVER_ID : "10.10.10.1"} 5430b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 5530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley These options are injected into response packets if the client requests 5630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley it. See inject_options(). 5730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 581964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley super(DhcpHandlingRule, self).__init__() 591964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self._is_final_handler = False 601964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self._logger = logging.getLogger("dhcp.handling_rule") 6130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley self._options = additional_options 623a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart self._fields = custom_fields 639b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley self._target_time_seconds = None 649b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley self._allowable_time_delta_seconds = 0.5 658d2348b6332131cf784075fb1882ed17e851d399Paul Stewart self._force_reply_options = [] 668962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal self._message_type = message_type 67f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self._last_warning = None 68f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal 69f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal def __str__(self): 70f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal if self._last_warning: 71f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal return '%s (%s)' % (self.__class__.__name__, self._last_warning) 72f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal else: 73f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal return self.__class__.__name__ 741964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 751964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley @property 761964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley def logger(self): 771964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley return self._logger 781964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 791964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley @property 801964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley def is_final_handler(self): 811964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley return self._is_final_handler 821964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 831964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley @is_final_handler.setter 841964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley def is_final_handler(self, value): 851964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self._is_final_handler = value 861964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 8730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley @property 8830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley def options(self): 8930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 9030b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley Returns a dictionary that maps from DhcpPacket options to their values. 9130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 9230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley return self._options 931964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 949b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley @property 953a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart def fields(self): 963a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart """ 973a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart Returns a dictionary that maps from DhcpPacket fields to their values. 983a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart """ 993a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart return self._fields 1003a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart 1013a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart @property 1029b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def target_time_seconds(self): 1039b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley """ 1049b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley If this is not None, packets will be rejected if they don't fall within 1059b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley |self.allowable_time_delta_seconds| seconds of 1069b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley |self.target_time_seconds|. A value of None will cause this handler to 1079b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley ignore the target packet time. 1089b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1099b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley Defaults to None. 1109b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley """ 1119b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return self._target_time_seconds 1129b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1139b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley @target_time_seconds.setter 1149b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def target_time_seconds(self, value): 1159b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley self._target_time_seconds = value 1169b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1179b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley @property 1189b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def allowable_time_delta_seconds(self): 1199b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley """ 1209b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley A configurable fudge factor for |self.target_time_seconds|. If a packet 1219b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley comes in at time T and: 1229b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1239b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley delta = abs(T - |self.target_time_seconds|) 1249b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1259b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley Then if delta < |self.allowable_time_delta_seconds|, we accept the 1269b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley packet. Otherwise we either fail the test or ignore the packet, 1279b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley depending on whether this packet is before or after the window. 1289b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1299b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley Defaults to 0.5 seconds. 1309b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley """ 1319b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return self._allowable_time_delta_seconds 1329b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1339b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley @allowable_time_delta_seconds.setter 1349b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def allowable_time_delta_seconds(self, value): 1359b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley self._allowable_time_delta_seconds = value 1369b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1379b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley @property 1389b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def packet_is_too_late(self): 1399b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley if self.target_time_seconds is None: 1409b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return False 1416e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley delta = time.time() - self.target_time_seconds 1426e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley logging.debug("Handler received packet %0.2f seconds from target time.", 1436e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley delta) 1446e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley if delta > self._allowable_time_delta_seconds: 1456e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley logging.info("Packet was too late for handling (+%0.2f seconds)", 1466e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley delta - self._allowable_time_delta_seconds) 1479b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return True 1489b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley logging.info("Packet was not too late for handling.") 1499b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return False 1509b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1519b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley @property 1529b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def packet_is_too_soon(self): 1539b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley if self.target_time_seconds is None: 1549b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return False 1556e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley delta = time.time() - self.target_time_seconds 1566e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley logging.debug("Handler received packet %0.2f seconds from target time.", 1576e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley delta) 1586e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley if -delta > self._allowable_time_delta_seconds: 1596e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley logging.info("Packet arrived too soon for handling: " 1606e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley "(-%0.2f seconds)", 1616e9010b3ec8d09858be425292e4f83c9bb54dbf5Christopher Wiley -delta - self._allowable_time_delta_seconds) 1629b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return True 1639b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley logging.info("Packet was not too soon for handling.") 1649b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return False 1659b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 1668d2348b6332131cf784075fb1882ed17e851d399Paul Stewart @property 1678d2348b6332131cf784075fb1882ed17e851d399Paul Stewart def force_reply_options(self): 1688d2348b6332131cf784075fb1882ed17e851d399Paul Stewart return self._force_reply_options 1698d2348b6332131cf784075fb1882ed17e851d399Paul Stewart 1708d2348b6332131cf784075fb1882ed17e851d399Paul Stewart @force_reply_options.setter 1718d2348b6332131cf784075fb1882ed17e851d399Paul Stewart def force_reply_options(self, value): 1728d2348b6332131cf784075fb1882ed17e851d399Paul Stewart self._force_reply_options = value 1738d2348b6332131cf784075fb1882ed17e851d399Paul Stewart 174e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart @property 175e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart def response_packet_count(self): 176e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart return 1 177e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart 178f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal def emit_warning(self, warning): 179f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal """ 180f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal Log a warning, and retain that warning as |_last_warning|. 181f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal 182f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal @param warning: The warning message 183f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal """ 184f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self.logger.warning(warning) 185f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self._last_warning = warning 186f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal 1879b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def handle(self, query_packet): 18830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 18930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley The DhcpTestServer will call this method to ask a handling rule whether 1907d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley it wants to take some action in response to a packet. The handler 1917d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley should return some combination of RESPONSE_* bits as described above. 19230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 19330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |packet| is a valid DHCP packet, but the values of fields and presence 19430b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley of options is not guaranteed. 19530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 1969b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley if self.packet_is_too_late: 1979b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return RESPONSE_TEST_FAILED 1989b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley if self.packet_is_too_soon: 1999b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return RESPONSE_NO_ACTION 2009b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley return self.handle_impl(query_packet) 2019b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley 2029b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def handle_impl(self, query_packet): 2034df7ac613759f0fd848619864e389454c70bc505Christopher Wiley logging.error("DhcpHandlingRule.handle_impl() called.") 2047d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return RESPONSE_TEST_FAILED 2051964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 20630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley def respond(self, query_packet): 20730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 2087d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley Called by the DhcpTestServer to generate a packet to send back to the 2097d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley client. This method is called if and only if the response returned from 2107d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley handle() had RESPONSE_HAVE_RESPONSE set. 21130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 2121964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley return None 2131964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 21430b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley def inject_options(self, packet, requested_parameters): 21530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 21630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley Adds options listed in the intersection of |requested_parameters| and 2178d2348b6332131cf784075fb1882ed17e851d399Paul Stewart |self.options| to |packet|. Also include the options in the 2188d2348b6332131cf784075fb1882ed17e851d399Paul Stewart intersection of |self.force_reply_options| and |self.options|. 21930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 22030b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |packet| is a DhcpPacket. 22130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 22230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |requested_parameters| is a list of options numbers as you would find in 22330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley a DHCP_DISCOVER or DHCP_REQUEST packet after being parsed by DhcpPacket 22430b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley (e.g. [1, 121, 33, 3, 6, 12]). 22530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 22630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley Subclassed handling rules may call this to inject options into response 22730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley packets to the client. This process emulates a real DHCP server which 22830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley would have a pool of configuration settings to hand out to DHCP clients 22930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley upon request. 23030b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 23130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley for option, value in self.options.items(): 2328d2348b6332131cf784075fb1882ed17e851d399Paul Stewart if (option.number in requested_parameters or 2338d2348b6332131cf784075fb1882ed17e851d399Paul Stewart option in self.force_reply_options): 23430b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley packet.set_option(option, value) 23530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 2363a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart def inject_fields(self, packet): 2373a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart """ 2383a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart Adds fields listed in |self.fields| to |packet|. 2393a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart 2403a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart |packet| is a DhcpPacket. 2413a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart 2423a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart Subclassed handling rules may call this to inject fields into response 2433a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart packets to the client. This process emulates a real DHCP server which 2443a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart would have a pool of configuration settings to hand out to DHCP clients 2453a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart upon request. 2463a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart """ 2473a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart for field, value in self.fields.items(): 2483a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart packet.set_field(field, value) 2493a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart 2508962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal def is_our_message_type(self, packet): 2518962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal """ 2528962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal Checks if the Message Type DHCP Option in |packet| matches the message 2538962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal type handled by this rule. Logs a warning if the types do not match. 2548962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal 2558962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal @param packet: a DhcpPacket 2568962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal 2578962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal @returns True or False 2588962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal """ 2598962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal if packet.message_type == self._message_type: 2608962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal return True 2618962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal else: 262f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self.emit_warning("Packet's message type was %s, not %s." % ( 263f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal packet.message_type.name, 264f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self._message_type.name)) 2658962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal return False 2668962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal 26730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 2681964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wileyclass DhcpHandlingRule_RespondToDiscovery(DhcpHandlingRule): 26930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 27030b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley This handler will accept any DISCOVER packet received by the server. In 27130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley response to such a packet, the handler will construct an OFFER packet 27230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley offering |intended_ip| from a server at |server_ip| (from the constructor). 27330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 2741964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley def __init__(self, 2751964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley intended_ip, 2761964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley server_ip, 2777d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley additional_options, 2783a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart custom_fields, 2797d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley should_respond=True): 28030b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 28130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |intended_ip| is an IPv4 address string like "192.168.1.100". 28230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 28330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |server_ip| is an IPv4 address string like "192.168.1.1". 28430b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 28530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |additional_options| is handled as explained by DhcpHandlingRule. 28630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 28730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley super(DhcpHandlingRule_RespondToDiscovery, self).__init__( 2888962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal dhcp_packet.MESSAGE_TYPE_DISCOVERY, additional_options, 2898962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal custom_fields) 2901964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self._intended_ip = intended_ip 2911964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self._server_ip = server_ip 2927d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley self._should_respond = should_respond 2931964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 2949b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def handle_impl(self, query_packet): 2958962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal if not self.is_our_message_type(query_packet): 2967d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return RESPONSE_NO_ACTION 2977d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley 2981964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self.logger.info("Received valid DISCOVERY packet. Processing.") 2997d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley ret = RESPONSE_POP_HANDLER 3001964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley if self.is_final_handler: 3017d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley ret |= RESPONSE_TEST_SUCCEEDED 3027d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley if self._should_respond: 3037d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley ret |= RESPONSE_HAVE_RESPONSE 3047d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return ret 3051964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley 30630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley def respond(self, query_packet): 3078962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal if not self.is_our_message_type(query_packet): 3081964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley return None 3098962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal 3101964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self.logger.info("Responding to DISCOVERY packet.") 31130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley response_packet = dhcp_packet.DhcpPacket.create_offer_packet( 31230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley query_packet.transaction_id, 31330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley query_packet.client_hw_address, 3141964458a980b17c71ea10f9c00c59fe15bb83a96Christopher Wiley self._intended_ip, 31530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley self._server_ip) 31630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley requested_parameters = query_packet.get_option( 31730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley dhcp_packet.OPTION_PARAMETER_REQUEST_LIST) 31830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley if requested_parameters is not None: 31930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley self.inject_options(response_packet, requested_parameters) 3203a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart self.inject_fields(response_packet) 32130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley return response_packet 32230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 32319b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 3242b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawalclass DhcpHandlingRule_RejectRequest(DhcpHandlingRule): 3252b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal """ 3262b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal This handler receives a REQUEST packet, and responds with a NAK. 3272b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal """ 3282b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal def __init__(self): 3292b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal super(DhcpHandlingRule_RejectRequest, self).__init__( 3302b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal dhcp_packet.MESSAGE_TYPE_REQUEST, {}, {}) 3312b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal self._should_respond = True 3322b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal 3332b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal def handle_impl(self, query_packet): 3342b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal if not self.is_our_message_type(query_packet): 3352b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal return RESPONSE_NO_ACTION 3362b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal 3372b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal ret = RESPONSE_POP_HANDLER 3382b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal if self.is_final_handler: 3392b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal ret |= RESPONSE_TEST_SUCCEEDED 3402b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal if self._should_respond: 3412b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal ret |= RESPONSE_HAVE_RESPONSE 3422b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal return ret 3432b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal 3442b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal def respond(self, query_packet): 3452b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal if not self.is_our_message_type(query_packet): 3462b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal return None 3472b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal 3482b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal self.logger.info("NAKing the REQUEST packet.") 3492b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal response_packet = dhcp_packet.DhcpPacket.create_nak_packet( 3502b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal query_packet.transaction_id, query_packet.client_hw_address) 3512b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal return response_packet 3522b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal 3532b680b292bdc3dcba5398563fb25b3613796039fmukesh agrawal 35419b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wileyclass DhcpHandlingRule_RespondToRequest(DhcpHandlingRule): 35530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 3566c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley This handler accepts any REQUEST packet that contains options for SERVER_ID 3576c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley and REQUESTED_IP that match |expected_server_ip| and |expected_requested_ip| 3586c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley respectively. It responds with an ACKNOWLEDGEMENT packet from a DHCP server 3596c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley at |response_server_ip| granting |response_granted_ip| to a client at the 3606c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley address given in the REQUEST packet. If |response_server_ip| or 3616c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley |response_granted_ip| are not given, then they default to 3626c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley |expected_server_ip| and |expected_requested_ip| respectively. 36330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 36419b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley def __init__(self, 36519b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley expected_requested_ip, 36619b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley expected_server_ip, 36730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley additional_options, 3683a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart custom_fields, 3697d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley should_respond=True, 3706c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley response_server_ip=None, 37169769b6f92468592d0f22679ed0e683aa88505e8Paul Stewart response_granted_ip=None, 37269769b6f92468592d0f22679ed0e683aa88505e8Paul Stewart expect_server_ip_set=True): 37330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 37430b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley All *_ip arguments are IPv4 address strings like "192.168.1.101". 37530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley 37630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley |additional_options| is handled as explained by DhcpHandlingRule. 37730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley """ 37830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley super(DhcpHandlingRule_RespondToRequest, self).__init__( 3798962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal dhcp_packet.MESSAGE_TYPE_REQUEST, additional_options, 3808962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal custom_fields) 38119b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self._expected_requested_ip = expected_requested_ip 38219b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self._expected_server_ip = expected_server_ip 3837d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley self._should_respond = should_respond 3846c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley self._granted_ip = response_granted_ip 3856c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley self._server_ip = response_server_ip 38669769b6f92468592d0f22679ed0e683aa88505e8Paul Stewart self._expect_server_ip_set = expect_server_ip_set 3876c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley if self._granted_ip is None: 38819b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self._granted_ip = self._expected_requested_ip 38919b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley if self._server_ip is None: 39019b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self._server_ip = self._expected_server_ip 39119b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 3929b95bdd5945e23f48bbc5910f34a6a571444642aChristopher Wiley def handle_impl(self, query_packet): 3938962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal if not self.is_our_message_type(query_packet): 3947d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return RESPONSE_NO_ACTION 39519b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 39619b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self.logger.info("Received REQUEST packet, checking fields...") 39730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley server_ip = query_packet.get_option(dhcp_packet.OPTION_SERVER_ID) 39830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley requested_ip = query_packet.get_option(dhcp_packet.OPTION_REQUESTED_IP) 39969769b6f92468592d0f22679ed0e683aa88505e8Paul Stewart server_ip_provided = server_ip is not None 40069769b6f92468592d0f22679ed0e683aa88505e8Paul Stewart if ((server_ip_provided != self._expect_server_ip_set) or 40169769b6f92468592d0f22679ed0e683aa88505e8Paul Stewart (requested_ip is None)): 40219b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self.logger.info("REQUEST packet did not have the expected " 40319b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley "options, discarding.") 4047d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return RESPONSE_NO_ACTION 40519b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 40669769b6f92468592d0f22679ed0e683aa88505e8Paul Stewart if server_ip_provided and server_ip != self._expected_server_ip: 407f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self.emit_warning("REQUEST packet's server ip did not match our " 408f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal "expectations; expected %s but got %s" % 409f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal (self._expected_server_ip, server_ip)) 4107d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return RESPONSE_NO_ACTION 41119b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 41290c515dd484c31e2973142660a71bd596314a89bChristopher Wiley if requested_ip != self._expected_requested_ip: 413f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self.emit_warning("REQUEST packet's requested IP did not match " 414f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal "our expectations; expected %s but got %s" % 415f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal (self._expected_requested_ip, requested_ip)) 4167d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return RESPONSE_NO_ACTION 41719b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 41819b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self.logger.info("Received valid REQUEST packet, processing") 4197d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley ret = RESPONSE_POP_HANDLER 42019b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley if self.is_final_handler: 4217d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley ret |= RESPONSE_TEST_SUCCEEDED 4227d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley if self._should_respond: 4237d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley ret |= RESPONSE_HAVE_RESPONSE 4247d36aac2e68c1189b9c502cd095ed0edcef152c1Christopher Wiley return ret 42519b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 42630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley def respond(self, query_packet): 4278962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal if not self.is_our_message_type(query_packet): 42819b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley return None 42919b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley 43019b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self.logger.info("Responding to REQUEST packet.") 43130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley response_packet = dhcp_packet.DhcpPacket.create_acknowledgement_packet( 43230b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley query_packet.transaction_id, 43330b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley query_packet.client_hw_address, 43419b39f62edb37c2971b4a32fb9aad664ccd36c90Christopher Wiley self._granted_ip, 43530b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley self._server_ip) 43630b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley requested_parameters = query_packet.get_option( 43730b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley dhcp_packet.OPTION_PARAMETER_REQUEST_LIST) 43830b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley if requested_parameters is not None: 43930b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley self.inject_options(response_packet, requested_parameters) 4403a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart self.inject_fields(response_packet) 44130b095f46114605a4013a2840c02ea90cf76f9e9Christopher Wiley return response_packet 4426c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4436c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4446c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wileyclass DhcpHandlingRule_RespondToPostT2Request( 4456c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley DhcpHandlingRule_RespondToRequest): 4466c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley """ 4476c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley This handler is a lot like DhcpHandlingRule_RespondToRequest except that it 4486c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley expects request packets like those sent after the T2 deadline (see RFC 4496c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 2131). This is the only time that you can find a request packet without the 4506c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley SERVER_ID option. It responds to packets in exactly the same way. 4516c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley """ 4526c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley def __init__(self, 4536c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley expected_requested_ip, 4546c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley response_server_ip, 4556c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley additional_options, 4563a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart custom_fields, 4576c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley should_respond=True, 4586c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley response_granted_ip=None): 4596c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley """ 4606c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley All *_ip arguments are IPv4 address strings like "192.168.1.101". 4616c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4626c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley |additional_options| is handled as explained by DhcpHandlingRule. 4636c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley """ 4646c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley super(DhcpHandlingRule_RespondToPostT2Request, self).__init__( 4656c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley expected_requested_ip, 4666c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley None, 4676c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley additional_options, 4683a37ed1648539f17b30e0e1eabcfff11fed2a8f5Paul Stewart custom_fields, 4696c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley should_respond=should_respond, 4706c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley response_server_ip=response_server_ip, 4716c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley response_granted_ip=response_granted_ip) 4726c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4736c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley def handle_impl(self, query_packet): 4748962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal if not self.is_our_message_type(query_packet): 4756c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley return RESPONSE_NO_ACTION 4766c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4776c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley self.logger.info("Received REQUEST packet, checking fields...") 4786c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley if query_packet.get_option(dhcp_packet.OPTION_SERVER_ID) is not None: 4796c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley self.logger.info("REQUEST packet had a SERVER_ID option, which it " 4806c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley "is not expected to have, discarding.") 4816c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley return RESPONSE_NO_ACTION 4826c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4836c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley requested_ip = query_packet.get_option(dhcp_packet.OPTION_REQUESTED_IP) 4846c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley if requested_ip is None: 4856c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley self.logger.info("REQUEST packet did not have the expected " 4866c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley "request ip option at all, discarding.") 4876c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley return RESPONSE_NO_ACTION 4886c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4896c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley if requested_ip != self._expected_requested_ip: 490f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self.emit_warning("REQUEST packet's requested IP did not match " 491f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal "our expectations; expected %s but got %s" % 492f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal (self._expected_requested_ip, requested_ip)) 4936c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley return RESPONSE_NO_ACTION 4946c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley 4956c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley self.logger.info("Received valid post T2 REQUEST packet, processing") 4966c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley ret = RESPONSE_POP_HANDLER 4976c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley if self.is_final_handler: 4986c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley ret |= RESPONSE_TEST_SUCCEEDED 4996c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley if self._should_respond: 5006c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley ret |= RESPONSE_HAVE_RESPONSE 5016c921a7efb5c263d47322aaf77ae7adfa6c47752Christopher Wiley return ret 5026c6a1425996beee22a4a53639197308e250b708cPaul Stewart 5036c6a1425996beee22a4a53639197308e250b708cPaul Stewart 5046c6a1425996beee22a4a53639197308e250b708cPaul Stewartclass DhcpHandlingRule_AcceptRelease(DhcpHandlingRule): 5056c6a1425996beee22a4a53639197308e250b708cPaul Stewart """ 5066c6a1425996beee22a4a53639197308e250b708cPaul Stewart This handler accepts any RELEASE packet that contains an option for 5076c6a1425996beee22a4a53639197308e250b708cPaul Stewart SERVER_ID matches |expected_server_ip|. There is no response to this 5086c6a1425996beee22a4a53639197308e250b708cPaul Stewart packet. 5096c6a1425996beee22a4a53639197308e250b708cPaul Stewart """ 5106c6a1425996beee22a4a53639197308e250b708cPaul Stewart def __init__(self, 5116c6a1425996beee22a4a53639197308e250b708cPaul Stewart expected_server_ip, 5126c6a1425996beee22a4a53639197308e250b708cPaul Stewart additional_options, 5136c6a1425996beee22a4a53639197308e250b708cPaul Stewart custom_fields): 5146c6a1425996beee22a4a53639197308e250b708cPaul Stewart """ 5156c6a1425996beee22a4a53639197308e250b708cPaul Stewart All *_ip arguments are IPv4 address strings like "192.168.1.101". 5166c6a1425996beee22a4a53639197308e250b708cPaul Stewart 5176c6a1425996beee22a4a53639197308e250b708cPaul Stewart |additional_options| is handled as explained by DhcpHandlingRule. 5186c6a1425996beee22a4a53639197308e250b708cPaul Stewart """ 5196c6a1425996beee22a4a53639197308e250b708cPaul Stewart super(DhcpHandlingRule_AcceptRelease, self).__init__( 5208962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal dhcp_packet.MESSAGE_TYPE_RELEASE, additional_options, 5218962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal custom_fields) 5226c6a1425996beee22a4a53639197308e250b708cPaul Stewart self._expected_server_ip = expected_server_ip 5236c6a1425996beee22a4a53639197308e250b708cPaul Stewart 5246c6a1425996beee22a4a53639197308e250b708cPaul Stewart def handle_impl(self, query_packet): 5258962b2d93ea3ac596b0ac46b1798c67bccd5e3d2mukesh agrawal if not self.is_our_message_type(query_packet): 5266c6a1425996beee22a4a53639197308e250b708cPaul Stewart return RESPONSE_NO_ACTION 5276c6a1425996beee22a4a53639197308e250b708cPaul Stewart 5286c6a1425996beee22a4a53639197308e250b708cPaul Stewart self.logger.info("Received RELEASE packet, checking fields...") 5296c6a1425996beee22a4a53639197308e250b708cPaul Stewart server_ip = query_packet.get_option(dhcp_packet.OPTION_SERVER_ID) 530f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart if server_ip is None: 5316c6a1425996beee22a4a53639197308e250b708cPaul Stewart self.logger.info("RELEASE packet did not have the expected " 5326c6a1425996beee22a4a53639197308e250b708cPaul Stewart "options, discarding.") 5336c6a1425996beee22a4a53639197308e250b708cPaul Stewart return RESPONSE_NO_ACTION 5346c6a1425996beee22a4a53639197308e250b708cPaul Stewart 5356c6a1425996beee22a4a53639197308e250b708cPaul Stewart if server_ip != self._expected_server_ip: 536f56f7fde778822d17965bcd73c67dac1bb422829mukesh agrawal self.emit_warning("RELEASE packet's server ip did not match our " 5376c6a1425996beee22a4a53639197308e250b708cPaul Stewart "expectations; expected %s but got %s" % 5386c6a1425996beee22a4a53639197308e250b708cPaul Stewart (self._expected_server_ip, server_ip)) 5396c6a1425996beee22a4a53639197308e250b708cPaul Stewart return RESPONSE_NO_ACTION 5406c6a1425996beee22a4a53639197308e250b708cPaul Stewart 5416c6a1425996beee22a4a53639197308e250b708cPaul Stewart self.logger.info("Received valid RELEASE packet, processing") 5426c6a1425996beee22a4a53639197308e250b708cPaul Stewart ret = RESPONSE_POP_HANDLER 5436c6a1425996beee22a4a53639197308e250b708cPaul Stewart if self.is_final_handler: 5446c6a1425996beee22a4a53639197308e250b708cPaul Stewart ret |= RESPONSE_TEST_SUCCEEDED 5456c6a1425996beee22a4a53639197308e250b708cPaul Stewart return ret 546e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart 547e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart 548e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewartclass DhcpHandlingRule_RejectAndRespondToRequest( 549e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart DhcpHandlingRule_RespondToRequest): 550e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart """ 551e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart This handler accepts any REQUEST packet that contains options for SERVER_ID 552e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart and REQUESTED_IP that match |expected_server_ip| and |expected_requested_ip| 553e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart respectively. It responds with both an ACKNOWLEDGEMENT packet from a DHCP 554e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart server as well as a NAK, in order to simulate a network with two conflicting 555e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart servers. 556e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart """ 557e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart def __init__(self, 558e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart expected_requested_ip, 559e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart expected_server_ip, 560e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart additional_options, 561e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart custom_fields, 562e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart send_nak_before_ack): 563e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart super(DhcpHandlingRule_RejectAndRespondToRequest, self).__init__( 564e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart expected_requested_ip, 565e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart expected_server_ip, 566e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart additional_options, 567e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart custom_fields) 568e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart self._send_nak_before_ack = send_nak_before_ack 569e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart self._response_counter = 0 570e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart 571e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart @property 572e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart def response_packet_count(self): 573e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart return 2 574e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart 575e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart def respond(self, query_packet): 576e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart """ Respond to |query_packet| with a NAK then ACK or ACK then NAK. """ 577e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart if ((self._response_counter == 0 and self._send_nak_before_ack) or 578e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart (self._response_counter != 0 and not self._send_nak_before_ack)): 579e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart response_packet = dhcp_packet.DhcpPacket.create_nak_packet( 580e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart query_packet.transaction_id, query_packet.client_hw_address) 581e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart else: 582e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart response_packet = super(DhcpHandlingRule_RejectAndRespondToRequest, 583e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart self).respond(query_packet) 584e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart self._response_counter += 1 585e1d4fcb22f2f3f505a18e5078a73045872fb03ffPaul Stewart return response_packet 586f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart 587f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart 588f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewartclass DhcpHandlingRule_AcceptDecline(DhcpHandlingRule): 589f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart """ 590f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart This handler accepts any DECLINE packet that contains an option for 591f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart SERVER_ID matches |expected_server_ip|. There is no response to this 592f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart packet. 593f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart """ 594f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart def __init__(self, 595f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart expected_server_ip, 596f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart additional_options, 597f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart custom_fields): 598f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart """ 599f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart All *_ip arguments are IPv4 address strings like "192.168.1.101". 600f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart 601f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart |additional_options| is handled as explained by DhcpHandlingRule. 602f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart """ 603f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart super(DhcpHandlingRule_AcceptDecline, self).__init__( 604f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart dhcp_packet.MESSAGE_TYPE_DECLINE, additional_options, 605f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart custom_fields) 606f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart self._expected_server_ip = expected_server_ip 607f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart 608f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart def handle_impl(self, query_packet): 609f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart if not self.is_our_message_type(query_packet): 610f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart return RESPONSE_NO_ACTION 611f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart 612f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart self.logger.info("Received DECLINE packet, checking fields...") 613f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart server_ip = query_packet.get_option(dhcp_packet.OPTION_SERVER_ID) 614f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart if server_ip is None: 615f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart self.logger.info("DECLINE packet did not have the expected " 616f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart "options, discarding.") 617f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart return RESPONSE_NO_ACTION 618f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart 619f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart if server_ip != self._expected_server_ip: 620f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart self.emit_warning("DECLINE packet's server ip did not match our " 621f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart "expectations; expected %s but got %s" % 622f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart (self._expected_server_ip, server_ip)) 623f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart return RESPONSE_NO_ACTION 624f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart 625f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart self.logger.info("Received valid DECLINE packet, processing") 626f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart ret = RESPONSE_POP_HANDLER 627f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart if self.is_final_handler: 628f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart ret |= RESPONSE_TEST_SUCCEEDED 629f7efffe4912a75b932e8ff78b90bf1f5b697b4eaPaul Stewart return ret 630