]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
- Pull up recent 2.0 changes.
authorTed Lemon <source@isc.org>
Sun, 14 Feb 1999 19:27:56 +0000 (19:27 +0000)
committerTed Lemon <source@isc.org>
Sun, 14 Feb 1999 19:27:56 +0000 (19:27 +0000)
- Fix some problems in DHCPREQUEST code regarding client identity.
- Update references to lease's shared network to use subnet pointer.
- If there's more than one lease matching a particular hardware address,
  examine them all for possible applicability in find_lease().
- Only recover abandoned leases on DHCPREQUEST if the request was for
  the specific abandoned lease.   Push all other abandoned lease
  recovery code out into DHCPDISCOVER/allocate_lease processing.

server/dhcp.c

index 1370f5c64c38ec892351c1775b26cc563d878a61..179df0d04a9775f175d42178c3b4c973134daa9e 100644 (file)
@@ -3,8 +3,8 @@
    DHCP Protocol engine. */
 
 /*
- * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
- * All rights reserved.
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium.   All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: dhcp.c,v 1.73 1998/11/19 20:56:36 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: dhcp.c,v 1.74 1999/02/14 19:27:56 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -221,7 +221,7 @@ void dhcprequest (packet)
                   where it claims to have come from, it didn't come
                   from there.   Fry it. */
                if (!packet -> shared_network) {
-                       if (subnet) {
+                       if (subnet && subnet -> group -> authoritative) {
                                note ("%s: wrong network.", msgbuf);
                                nak_lease (packet, &cip);
                                return;
@@ -234,8 +234,11 @@ void dhcprequest (packet)
                   address that is not on that shared network, nak it. */
                subnet = find_grouped_subnet (packet -> shared_network, cip);
                if (!subnet) {
-                       note ("%s: wrong network.", msgbuf);
-                       nak_lease (packet, &cip);
+                       if (packet -> shared_network -> group -> authoritative)
+                       {
+                               note ("%s: wrong network.", msgbuf);
+                               nak_lease (packet, &cip);
+                       }
                        return;
                }
        }
@@ -246,12 +249,12 @@ void dhcprequest (packet)
        if (lease && !addr_eq (lease -> ip_addr, cip)) {
                /* If we found the address the client asked for, but
                    it wasn't what got picked, the lease belongs to us,
-                   so we can tenuously justify NAKing it. */
+                   so we should NAK it. */
                if (ours) {
                        note ("%s: wrong lease %s.", msgbuf, piaddr (cip));
                        nak_lease (packet, &cip);
                } else
-                       note ("%s: unknown lease %s", msgbuf, piaddr (cip));
+                       note ("%s: wrong lease %s", msgbuf, piaddr (cip));
                return;
        }
 
@@ -263,30 +266,28 @@ void dhcprequest (packet)
                return;
        }
 
-       /* If we own the lease that the client is asking for,
-          and it's already been assigned to the client, ack it. */
-       oc = lookup_option (packet -> options.dhcp_hash,
-                           DHO_DHCP_CLIENT_IDENTIFIER);
-       if (oc)
-               status = evaluate_option_cache (&data, packet,
-                                               &packet -> options, oc);
-       else
-               status = 0;
-       if (status && lease &&
-           ((lease -> uid_len && lease -> uid_len == data.len &&
-             !memcmp (data.data,
-                      lease -> uid, lease -> uid_len)) ||
-            (lease -> hardware_addr.hlen == packet -> raw -> hlen &&
-             lease -> hardware_addr.htype == packet -> raw -> htype &&
-             !memcmp (lease -> hardware_addr.haddr,
-                      packet -> raw -> chaddr,
-                      packet -> raw -> hlen)))) {
-               data_string_forget (&data, "dhcprequest");
-               ack_lease (packet, lease, DHCPACK, 0, msgbuf);
+       /* If we found a lease, but the client identifier on the lease
+          exists and is different than the id the client sent, then
+          we can't send this lease to the client. */
+       if (lease) {
+               oc = lookup_option (packet -> options.dhcp_hash,
+                                   DHO_DHCP_CLIENT_IDENTIFIER);
+               if (oc)
+                       status = evaluate_option_cache (&data, packet,
+                                                       &packet -> options, oc);
+               if (lease && oc && status && lease -> uid_len &&
+                   (lease -> uid_len != data.len ||
+                    memcmp (data.data,
+                               lease -> uid, lease -> uid_len))) {
+                       note ("%s: wrong owner %s.", msgbuf, piaddr (cip));
+                       nak_lease (packet, &cip);
+               } else
+                       ack_lease (packet, lease, DHCPACK, 0, msgbuf);
+               if (oc && status)
+                       data_string_forget (&data, "dhcprequest");
                return;
        }
        note ("%s: unknown lease %s.", msgbuf, piaddr (cip));
-       data_string_forget (&data, "dhcprequest");
 }
 
 void dhcprelease (packet)
@@ -502,14 +503,15 @@ void nak_lease (packet, cip)
                to.sin_addr = raw.giaddr;
                to.sin_port = local_port;
 
-#ifdef USE_FALLBACK
-               result = send_fallback (&fallback_interface,
-                                       packet, &raw, outgoing.packet_length,
-                                       from, &to, &hto);
-               if (result < 0)
-                       warn ("send_fallback: %m");
-               return;
-#endif
+               if (fallback_interface) {
+                       result = send_packet (fallback_interface,
+                                             packet, &raw,
+                                             outgoing.packet_length,
+                                             from, &to, &hto);
+                       if (result < 0)
+                               warn ("send_fallback: %m");
+                       return;
+               }
        } else {
                to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
                to.sin_port = remote_port;
@@ -539,6 +541,7 @@ void ack_lease (packet, lease, offer, when, msg)
        struct option_cache *oc;
        struct expression *expr;
        int status;
+       int ulafdr;
        int option_tag_size = 1; /* XXX */
 
        int i, j, s1, s2;
@@ -572,7 +575,7 @@ void ack_lease (packet, lease, offer, when, msg)
                lease -> client_hostname =
                        dmalloc (d1.len + 1, "ack_lease");
                if (!lease -> client_hostname)
-                       warn ("no memory for client hostname.\n");
+                       warn ("no memory for client hostname.");
                else {
                        memcpy (lease -> client_hostname, d1.data, d1.len);
                        lease -> client_hostname [d1.len] = 0;
@@ -887,7 +890,6 @@ void ack_lease (packet, lease, offer, when, msg)
 
        lt.host = lease -> host;
        lt.subnet = lease -> subnet;
-       lt.shared_network = lease -> shared_network;
        lt.billing_class = lease -> billing_class;
 
        /* Don't call supersede_lease on a mocked-up lease. */
@@ -1154,16 +1156,24 @@ void ack_lease (packet, lease, offer, when, msg)
             lookup_option (state -> options.server_hash,
                            SV_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE))) {
                i = DHO_ROUTERS;
-               oc = (struct option_cache *)0;
-               if (option_cache_allocate (&oc, "ack_lease")) {
-                       if (make_const_data (&oc -> expression,
-                                            lease -> ip_addr.iabuf,
-                                            lease -> ip_addr.len, 0, 0)) {
-                               oc -> option = dhcp_universe.options [i];
-                               save_option (state -> options.dhcp_hash, oc);
+               oc = lookup_option (state -> options.dhcp_hash, i);
+               if (!oc) {
+                       oc = (struct option_cache *)0;
+                       if (option_cache_allocate (&oc, "ack_lease")) {
+                               if (make_const_data (&oc -> expression,
+                                                    lease -> ip_addr.iabuf,
+                                                    lease -> ip_addr.len,
+                                                    0, 0)) {
+                                       oc -> option =
+                                               dhcp_universe.options [i];
+                                       save_option
+                                               (state -> options.dhcp_hash,
+                                                oc);
+                               }
                        }
-                       option_cache_dereference (&oc, "ack_lease");
                }
+               if (oc)
+                       option_cache_dereference (&oc, "ack_lease");
        }
 
 #ifdef DEBUG_PACKET
@@ -1177,11 +1187,14 @@ void ack_lease (packet, lease, offer, when, msg)
 
        /* If this is a DHCPOFFER, ping the lease address before actually
           sending the offer. */
-       if (offer == DHCPOFFER && !(lease -> flags & STATIC_LEASE)) {
+       if (offer == DHCPOFFER && !(lease -> flags & STATIC_LEASE) &&
+           cur_time - lease -> timestamp > 60) {
+               lease -> timestamp = cur_time;
                icmp_echorequest (&lease -> ip_addr);
                add_timeout (cur_time + 1, lease_ping_timeout, lease);
                ++outstanding_pings;
        } else {
+               lease -> timestamp = cur_time;
                dhcp_reply (lease);
        }
 }
@@ -1331,38 +1344,39 @@ void dhcp_reply (lease)
                to.sin_addr = raw.giaddr;
                to.sin_port = local_port;
 
-#ifdef USE_FALLBACK
-               result = send_fallback (&fallback_interface,
-                                       (struct packet *)0,
-                                       &raw, packet_length,
-                                       raw.siaddr, &to, &hto);
-               if (result < 0)
-                       warn ("send_fallback: %m");
+               if (fallback_interface) {
+                       result = send_packet (fallback_interface,
+                                             (struct packet *)0,
+                                             &raw, packet_length,
+                                             raw.siaddr, &to, &hto);
+                       if (result < 0)
+                               warn ("send_fallback: %m");
 
-               free_lease_state (state, "dhcp_reply fallback 1");
-               lease -> state = (struct lease_state *)0;
-               return;
-#endif
+                       free_lease_state (state, "dhcp_reply fallback 1");
+                       lease -> state = (struct lease_state *)0;
+                       return;
+               }
 
-       /* If it comes from a client who already knows its address and
+       /* If it comes from a client that already knows its address and
           is not requesting a broadcast response, sent it directly to
           that client. */
        } else if (raw.ciaddr.s_addr && state -> offer == DHCPACK &&
-                  !(raw.flags & htons (BOOTP_BROADCAST))) {
+                  !(raw.flags & htons (BOOTP_BROADCAST)) &&
+                  can_unicast_without_arp ()) {
                to.sin_addr = state -> ciaddr;
                to.sin_port = remote_port; /* XXX */
 
-#ifdef USE_FALLBACK
-               result = send_fallback (&fallback_interface,
-                                       (struct packet *)0,
-                                       &raw, packet_length,
-                                       raw.siaddr, &to, &hto);
-               if (result < 0)
-                       warn ("send_fallback: %m");
-               free_lease_state (state, "dhcp_reply fallback 1");
-               lease -> state = (struct lease_state *)0;
-               return;
-#endif
+               if (fallback_interface) {
+                       result = send_packet (fallback_interface,
+                                             (struct packet *)0,
+                                             &raw, packet_length,
+                                             raw.siaddr, &to, &hto);
+                       if (result < 0)
+                               warn ("send_fallback: %m");
+                       free_lease_state (state, "dhcp_reply fallback 1");
+                       lease -> state = (struct lease_state *)0;
+                       return;
+               }
 
        /* Otherwise, broadcast it on the local network. */
        } else {
@@ -1435,12 +1449,14 @@ struct lease *find_lease (packet, share, ours)
                        /* Find the lease matching this uid that's on the
                           network the packet came from (if any). */
                        for (; uid_lease; uid_lease = uid_lease -> n_uid)
-                               if (uid_lease -> shared_network == share)
+                               if (uid_lease -> subnet -> shared_network ==
+                                   share)
                                        break;
                        fixed_lease = (struct lease *)0;
 #if defined (DEBUG_FIND_LEASE)
-                       note ("Found lease for client identifier: %s.",
-                             piaddr (uid_lease -> ip_addr));
+                       if (uid_lease)
+                               note ("Found lease for client identifier: %s.",
+                                     piaddr (uid_lease -> ip_addr));
 #endif
                        if (uid_lease &&
                            (uid_lease -> flags & ABANDONED_LEASE)) {
@@ -1482,7 +1498,7 @@ struct lease *find_lease (packet, share, ours)
        /* Find the lease that's on the network the packet came from
           (if any). */
        for (; hw_lease; hw_lease = hw_lease -> n_hw) {
-               if (hw_lease -> shared_network == share) {
+               if (hw_lease -> subnet -> shared_network == share) {
                        if (hw_lease -> flags & ABANDONED_LEASE)
                                continue;
                        /* If we're allowed to use this lease, do so. */
@@ -1542,7 +1558,7 @@ struct lease *find_lease (packet, share, ours)
           that thought it had this lease answered an ARP or PING, causing the
           lease to be abandoned.   If so, this request probably came from
           that client. */
-       if (ip_lease && (ip_lease -> shared_network != share)) {
+       if (ip_lease && (ip_lease -> subnet -> shared_network != share)) {
                note ("...but it was on the wrong shared network.");
                ip_lease = (struct lease *)0;
        }
@@ -1576,7 +1592,8 @@ struct lease *find_lease (packet, share, ours)
                                      print_hw_addr (packet -> raw -> htype,
                                                     packet -> raw -> hlen,
                                                     packet -> raw -> chaddr),
-                                     ip_lease -> shared_network -> name);
+                                     (ip_lease -> subnet ->
+                                      shared_network -> name));
 
                        /* If the client is REQUESTing the lease, it shouldn't
                           still be using the old one, so we can free it for
@@ -1584,7 +1601,7 @@ struct lease *find_lease (packet, share, ours)
                           lease is on the same network, of course. */
 
                        if (packet -> packet_type == DHCPREQUEST &&
-                           share == uid_lease -> shared_network)
+                           share == uid_lease -> subnet -> shared_network)
                                dissociate_lease (uid_lease);
 
                        uid_lease = ip_lease;
@@ -1596,16 +1613,20 @@ struct lease *find_lease (packet, share, ours)
           match, except that if the hardware address matches and the
           client is now doing dynamic BOOTP (and thus hasn't provided
           a uid) we let the client get away with it. */
-       if (hw_lease &&
-           hw_lease -> ends >= cur_time &&
-           hw_lease -> uid &&
-           (!have_client_identifier ||
-            hw_lease -> uid_len != client_identifier.len ||
-            memcmp (hw_lease -> uid, client_identifier.data,
-                    hw_lease -> uid_len))) {
-               hw_lease = (struct lease *)0;
+       while (hw_lease &&
+              hw_lease -> ends >= cur_time &&
+              hw_lease -> uid &&
+              (!have_client_identifier ||
+               hw_lease -> uid_len != client_identifier.len ||
+               memcmp (hw_lease -> uid, client_identifier.data,
+                       hw_lease -> uid_len))) {
+               hw_lease = hw_lease -> n_hw;
 #if defined (DEBUG_FIND_LEASE)
-               note ("rejecting lease for hardware address.");
+               if (hw_lease)
+                       note ("trying next lease matching hw addr: %s",
+                             piaddr (hw_lease -> ip_addr));
+               else
+                       note ("rejecting lease for hardware address.");
 #endif
        }
 
@@ -1640,7 +1661,7 @@ struct lease *find_lease (packet, share, ours)
 
        /* Now eliminate leases that are on the wrong network... */
        if (ip_lease &&
-           (share != ip_lease -> shared_network)) {
+           (share != ip_lease -> subnet -> shared_network)) {
                release_lease (ip_lease);
                ip_lease = (struct lease *)0;
 #if defined (DEBUG_FIND_LEASE)
@@ -1648,7 +1669,7 @@ struct lease *find_lease (packet, share, ours)
 #endif
        }
        if (uid_lease &&
-           (share != uid_lease -> shared_network)) {
+           (share != uid_lease -> subnet -> shared_network)) {
                release_lease (uid_lease);
                uid_lease = (struct lease *)0;
 #if defined (DEBUG_FIND_LEASE)
@@ -1656,7 +1677,7 @@ struct lease *find_lease (packet, share, ours)
 #endif
        }
        if (hw_lease &&
-           (share != hw_lease -> shared_network)) {
+           (share != hw_lease -> subnet -> shared_network)) {
                release_lease (hw_lease);
                hw_lease = (struct lease *)0;
 #if defined (DEBUG_FIND_LEASE)
@@ -1752,13 +1773,23 @@ struct lease *find_lease (packet, share, ours)
                note ("Not returning a lease.");
 #endif
 
-       /* If we find an abandoned lease, take it, but print a
-          warning message, so that if it continues to lose,
-          the administrator will eventually investigate. */
-       if (lease && lease -> flags & ABANDONED_LEASE) {
-               warn ("Reclaiming REQUESTed abandoned IP address %s.\n",
+       /* If we find an abandoned lease, but it's the one the client
+          requested, we assume that previous bugginess on the part
+          of the client, or a server database loss, caused the lease to
+          be abandoned, so we reclaim it and let the client have it. */
+       if (lease && lease -> flags & ABANDONED_LEASE && lease == ip_lease &&
+           packet -> packet_type == DHCPREQUEST) {
+               warn ("Reclaiming REQUESTed abandoned IP address %s.",
                      piaddr (lease -> ip_addr));
                lease -> flags &= ~ABANDONED_LEASE;
+       } else if (lease && lease -> flags & ABANDONED_LEASE) {
+       /* Otherwise, if it's not the one the client requested, we do not
+          return it - instead, we claim it's ours, causing a DHCPNAK to be
+          sent if this lookup is for a DHCPREQUEST, and force the client
+          to go back through the allocation process. */
+               if (ours)
+                       *ours = 1;
+               lease = (struct lease *)0;
        }
 
        return lease;
@@ -1779,7 +1810,6 @@ struct lease *mockup_lease (packet, share, hp)
        if (!mock.subnet)
                return (struct lease *)0;
        mock.next = mock.prev = (struct lease *)0;
-       mock.shared_network = mock.subnet -> shared_network;
        mock.host = hp;
        mock.uid = hp -> client_identifier.data;
        mock.uid_len = hp -> client_identifier.len;