📄 dhcp.c
字号:
/* dhcp.c DHCP Protocol engine. *//* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * <info@isc.org> * http://www.isc.org/ * * This software has been written for Internet Systems Consortium * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. * To learn more about Internet Systems Consortium, see * ``http://www.isc.org/''. To learn more about Vixie Enterprises, * see ``http://www.vix.com''. To learn more about Nominum, Inc., see * ``http://www.nominum.com''. */#ifndef lintstatic char copyright[] ="$Id: dhcp.c,v 1.192.2.35 2004/06/17 20:54:40 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";#endif /* not lint */#include "dhcpd.h"int outstanding_pings;static char dhcp_message [256];static const char *dhcp_type_names [] = { "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST", "DHCPDECLINE", "DHCPACK", "DHCPNAK", "DHCPRELEASE", "DHCPINFORM"};const int dhcp_type_name_max = ((sizeof dhcp_type_names) / sizeof (char *));#if defined (TRACING)# define send_packet trace_packet_send#endifvoid dhcp (packet) struct packet *packet;{ int ms_nulltp = 0; struct option_cache *oc; struct lease *lease = (struct lease *)0; const char *errmsg; struct data_string data; if (!locate_network (packet) && packet -> packet_type != DHCPREQUEST && packet -> packet_type != DHCPINFORM) { const char *s; char typebuf [32]; errmsg = "unknown network segment"; bad_packet: if (packet -> packet_type > 0 && packet -> packet_type < dhcp_type_name_max - 1) { s = dhcp_type_names [packet -> packet_type - 1]; } else { /* %Audit% Cannot exceed 28 bytes. %2004.06.17,Safe% */ sprintf (typebuf, "type %d", packet -> packet_type); s = typebuf; } log_info ("%s from %s via %s: %s", s, (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : "<no identifier>"), packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name, errmsg); goto out; } /* There is a problem with the relay agent information option, which is that in order for a normal relay agent to append this option, the relay agent has to have been involved in getting the packet from the client to the server. Note that this is the software entity known as the relay agent, _not_ the hardware entity known as a router in which the relay agent may be running, so the fact that a router has forwarded a packet does not mean that the relay agent in the router was involved. So when the client is in INIT or INIT-REBOOT or REBINDING state, the relay agent gets to tack on its options, but when it's not, the relay agent doesn't get to do this, which means that any decisions the DHCP server may make based on the agent options will be made incorrectly. We work around this in the following way: if this is a DHCPREQUEST and doesn't have relay agent information options, we see if there's an existing lease for this IP address and this client that _does_ have stashed agent options. If so, then we tack those options onto the packet as if they came from the client. Later on, when we are deciding whether to steal the agent options from the packet, if the agent options stashed on the lease are the same as those stashed on the packet, we don't steal them - this ensures that the client never receives its agent options. */ if (packet -> packet_type == DHCPREQUEST && packet -> raw -> ciaddr.s_addr && !packet -> raw -> giaddr.s_addr && (packet -> options -> universe_count < agent_universe.index || !packet -> options -> universes [agent_universe.index])) { struct iaddr cip; cip.len = sizeof packet -> raw -> ciaddr; memcpy (cip.iabuf, &packet -> raw -> ciaddr, sizeof packet -> raw -> ciaddr); if (!find_lease_by_ip_addr (&lease, cip, MDL)) goto nolease; /* If there are no agent options on the lease, it's not interesting. */ if (!lease -> agent_options) goto nolease; /* The client should not be unicasting a renewal if its lease has expired, so make it go through the process of getting its agent options legally. */ if (lease -> ends < cur_time) goto nolease; if (lease -> uid_len) { oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_CLIENT_IDENTIFIER); if (!oc) goto nolease; memset (&data, 0, sizeof data); if (!evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) goto nolease; if (lease -> uid_len != data.len || memcmp (lease -> uid, data.data, data.len)) { data_string_forget (&data, MDL); goto nolease; } data_string_forget (&data, MDL); } else if ((lease -> hardware_addr.hbuf [0] != packet -> raw -> htype) || (lease -> hardware_addr.hlen - 1 != packet -> raw -> hlen) || memcmp (&lease -> hardware_addr.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen)) goto nolease; /* Okay, so we found a lease that matches the client. */ option_chain_head_reference ((struct option_chain_head **) &(packet -> options -> universes [agent_universe.index]), lease -> agent_options, MDL); } nolease: /* Classify the client. */ if ((oc = lookup_option (&dhcp_universe, packet -> options, DHO_HOST_NAME))) { if (!oc -> expression) while (oc -> data.len && oc -> data.data [oc -> data.len - 1] == 0) { ms_nulltp = 1; oc -> data.len--; } } classify_client (packet); switch (packet -> packet_type) { case DHCPDISCOVER: dhcpdiscover (packet, ms_nulltp); break; case DHCPREQUEST: dhcprequest (packet, ms_nulltp, lease); break; case DHCPRELEASE: dhcprelease (packet, ms_nulltp); break; case DHCPDECLINE: dhcpdecline (packet, ms_nulltp); break; case DHCPINFORM: dhcpinform (packet, ms_nulltp); break; case DHCPACK: case DHCPOFFER: case DHCPNAK: break; default: errmsg = "unknown packet type"; goto bad_packet; } out: if (lease) lease_dereference (&lease, MDL);}void dhcpdiscover (packet, ms_nulltp) struct packet *packet; int ms_nulltp;{ struct lease *lease = (struct lease *)0; char msgbuf [1024]; /* XXX */ TIME when; char *s; int allocatedp = 0; int peer_has_leases = 0;#if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *peer;#endif find_lease (&lease, packet, packet -> shared_network, 0, &allocatedp, (struct lease *)0, MDL); if (lease && lease -> client_hostname) { if ((strlen (lease -> client_hostname) <= 64) && db_printable (lease -> client_hostname)) s = lease -> client_hostname; else s = "Hostname Unsuitable for Printing"; } else s = (char *)0; /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ snprintf (msgbuf, sizeof msgbuf, "DHCPDISCOVER from %s %s%s%svia %s", (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1 (lease -> uid_len, lease -> uid, lease -> uid_len) : "<no identifier>")), s ? "(" : "", s ? s : "", s ? ") " : "", packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name); /* Sourceless packets don't make sense here. */ if (!packet -> shared_network) { log_info ("Packet from unknown subnet: %s", inet_ntoa (packet -> raw -> giaddr)); goto out; }#if defined (FAILOVER_PROTOCOL) if (lease && lease -> pool && lease -> pool -> failover_peer) { peer = lease -> pool -> failover_peer; /* If the lease is ours to allocate, then allocate it, but set the allocatedp flag. */ if (lease_mine_to_reallocate (lease)) allocatedp = 1; /* If the lease is active, do load balancing to see who allocates the lease (if it's active, it already belongs to the client, or we wouldn't have gotten it from find_lease (). */ else if (lease -> binding_state == FTS_ACTIVE && (peer -> service_state != cooperating || load_balance_mine (packet, peer))) ; /* Otherwise, we can't let the client have this lease. */ else {#if defined (DEBUG_FIND_LEASE) log_debug ("discarding %s - %s", piaddr (lease -> ip_addr), binding_state_print (lease -> binding_state));#endif lease_dereference (&lease, MDL); } }#endif /* If we didn't find a lease, try to allocate one... */ if (!lease) { if (!allocate_lease (&lease, packet, packet -> shared_network -> pools, &peer_has_leases)) { if (peer_has_leases) log_error ("%s: peer holds all free leases", msgbuf); else log_error ("%s: network %s: no free leases", msgbuf, packet -> shared_network -> name); return; }#if defined (FAILOVER_PROTOCOL) if (lease -> pool && lease -> pool -> failover_peer) dhcp_failover_pool_check (lease -> pool);#endif allocatedp = 1; }#if defined (FAILOVER_PROTOCOL) if (lease && lease -> pool && lease -> pool -> failover_peer) { peer = lease -> pool -> failover_peer; if (peer -> service_state == not_responding || peer -> service_state == service_startup) { log_info ("%s: not responding%s", msgbuf, peer -> nrr); goto out; } } else peer = (dhcp_failover_state_t *)0; /* Do load balancing if configured. */ /* If the lease is newly allocated, and we're not the server that the client would normally get with load balancing, and the failover protocol state is normal, let the other server get this. XXX Check protocol spec to make sure that predicating this on XXX allocatedp is okay - I'm doing this so that the client won't XXX be forced to switch servers (and IP addresses) just because XXX of bad luck, when it's possible for it to get the address it XXX is requesting. Not sure this is allowed. */ if (allocatedp && peer) { if (peer -> service_state == cooperating) { if (!load_balance_mine (packet, peer)) { log_debug ("%s: load balance to peer %s", msgbuf, peer -> name); goto out; } } }#endif /* If it's an expired lease, get rid of any bindings. */ if (lease -> ends < cur_time && lease -> scope) binding_scope_dereference (&lease -> scope, MDL); /* Set the lease to really expire in 2 minutes, unless it has not yet expired, in which case leave its expiry time alone. */ when = cur_time + 120; if (when < lease -> ends) when = lease -> ends; ack_lease (packet, lease, DHCPOFFER, when, msgbuf, ms_nulltp); out: if (lease) lease_dereference (&lease, MDL);}void dhcprequest (packet, ms_nulltp, ip_lease) struct packet *packet; int ms_nulltp; struct lease *ip_lease;{ struct lease *lease; struct iaddr cip; struct iaddr sip; struct subnet *subnet; int ours = 0; struct option_cache *oc; struct data_string data; int status; char msgbuf [1024]; /* XXX */ char *s; char smbuf [19];#if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *peer;#endif int have_server_identifier = 0; int have_requested_addr = 0; oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_REQUESTED_ADDRESS); memset (&data, 0, sizeof data); if (oc && evaluate_option_cache (&data, packet, (struct lease *)0,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -