Exemple #1
0
    def reconnect_service(self):
        """
        Disconnect and reconnect Ethernet.

        Ask shill to disconnect and reconnect the Service for our
        virtual Ethernet link. This causes shill to shut down and
        restart dhcpcd for the link.
        """
        service = self.find_ethernet_service(self.interface_name)
        service.Disconnect()
        rules = [
            # Respond to DISCOVERY, but then NAK the REQUEST.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RejectRequest(),

            # Allow a successful negotiation the second time around.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
        ]
        rules[-1].is_final_handler = True
        self.server.start_test(rules,
                               dhcp_test_base.DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        service.Connect()
    def test_body(self):
        """Main body of the test."""
        subnet_mask = self.ethernet_pair.interface_subnet_mask
        intended_ip = dhcp_test_base.DhcpTestBase.rewrite_ip_suffix(
            subnet_mask, self.server_ip, INTENDED_IP_SUFFIX)
        # Two real name servers, and a bogus one to be unpredictable.
        dns_servers = ["8.8.8.8", "8.8.4.4", "192.168.87.88"]
        proxy_auto_config = "http://server:8080/wpad.dat"
        # This is the pool of information the server will give out to the client
        # upon request.
        dhcp_options = {
            dhcp_packet.OPTION_SERVER_ID: self.server_ip,
            dhcp_packet.OPTION_SUBNET_MASK: subnet_mask,
            dhcp_packet.OPTION_IP_LEASE_TIME: LEASE_TIME_SECONDS,
            dhcp_packet.OPTION_REQUESTED_IP: intended_ip,
            dhcp_packet.OPTION_DNS_SERVERS: dns_servers,
            dhcp_packet.OPTION_WEB_PROXY_AUTO_DISCOVERY: proxy_auto_config
        }
        rules = [
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                intended_ip, self.server_ip, dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                intended_ip, self.server_ip, dhcp_options, {})
        ]
        rules[-1].is_final_handler = True
        self.server.start_test(rules, DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        self.server.wait_for_test_to_finish()
        if not self.server.last_test_passed:
            raise error.TestFail("Test server didn't get all the messages it "
                                 "was told to expect during negotiation.")

        self.wait_for_dhcp_propagation()
        self.check_wpad_config(proxy_auto_config)
Exemple #3
0
    def send_nak_then_ack_with_conflict(self):
        """
        Send an NAK followed by an ACK on re-connect to with address conflict.

        Ask shill to disconnect and reconnect the Service for our
        virtual Ethernet link. This causes shill to shut down and
        restart dhcpcd for the link.

        On reconnect, perform a test where the server responds to a
        REQUEST with a NAK followed by an ACK, however with a lease
        for an invalid address (the same IP address as the DHCP server).

        Ensure that the client rejects the invalid lease with a DECLINE,
        and that it also ignores the first OFFER for the same invalid
        address.
        """
        service = self.find_ethernet_service(self.interface_name)
        service.Disconnect()
        rules = [
            # Respond to DISCOVERY, but then both NAK then ACK the REQUEST,
            # supplying the server's own IP address.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.server_ip, self.server_ip, self.dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RejectAndRespondToRequest(
                self.server_ip, self.server_ip, self.dhcp_options, {}, True),

            # The client should eventually reject this lease since this
            # address is in use.
            dhcp_handling_rule.DhcpHandlingRule_AcceptDecline(
                self.server_ip, self.dhcp_options, {}),

            # Offer up the same (invalid) IP address.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.server_ip, self.server_ip, self.dhcp_options, {}),

            # The client should ignore the previous offer and perform
            # another DISCOVER request.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
        ]
        rules[-1].is_final_handler = True
        self.server.start_test(rules,
                               dhcp_test_base.DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        service.Connect()
Exemple #4
0
    def send_nak_then_ack_then_verify(self):
        """
        Send an NAK followed by an ACK then verify client IP address.

        Ask shill to disconnect and reconnect the Service for our
        virtual Ethernet link. This causes shill to shut down and
        restart dhcpcd for the link.

        On reconnect, perform a test where the server responds to a
        REQUEST with a NAK followed by an ACK.  This method asserts
        that the client does not DECLINE this address.
        """
        service = self.find_ethernet_service(self.interface_name)
        service.Disconnect()

        # This rule serves two purposes: First it asserts that the client
        # does not send a DECLINE response.  Second, it waits until the
        # test timeout, by which time client will have completed an "ARP
        # self" operation to validate the offered IP adddres.
        decline_rule = dhcp_handling_rule.DhcpHandlingRule_AcceptDecline(
            self.intended_ip, self.dhcp_options, {})

        rules = [
            # Respond to DISCOVERY, but then both NAK then ACK the REQUEST,
            # supplying the server's own IP address.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RejectAndRespondToRequest(
                self.intended_ip, self.server_ip, self.dhcp_options, {}, True),
            decline_rule
        ]
        rules[-1].is_final_handler = True
        self.server.start_test(rules,
                               dhcp_test_base.DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        service.Connect()
        self.server.wait_for_test_to_finish()

        # This is a negative test, since we expect the last rule to fail.
        if self.server.last_test_passed:
            raise error.TestFail('DHCP DECLINE message was received')
        elif self.server.current_rule != decline_rule:
            raise error.TestFail('Failed on %s rule' %
                                 self.server.current_rule)

        dhcp_config = self.get_interface_ipconfig(
            self.ethernet_pair.peer_interface_name)
        if dhcp_config is None:
            raise error.TestFail('Did not get a DHCP config')
        if dhcp_config[dhcp_test_base.DHCPCD_KEY_ADDRESS] != self.intended_ip:
            raise error.TestFail('Client did not attain expected address %s' %
                                 self.intended_ip)
    def negotiate_and_check_lease(self,
                                  dhcp_options,
                                  custom_fields={},
                                  disable_check=False):
        """
        Perform DHCP lease negotiation, and ensure that the resulting
        ipconfig matches the DHCP options provided to the server.

        @param dhcp_options dict of properties the DHCP server should provide.
        @param custom_fields dict of custom DHCP parameters to add to server.
        @param disable_check bool whether to perform IPConfig parameter
             checking.

        """
        if dhcp_packet.OPTION_REQUESTED_IP not in dhcp_options:
            raise error.TestFail('You must specify OPTION_REQUESTED_IP to '
                                 'negotiate a DHCP lease')
        intended_ip = dhcp_options[dhcp_packet.OPTION_REQUESTED_IP]
        # Build up the handling rules for the server and start the test.
        rules = []
        rules.append(dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                intended_ip,
                self.server_ip,
                dhcp_options,
                custom_fields))
        rules.append(dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                intended_ip,
                self.server_ip,
                dhcp_options,
                custom_fields))
        rules[-1].is_final_handler = True
        self.server.start_test(rules, DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        logging.info('Server is negotiating new lease with options: %s',
                     dhcp_options)
        self.server.wait_for_test_to_finish()
        if not self.server.last_test_passed:
            raise error.TestFail(
                'Test failed: active rule is %s' % self.server.current_rule)

        if disable_check:
            logging.info('Skipping check of negotiated DHCP lease parameters.')
        else:
            self.wait_for_dhcp_propagation()
            self.check_dhcp_config(dhcp_options)
    def test_body(self):
        """Main body of the test."""
        subnet_mask = self.ethernet_pair.interface_subnet_mask
        intended_ip = dhcp_test_base.DhcpTestBase.rewrite_ip_suffix(
                subnet_mask,
                self.server_ip,
                INTENDED_IP_SUFFIX)
        # Two real name servers, and a bogus one to be unpredictable.
        dns_servers = ['8.8.8.8', '8.8.4.4', '192.168.87.88']
        vendor_options = 'ANDROID_METERED'
        # This is the pool of information the server will give out to the client
        # upon request.
        dhcp_options = {
                dhcp_packet.OPTION_SERVER_ID : self.server_ip,
                dhcp_packet.OPTION_SUBNET_MASK : subnet_mask,
                dhcp_packet.OPTION_IP_LEASE_TIME : LEASE_TIME_SECONDS,
                dhcp_packet.OPTION_REQUESTED_IP : intended_ip,
                dhcp_packet.OPTION_DNS_SERVERS : dns_servers,
                dhcp_packet.OPTION_VENDOR_ENCAPSULATED_OPTIONS : vendor_options
                }
        rules = [
                dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                        intended_ip, self.server_ip, dhcp_options, {}),
                dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                        intended_ip, self.server_ip, dhcp_options, {})
                ]
        rules[-1].is_final_handler = True

        # In some DHCP server implementations, the vendor encapsulated option
        # is provided in the DHCP response without the client requesting it.
        for rule in rules:
            rule.force_reply_options = [
                    dhcp_packet.OPTION_VENDOR_ENCAPSULATED_OPTIONS ]

        self.server.start_test(rules, DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        self.server.wait_for_test_to_finish()
        if not self.server.last_test_passed:
            raise error.TestFail('Test server didn\'t get all the messages it '
                                 'was told to expect during negotiation.')

        self.wait_for_dhcp_propagation()
        self.check_vendor_encapsulated_options(vendor_options)
Exemple #7
0
    def test_body(self):
        """Main body of the test."""
        subnet_mask = self.ethernet_pair.interface_subnet_mask
        intended_ip = dhcp_test_base.DhcpTestBase.rewrite_ip_suffix(
            subnet_mask, self.server_ip, INTENDED_IP_SUFFIX)
        # It doesn't matter what is contained in this option value, except that
        # the DHCP client does not crash decoding it or passing its
        # interpretation of it back to shill.
        fqdn_option = '\x03\xff\x00'
        # This is the pool of information the server will give out to the client
        # upon request.
        dhcp_options = {
            dhcp_packet.OPTION_SERVER_ID: self.server_ip,
            dhcp_packet.OPTION_SUBNET_MASK: subnet_mask,
            dhcp_packet.OPTION_IP_LEASE_TIME: LEASE_TIME_SECONDS,
            dhcp_packet.OPTION_REQUESTED_IP: intended_ip,
            dhcp_packet.OPTION_FULLY_QUALIFIED_DOMAIN_NAME: fqdn_option
        }
        rules = [
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                intended_ip, self.server_ip, dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                intended_ip, self.server_ip, dhcp_options, {})
        ]
        rules[-1].is_final_handler = True

        # In some DHCP server implementations, the FQDN option is provided in
        # the DHCP ACK response without the client requesting it.
        rules[-1].force_reply_options = [
            dhcp_packet.OPTION_FULLY_QUALIFIED_DOMAIN_NAME
        ]

        self.server.start_test(rules, DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        self.server.wait_for_test_to_finish()
        if not self.server.last_test_passed:
            raise error.TestFail('Test server didn\'t get all the messages it '
                                 'was told to expect during negotiation.')

        # This test passes if the DHCP client lives long enough to send
        # the network configuration to shill.
        self.wait_for_dhcp_propagation()
        self.check_dhcp_config(dhcp_options)
Exemple #8
0
    def force_dhcp_renew(self):
        """
        Force a DHCP renewal.

        Ask shill to Refresh the configuration for the IPConfig object
        associated with our virtual Ethernet link. This causes shill
        to ask dhcpcd to renew its DHCP lease.
        """
        rules = [
            # Reject REQUEST from renewal attempt.
            dhcp_handling_rule.DhcpHandlingRule_RejectRequest(),

            # Allow a successful negotiation after that.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
        ]
        rules[-1].is_final_handler = True
        self.server.start_test(rules,
                               dhcp_test_base.DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        self.get_interface_ipconfig_objects(self.interface_name)[0].Refresh()
    def test_body(self):
        """Main body of the test."""
        subnet_mask = self.ethernet_pair.interface_subnet_mask
        intended_ip = dhcp_test_base.DhcpTestBase.rewrite_ip_suffix(
            subnet_mask, self.server_ip, INTENDED_IP_SUFFIX)
        # Pick an address that's unlikely to be in the broadcast domain of the
        # virtual network pair.
        gateway_ip = "10.11.12.13"
        # Two real name servers, and a bogus one to be unpredictable.
        dns_servers = ['8.8.8.8', '8.8.4.4', '192.168.87.88']
        vendor_options = 'ANDROID_METERED'
        # This is the pool of information the server will give out to the client
        # upon request.
        dhcp_options = {
            dhcp_packet.OPTION_SERVER_ID: self.server_ip,
            dhcp_packet.OPTION_SUBNET_MASK: subnet_mask,
            dhcp_packet.OPTION_IP_LEASE_TIME: LEASE_TIME_SECONDS,
            dhcp_packet.OPTION_REQUESTED_IP: intended_ip,
            dhcp_packet.OPTION_DNS_SERVERS: dns_servers,
            dhcp_packet.OPTION_ROUTERS: [gateway_ip],
        }
        rules = [
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                intended_ip, self.server_ip, dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                intended_ip, self.server_ip, dhcp_options, {})
        ]
        rules[-1].is_final_handler = True

        self.server.start_test(rules, DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        self.server.wait_for_test_to_finish()
        if not self.server.last_test_passed:
            raise error.TestFail('Test server didn\'t get all the messages it '
                                 'was told to expect during negotiation.')

        self.wait_for_dhcp_propagation()
        interface_name = self.ethernet_pair.peer_interface_name
        self.check_shill_gateway_setup(interface_name, gateway_ip)
        self.check_routing_table_gateway_setup(interface_name, gateway_ip)
Exemple #10
0
    def send_ack_then_nak(self):
        """
        Send an ACK followed by a NAK on re-connect to Ethernet.

        Ask shill to disconnect and reconnect the Service for our
        virtual Ethernet link. This causes shill to shut down and
        restart dhcpcd for the link.  Then perform a test where
        the server responds to a REQUEST with an ACK followed by
        an ACK.
        """
        service = self.find_ethernet_service(self.interface_name)
        service.Disconnect()
        rules = [
            # Respond to DISCOVERY, but then both ACK and NAK the REQUEST.
            dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                self.intended_ip, self.server_ip, self.dhcp_options, {}),
            dhcp_handling_rule.DhcpHandlingRule_RejectAndRespondToRequest(
                self.intended_ip, self.server_ip, self.dhcp_options, {},
                False),
        ]
        rules[-1].is_final_handler = True
        self.server.start_test(rules,
                               dhcp_test_base.DHCP_NEGOTIATION_TIMEOUT_SECONDS)
        service.Connect()
Exemple #11
0
    def make_lease_negotiation_rules(self, options):
        """Generate a set of lease negotiation handling rules for a
        server that will successfully return an IP address to the client.

        @param options dict of options to be negotiated.  In particular,
            the dhcp_packet.OPTION_REQUESTED_IP element is used to configure
            the address that will be returned to the client.
        @return array of DhcpHandlingRule instances which implement the
            negotiation.

        """
        intended_ip = options[dhcp_packet.OPTION_REQUESTED_IP]
        rules = []
        rules.append(dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                intended_ip,
                self.server_ip,
                options,
                {}))
        rules.append(dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
                intended_ip,
                self.server_ip,
                options,
                {}))
        return rules
Exemple #12
0
def test_simple_server_exchange(server):
    intended_ip = "127.0.0.42"
    subnet_mask = "255.255.255.0"
    server_ip = "127.0.0.1"
    lease_time_seconds = 60
    test_timeout = 3.0
    mac_addr = "\x01\x02\x03\x04\x05\x06"
    # Build up our packets and have them request some default option values,
    # like the IP we're being assigned and the address of the server assigning
    # it.
    discovery_message = dhcp_packet.DhcpPacket.create_discovery_packet(
        mac_addr)
    discovery_message.set_option(
        dhcp_packet.OPTION_PARAMETER_REQUEST_LIST,
        dhcp_packet.OPTION_VALUE_PARAMETER_REQUEST_LIST_DEFAULT)
    request_message = dhcp_packet.DhcpPacket.create_request_packet(
        discovery_message.transaction_id, mac_addr)
    request_message.set_option(
        dhcp_packet.OPTION_PARAMETER_REQUEST_LIST,
        dhcp_packet.OPTION_VALUE_PARAMETER_REQUEST_LIST_DEFAULT)
    # This is the pool of settings the DHCP server will seem to draw from to
    # answer queries from the client.  This information is written into packets
    # through the handling rules.
    dhcp_server_config = {
        dhcp_packet.OPTION_SERVER_ID: server_ip,
        dhcp_packet.OPTION_SUBNET_MASK: subnet_mask,
        dhcp_packet.OPTION_IP_LEASE_TIME: lease_time_seconds,
        dhcp_packet.OPTION_REQUESTED_IP: intended_ip,
    }
    # Build up the handling rules for the server and start the test.
    rules = []
    rules.append(
        dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
            intended_ip, server_ip, dhcp_server_config))
    rules.append(
        dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
            intended_ip, server_ip, dhcp_server_config))
    rules[-1].is_final_handler = True
    server.start_test(rules, test_timeout)
    # Because we don't want to require root permissions to run these tests,
    # listen on the loopback device, don't broadcast, and don't use reserved
    # ports (like the actual DHCP ports).  Use 8068/8067 instead.
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client_socket.bind(("127.0.0.1", 8068))
    client_socket.settimeout(0.1)
    client_socket.sendto(discovery_message.to_binary_string(),
                         (server_ip, 8067))

    offer_packet = receive_packet(client_socket)
    if offer_packet is None:
        return False

    if (offer_packet.message_type != dhcp_packet.MESSAGE_TYPE_OFFER):
        print "Type of DHCP response is not offer."
        return False

    if offer_packet.get_field(dhcp_packet.FIELD_YOUR_IP) != intended_ip:
        print "Server didn't offer the IP we expected."
        return False

    print "Offer looks good to the client, sending request."
    # In real tests, dhcpcd formats all the DISCOVERY and REQUEST messages.  In
    # our unit test, we have to do this ourselves.
    request_message.set_option(
        dhcp_packet.OPTION_SERVER_ID,
        offer_packet.get_option(dhcp_packet.OPTION_SERVER_ID))
    request_message.set_option(
        dhcp_packet.OPTION_SUBNET_MASK,
        offer_packet.get_option(dhcp_packet.OPTION_SUBNET_MASK))
    request_message.set_option(
        dhcp_packet.OPTION_IP_LEASE_TIME,
        offer_packet.get_option(dhcp_packet.OPTION_IP_LEASE_TIME))
    request_message.set_option(
        dhcp_packet.OPTION_REQUESTED_IP,
        offer_packet.get_option(dhcp_packet.OPTION_REQUESTED_IP))
    # Send the REQUEST message.
    client_socket.sendto(request_message.to_binary_string(), (server_ip, 8067))
    ack_packet = receive_packet(client_socket)
    if ack_packet is None:
        return False

    if (ack_packet.message_type != dhcp_packet.MESSAGE_TYPE_ACK):
        print "Type of DHCP response is not acknowledgement."
        return False

    if offer_packet.get_field(dhcp_packet.FIELD_YOUR_IP) != intended_ip:
        print "Server didn't give us the IP we expected."
        return False

    print "Waiting for the server to finish."
    server.wait_for_test_to_finish()
    print "Server agrees that the test is over."
    if not server.last_test_passed:
        print "Server is unhappy with the test result."
        return False

    print "test_simple_server_exchange PASSED."
    return True
    def test_body(self):
        subnet_mask = self.ethernet_pair.interface_subnet_mask
        intended_ip = dhcp_test_base.DhcpTestBase.rewrite_ip_suffix(
            subnet_mask, self.server_ip, INTENDED_IP_SUFFIX)
        # Two real name servers, and a bogus one to be unpredictable.
        dns_servers = ["8.8.8.8", "8.8.4.4", "192.168.87.88"]
        domain_name = "corp.google.com"
        dns_search_list = [
            "corgie.google.com",
            "lies.google.com",
            "that.is.a.tasty.burger.google.com",
        ]
        # This is the pool of information the server will give out to the client
        # upon request.
        dhcp_options = {
            dhcp_packet.OPTION_SERVER_ID: self.server_ip,
            dhcp_packet.OPTION_SUBNET_MASK: subnet_mask,
            dhcp_packet.OPTION_IP_LEASE_TIME: LEASE_TIME_SECONDS,
            dhcp_packet.OPTION_REQUESTED_IP: intended_ip,
            dhcp_packet.OPTION_DNS_SERVERS: dns_servers,
            dhcp_packet.OPTION_DOMAIN_NAME: domain_name,
            dhcp_packet.OPTION_DNS_DOMAIN_SEARCH_LIST: dns_search_list,
            dhcp_packet.OPTION_RENEWAL_T1_TIME_VALUE: LEASE_T1_TIME,
            dhcp_packet.OPTION_REBINDING_T2_TIME_VALUE: LEASE_T2_TIME,
        }
        self.negotiate_and_check_lease(dhcp_options)
        # This is very imprecise, since there is some built in delay in
        # negotiate_new_lease() for settings propagations, but we're not
        # interested in microsecond timings anyway.
        lease_start_time = time.time()
        t1_deadline = lease_start_time + LEASE_T1_TIME
        t2_deadline = lease_start_time + LEASE_T2_TIME
        # Ignore the T1 deadline packet.
        t1_handler = dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
            intended_ip,
            self.server_ip,
            dhcp_options, {},
            should_respond=False)
        t1_handler.target_time_seconds = t1_deadline
        t1_handler.allowable_time_delta_seconds = RENEWAL_TIME_DELTA_SECONDS
        t2_handler = dhcp_handling_rule.DhcpHandlingRule_RespondToPostT2Request(
            intended_ip,
            self.server_ip,
            dhcp_options, {},
            should_respond=False)
        t2_handler.target_time_seconds = t2_deadline
        t2_handler.allowable_time_delta_seconds = RENEWAL_TIME_DELTA_SECONDS
        discovery_handler = \
                dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
                        intended_ip,
                        self.server_ip,
                        dhcp_options,
                        {},
                        should_respond=False)
        rules = [t1_handler, t2_handler, discovery_handler]
        rules[-1].is_final_handler = True
        self.server.start_test(rules, DHCP_RENEWAL_TIMEOUT_SECONDS)
        self.server.wait_for_test_to_finish()
        if not self.server.last_test_passed:
            raise error.TestFail("Test server didn't get all the messages it "
                                 "was told to expect for renewal.")

        # The service should leave the connected state after shill attempts
        # one last DHCP attempt from scratch.  We may miss the transition to the
        # "idle" state since the system immediately attempts to re-connect, so
        # we also test for the "configuration" state.
        service = self.find_ethernet_service(
            self.ethernet_pair.peer_interface_name)
        (successful, state, duration) = self.shill_proxy.wait_for_property_in(
            service, self.shill_proxy.SERVICE_PROPERTY_STATE,
            ('failure', 'idle', 'configuration'), DHCP_ATTEMPT_TIMEOUT_SECONDS)
        if not successful:
            raise error.TestFail(
                'Service failed to go idle in %ds (state %s)' %
                (duration, state))
        logging.info('In state "%s" after %d seconds', state, duration)