mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-31 14:25:41 +00:00
Move a lot of packet discard code into ack_lease(), after we've evaluated the statements associated with a packet. Use new-style option processing. No more explicit user/vendor class support. Revamp lease time processing. Get client identifier directly from host declaration.
This commit is contained in:
687
server/dhcp.c
687
server/dhcp.c
@@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
#ifndef lint
|
#ifndef lint
|
||||||
static char copyright[] =
|
static char copyright[] =
|
||||||
"$Id: dhcp.c,v 1.63 1998/04/19 23:34:43 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
|
"$Id: dhcp.c,v 1.64 1998/06/25 03:56:24 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
|
||||||
#endif /* not lint */
|
#endif /* not lint */
|
||||||
|
|
||||||
#include "dhcpd.h"
|
#include "dhcpd.h"
|
||||||
@@ -109,12 +109,6 @@ void dhcpdiscover (packet)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure this packet satisfies the configured minimum
|
|
||||||
number of seconds. */
|
|
||||||
if (packet -> raw -> secs <
|
|
||||||
packet -> shared_network -> group -> min_secs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* If we didn't find a lease, try to allocate one... */
|
/* If we didn't find a lease, try to allocate one... */
|
||||||
if (!lease) {
|
if (!lease) {
|
||||||
lease = packet -> shared_network -> last_lease;
|
lease = packet -> shared_network -> last_lease;
|
||||||
@@ -162,24 +156,7 @@ void dhcpdiscover (packet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this subnet won't boot unknown clients, ignore the
|
ack_lease (packet, lease, DHCPOFFER, cur_time + 120);
|
||||||
request. */
|
|
||||||
if (!lease -> host &&
|
|
||||||
!lease -> subnet -> group -> boot_unknown_clients) {
|
|
||||||
note ("Ignoring unknown client %s",
|
|
||||||
print_hw_addr (packet -> raw -> htype,
|
|
||||||
packet -> raw -> hlen,
|
|
||||||
packet -> raw -> chaddr));
|
|
||||||
} else if (lease -> host &&
|
|
||||||
!lease -> host -> group -> allow_booting) {
|
|
||||||
note ("Declining to boot client %s",
|
|
||||||
lease -> host -> name
|
|
||||||
? lease -> host -> name
|
|
||||||
: print_hw_addr (packet -> raw -> htype,
|
|
||||||
packet -> raw -> hlen,
|
|
||||||
packet -> raw -> chaddr));
|
|
||||||
} else
|
|
||||||
ack_lease (packet, lease, DHCPOFFER, cur_time + 120);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dhcprequest (packet)
|
void dhcprequest (packet)
|
||||||
@@ -422,32 +399,29 @@ void nak_lease (packet, cip)
|
|||||||
unsigned char nak = DHCPNAK;
|
unsigned char nak = DHCPNAK;
|
||||||
struct packet outgoing;
|
struct packet outgoing;
|
||||||
struct hardware hto;
|
struct hardware hto;
|
||||||
|
int i;
|
||||||
|
|
||||||
struct tree_cache *options [256];
|
struct option_state options;
|
||||||
struct tree_cache dhcpnak_tree;
|
|
||||||
struct tree_cache dhcpmsg_tree;
|
|
||||||
|
|
||||||
memset (options, 0, sizeof options);
|
memset (&options, 0, sizeof options);
|
||||||
memset (&outgoing, 0, sizeof outgoing);
|
memset (&outgoing, 0, sizeof outgoing);
|
||||||
memset (&raw, 0, sizeof raw);
|
memset (&raw, 0, sizeof raw);
|
||||||
outgoing.raw = &raw;
|
outgoing.raw = &raw;
|
||||||
|
|
||||||
/* Set DHCP_MESSAGE_TYPE to DHCPNAK */
|
/* Set DHCP_MESSAGE_TYPE to DHCPNAK */
|
||||||
options [DHO_DHCP_MESSAGE_TYPE] = &dhcpnak_tree;
|
i = DHO_DHCP_MESSAGE_TYPE;
|
||||||
options [DHO_DHCP_MESSAGE_TYPE] -> value = &nak;
|
options.dhcp_options [i] =
|
||||||
options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof nak;
|
option_cache (make_const_data (&nak, sizeof nak, 0, 0),
|
||||||
options [DHO_DHCP_MESSAGE_TYPE] -> buf_size = sizeof nak;
|
dhcp_universe.options [i]);
|
||||||
options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
|
|
||||||
options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
|
|
||||||
|
|
||||||
/* Set DHCP_MESSAGE to whatever the message is */
|
/* Set DHCP_MESSAGE to whatever the message is */
|
||||||
options [DHO_DHCP_MESSAGE] = &dhcpmsg_tree;
|
i = DHO_DHCP_MESSAGE;
|
||||||
options [DHO_DHCP_MESSAGE] -> value = (unsigned char *)dhcp_message;
|
options.server_options [i] =
|
||||||
options [DHO_DHCP_MESSAGE] -> buf_size =
|
option_cache (make_const_data (dhcp_message,
|
||||||
options [DHO_DHCP_MESSAGE] -> len = strlen (dhcp_message);
|
strlen (dhcp_message),
|
||||||
options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF;
|
1, 0),
|
||||||
options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0;
|
dhcp_universe.options [i]);
|
||||||
|
|
||||||
/* Do not use the client's requested parameter list. */
|
/* Do not use the client's requested parameter list. */
|
||||||
packet -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].len = 0;
|
packet -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].len = 0;
|
||||||
packet -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data =
|
packet -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data =
|
||||||
@@ -455,8 +429,7 @@ void nak_lease (packet, cip)
|
|||||||
|
|
||||||
/* Set up the option buffer... */
|
/* Set up the option buffer... */
|
||||||
outgoing.packet_length =
|
outgoing.packet_length =
|
||||||
cons_options (packet, outgoing.raw, 0,
|
cons_options (packet, outgoing.raw, 0, &options, 0, 0, 0);
|
||||||
options, packet -> agent_options, 0, 0, 0);
|
|
||||||
|
|
||||||
/* memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/
|
/* memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/
|
||||||
raw.siaddr = packet -> interface -> primary_address;
|
raw.siaddr = packet -> interface -> primary_address;
|
||||||
@@ -544,9 +517,11 @@ void ack_lease (packet, lease, offer, when)
|
|||||||
struct lease_state *state;
|
struct lease_state *state;
|
||||||
TIME lease_time;
|
TIME lease_time;
|
||||||
TIME offered_lease_time;
|
TIME offered_lease_time;
|
||||||
|
struct data_string d1;
|
||||||
|
TIME comp_lease_time;
|
||||||
|
|
||||||
struct class *vendor_class, *user_class;
|
|
||||||
int i;
|
int i;
|
||||||
|
int val;
|
||||||
|
|
||||||
/* If we're already acking this lease, don't do it again. */
|
/* If we're already acking this lease, don't do it again. */
|
||||||
if (lease -> state) {
|
if (lease -> state) {
|
||||||
@@ -554,44 +529,7 @@ void ack_lease (packet, lease, offer, when)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet -> options [DHO_DHCP_CLASS_IDENTIFIER].len) {
|
/* XXX Process class restrictions. */
|
||||||
vendor_class =
|
|
||||||
find_class (0,
|
|
||||||
(char *)packet ->
|
|
||||||
options [DHO_DHCP_CLASS_IDENTIFIER].data,
|
|
||||||
packet ->
|
|
||||||
options [DHO_DHCP_CLASS_IDENTIFIER].len);
|
|
||||||
} else {
|
|
||||||
vendor_class = (struct class *)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet -> options [DHO_DHCP_USER_CLASS_ID].len) {
|
|
||||||
user_class =
|
|
||||||
find_class (1,
|
|
||||||
(char *)packet ->
|
|
||||||
options [DHO_DHCP_USER_CLASS_ID].data,
|
|
||||||
packet ->
|
|
||||||
options [DHO_DHCP_USER_CLASS_ID].len);
|
|
||||||
} else {
|
|
||||||
user_class = (struct class *)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there is not a specific host entry, and either the
|
|
||||||
* vendor class or user class (if they exist) deny booting,
|
|
||||||
* then bug out.
|
|
||||||
*/
|
|
||||||
if (!lease -> host) {
|
|
||||||
if (vendor_class && !vendor_class -> group -> allow_booting) {
|
|
||||||
debug ("Booting denied by vendor class");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user_class && !user_class -> group -> allow_booting) {
|
|
||||||
debug ("Booting denied by user class");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate a lease state structure... */
|
/* Allocate a lease state structure... */
|
||||||
state = new_lease_state ("ack_lease");
|
state = new_lease_state ("ack_lease");
|
||||||
@@ -624,24 +562,104 @@ void ack_lease (packet, lease, offer, when)
|
|||||||
lease -> client_hostname = 0;
|
lease -> client_hostname = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Choose a filename; first from the host_decl, if any, then from
|
/* Process all of the executable statements associated with
|
||||||
the user class, then from the vendor class. */
|
this lease. */
|
||||||
if (lease -> host && lease -> host -> group -> filename)
|
memset (&state -> options, 0, sizeof state -> options);
|
||||||
state -> filename = lease -> host -> group -> filename;
|
|
||||||
else if (user_class && user_class -> group -> filename)
|
/* Steal the agent options from the packet. */
|
||||||
state -> filename = user_class -> group -> filename;
|
if (packet -> agent_options) {
|
||||||
else if (vendor_class && vendor_class -> group -> filename)
|
state -> options.agent_options = packet -> agent_options;
|
||||||
state -> filename = vendor_class -> group -> filename;
|
packet -> agent_options = (struct agent_options *)0;
|
||||||
else state -> filename = (char *)0;
|
}
|
||||||
|
|
||||||
|
/* Execute the subnet statements. */
|
||||||
|
execute_statements_in_scope (packet, &state -> options,
|
||||||
|
lease -> subnet -> group,
|
||||||
|
(struct group *)0);
|
||||||
|
|
||||||
|
/* Vendor and user classes are only supported for DHCP clients. */
|
||||||
|
if (state -> offer) {
|
||||||
|
/* XXX process class stuff here. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have a host_decl structure, run the options associated
|
||||||
|
with its group. */
|
||||||
|
if (lease -> host)
|
||||||
|
execute_statements_in_scope (packet, &state -> options,
|
||||||
|
lease -> host -> group,
|
||||||
|
lease -> subnet -> group);
|
||||||
|
|
||||||
|
/* Make sure this packet satisfies the configured minimum
|
||||||
|
number of seconds. */
|
||||||
|
if (state -> options.server_options [SV_MIN_SECS]) {
|
||||||
|
d1 = evaluate_data_expression
|
||||||
|
(packet,
|
||||||
|
(state -> options.server_options
|
||||||
|
[SV_MIN_SECS] -> expression));
|
||||||
|
if (d1.len && packet -> raw -> secs < d1.data [0])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drop the request if it's not allowed for this client. */
|
||||||
|
if (!lease -> host &&
|
||||||
|
state -> options.server_options [SV_BOOT_UNKNOWN_CLIENTS]) {
|
||||||
|
d1 = evaluate_data_expression
|
||||||
|
(packet, (state -> options.server_options
|
||||||
|
[SV_BOOT_UNKNOWN_CLIENTS] -> expression));
|
||||||
|
if (d1.len && !d1.data [0]) {
|
||||||
|
note ("Ignoring unknown client %s",
|
||||||
|
print_hw_addr (packet -> raw -> htype,
|
||||||
|
packet -> raw -> hlen,
|
||||||
|
packet -> raw -> chaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drop the request if it's not allowed for this client. */
|
||||||
|
if (!offer &&
|
||||||
|
state -> options.server_options [SV_ALLOW_BOOTP]) {
|
||||||
|
d1 = evaluate_data_expression
|
||||||
|
(packet, (state -> options.server_options
|
||||||
|
[SV_ALLOW_BOOTP] -> expression));
|
||||||
|
if (d1.len && !d1.data [0]) {
|
||||||
|
note ("Ignoring BOOTP client %s",
|
||||||
|
print_hw_addr (packet -> raw -> htype,
|
||||||
|
packet -> raw -> hlen,
|
||||||
|
packet -> raw -> chaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state -> options.server_options [SV_ALLOW_BOOTING]) {
|
||||||
|
d1 = evaluate_data_expression
|
||||||
|
(packet, (state -> options.server_options
|
||||||
|
[SV_ALLOW_BOOTING] -> expression));
|
||||||
|
if (d1.len && !d1.data [0]) {
|
||||||
|
note ("Declining to boot client %s",
|
||||||
|
lease -> host -> name
|
||||||
|
? lease -> host -> name
|
||||||
|
: print_hw_addr (packet -> raw -> htype,
|
||||||
|
packet -> raw -> hlen,
|
||||||
|
packet -> raw -> chaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out the filename. */
|
||||||
|
if (state -> options.server_options [SV_FILENAME])
|
||||||
|
state -> filename =
|
||||||
|
(evaluate_data_expression
|
||||||
|
(packet,
|
||||||
|
(state -> options.
|
||||||
|
server_options [SV_FILENAME] -> expression)));
|
||||||
|
|
||||||
/* Choose a server name as above. */
|
/* Choose a server name as above. */
|
||||||
if (lease -> host && lease -> host -> group -> server_name)
|
if (state -> options.server_options [SV_SERVER_NAME])
|
||||||
state -> server_name = lease -> host -> group -> server_name;
|
state -> server_name =
|
||||||
else if (user_class && user_class -> group -> server_name)
|
(evaluate_data_expression
|
||||||
state -> server_name = user_class -> group -> server_name;
|
(packet,
|
||||||
else if (vendor_class && vendor_class -> group -> server_name)
|
(state -> options.
|
||||||
state -> server_name = vendor_class -> group -> server_name;
|
server_options [SV_SERVER_NAME] -> expression)));
|
||||||
else state -> server_name = (char *)0;
|
|
||||||
|
|
||||||
/* At this point, we have a lease that we can offer the client.
|
/* At this point, we have a lease that we can offer the client.
|
||||||
Now we construct a lease structure that contains what we want,
|
Now we construct a lease structure that contains what we want,
|
||||||
@@ -659,70 +677,83 @@ void ack_lease (packet, lease, offer, when)
|
|||||||
/* Figure out how long a lease to assign. If this is a
|
/* Figure out how long a lease to assign. If this is a
|
||||||
dynamic BOOTP lease, its duration must be infinite. */
|
dynamic BOOTP lease, its duration must be infinite. */
|
||||||
if (offer) {
|
if (offer) {
|
||||||
if (packet -> options [DHO_DHCP_LEASE_TIME].len == 4) {
|
if (packet -> options [DHO_DHCP_LEASE_TIME].len ==
|
||||||
|
sizeof (u_int32_t)) {
|
||||||
lease_time = getULong
|
lease_time = getULong
|
||||||
(packet -> options [DHO_DHCP_LEASE_TIME].data);
|
(packet -> options [DHO_DHCP_LEASE_TIME].data);
|
||||||
|
comp_lease_time = DEFAULT_MAX_LEASE_TIME;
|
||||||
/* Don't let the client ask for a longer lease than
|
i = SV_MAX_LEASE_TIME;
|
||||||
is supported for this subnet or host. */
|
if (state ->
|
||||||
if (lease -> host &&
|
options.server_options [i]) {
|
||||||
lease -> host -> group -> max_lease_time) {
|
d1 = evaluate_data_expression
|
||||||
if (lease_time >
|
(packet,
|
||||||
lease -> host -> group -> max_lease_time)
|
state -> options.
|
||||||
lease_time = (lease -> host ->
|
server_options [i] -> expression);
|
||||||
group -> max_lease_time);
|
if (d1.len == sizeof (u_int32_t))
|
||||||
} else {
|
comp_lease_time = getULong (d1.data);
|
||||||
if (lease_time >
|
|
||||||
lease -> subnet -> group -> max_lease_time)
|
|
||||||
lease_time = (lease -> subnet ->
|
|
||||||
group -> max_lease_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enforce the maximum lease length. */
|
||||||
|
if (lease_time > comp_lease_time)
|
||||||
|
lease_time = comp_lease_time;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (lease -> host
|
i = SV_DEFAULT_LEASE_TIME;
|
||||||
&& lease -> host -> group -> default_lease_time)
|
lease_time = DEFAULT_DEFAULT_LEASE_TIME;
|
||||||
lease_time = (lease -> host ->
|
if (state ->
|
||||||
group -> default_lease_time);
|
options.server_options [i]) {
|
||||||
else
|
d1 = evaluate_data_expression
|
||||||
lease_time = (lease -> subnet ->
|
(packet,
|
||||||
group -> default_lease_time);
|
state -> options.
|
||||||
|
server_options [i] -> expression);
|
||||||
|
if (d1.len == sizeof (u_int32_t))
|
||||||
|
lease_time = getULong (d1.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enforce a minimum lease time, if specified. */
|
i = SV_MIN_LEASE_TIME;
|
||||||
if (lease -> host) {
|
comp_lease_time = DEFAULT_MIN_LEASE_TIME;
|
||||||
if (lease_time <
|
if (state -> options.server_options [i]) {
|
||||||
lease -> host -> group -> min_lease_time)
|
d1 = evaluate_data_expression
|
||||||
lease_time = (lease ->
|
(packet,
|
||||||
host -> group -> min_lease_time);
|
state -> options.
|
||||||
} else {
|
server_options [i] -> expression);
|
||||||
if (lease_time <
|
if (d1.len == sizeof (u_int32_t))
|
||||||
lease -> subnet -> group -> min_lease_time)
|
comp_lease_time = getULong (d1.data);
|
||||||
lease_time = (lease -> subnet ->
|
|
||||||
group -> min_lease_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lease_time < comp_lease_time)
|
||||||
|
lease_time = comp_lease_time;
|
||||||
|
|
||||||
state -> offered_expiry = cur_time + lease_time;
|
state -> offered_expiry = cur_time + lease_time;
|
||||||
if (when)
|
if (when)
|
||||||
lt.ends = when;
|
lt.ends = when;
|
||||||
else
|
else
|
||||||
lt.ends = state -> offered_expiry;
|
lt.ends = state -> offered_expiry;
|
||||||
} else {
|
} else {
|
||||||
if (lease -> host &&
|
lease_time = MAX_TIME - cur_time;
|
||||||
lease -> host -> group -> bootp_lease_length)
|
|
||||||
lt.ends = (cur_time +
|
i = SV_BOOTP_LEASE_LENGTH;
|
||||||
lease -> host ->
|
if (state -> options.server_options [i]) {
|
||||||
group -> bootp_lease_length);
|
d1 = evaluate_data_expression
|
||||||
else if (lease -> subnet -> group -> bootp_lease_length)
|
(packet,
|
||||||
lt.ends = (cur_time +
|
state -> options.
|
||||||
lease -> subnet ->
|
server_options [i] -> expression);
|
||||||
group -> bootp_lease_length);
|
if (d1.len == sizeof (u_int32_t))
|
||||||
else if (lease -> host &&
|
lease_time = getULong (d1.data);
|
||||||
lease -> host -> group -> bootp_lease_cutoff)
|
}
|
||||||
lt.ends = lease -> host -> group -> bootp_lease_cutoff;
|
|
||||||
else
|
i = SV_BOOTP_LEASE_CUTOFF;
|
||||||
lt.ends = (lease -> subnet ->
|
if (state -> options.server_options [i]) {
|
||||||
group -> bootp_lease_cutoff);
|
d1 = evaluate_data_expression
|
||||||
state -> offered_expiry = lt.ends;
|
(packet,
|
||||||
|
state -> options.
|
||||||
|
server_options [i] -> expression);
|
||||||
|
if (d1.len == sizeof (u_int32_t))
|
||||||
|
lease_time = getULong (d1.data) - cur_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
lt.ends = state -> offered_expiry = cur_time + lease_time;
|
||||||
lt.flags = BOOTP_LEASE;
|
lt.flags = BOOTP_LEASE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,136 +836,58 @@ void ack_lease (packet, lease, offer, when)
|
|||||||
options [DHO_DHCP_MAX_MESSAGE_SIZE].data);
|
options [DHO_DHCP_MAX_MESSAGE_SIZE].data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Steal the agent options from the packet. */
|
|
||||||
if (packet -> agent_options) {
|
|
||||||
state -> agent_options = packet -> agent_options;
|
|
||||||
packet -> agent_options = (struct agent_options *)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Figure out what options to send to the client: */
|
|
||||||
|
|
||||||
/* Start out with the subnet options... */
|
|
||||||
memcpy (state -> options,
|
|
||||||
lease -> subnet -> group -> options,
|
|
||||||
sizeof state -> options);
|
|
||||||
|
|
||||||
/* Vendor and user classes are only supported for DHCP clients. */
|
|
||||||
if (state -> offer) {
|
|
||||||
/* If we have a vendor class, install those options,
|
|
||||||
superseding any subnet options. */
|
|
||||||
if (vendor_class) {
|
|
||||||
for (i = 0; i < 256; i++)
|
|
||||||
if (vendor_class -> group -> options [i])
|
|
||||||
state -> options [i] =
|
|
||||||
(vendor_class -> group ->
|
|
||||||
options [i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have a user class, install those options,
|
|
||||||
superseding any subnet and vendor class options. */
|
|
||||||
if (user_class) {
|
|
||||||
for (i = 0; i < 256; i++)
|
|
||||||
if (user_class -> group -> options [i])
|
|
||||||
state -> options [i] =
|
|
||||||
(user_class -> group ->
|
|
||||||
options [i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have a host_decl structure, install the associated
|
|
||||||
options, superseding anything that's in the way. */
|
|
||||||
if (lease -> host) {
|
|
||||||
for (i = 0; i < 256; i++)
|
|
||||||
if (lease -> host -> group -> options [i])
|
|
||||||
state -> options [i] = (lease -> host ->
|
|
||||||
group -> options [i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we didn't get a hostname from an option somewhere, see if
|
|
||||||
we can get one from the lease. */
|
|
||||||
i = DHO_HOST_NAME;
|
|
||||||
if (!state -> options [i] && lease -> hostname) {
|
|
||||||
state -> options [i] = new_tree_cache ("hostname");
|
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
|
||||||
state -> options [i] -> value =
|
|
||||||
(unsigned char *)lease -> hostname;
|
|
||||||
state -> options [i] -> len = strlen (lease -> hostname);
|
|
||||||
state -> options [i] -> buf_size = state -> options [i] -> len;
|
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now, if appropriate, put in DHCP-specific options that
|
/* Now, if appropriate, put in DHCP-specific options that
|
||||||
override those. */
|
override those. */
|
||||||
if (state -> offer) {
|
if (state -> offer) {
|
||||||
i = DHO_DHCP_MESSAGE_TYPE;
|
i = DHO_DHCP_MESSAGE_TYPE;
|
||||||
state -> options [i] = new_tree_cache ("message-type");
|
state -> options.dhcp_options [i] =
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
option_cache (make_const_data (&state -> offer,
|
||||||
state -> options [i] -> value = &state -> offer;
|
sizeof state -> offer,
|
||||||
state -> options [i] -> len = sizeof state -> offer;
|
0, 0),
|
||||||
state -> options [i] -> buf_size = sizeof state -> offer;
|
dhcp_universe.options [i]);
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
|
|
||||||
i = DHO_DHCP_SERVER_IDENTIFIER;
|
i = DHO_DHCP_SERVER_IDENTIFIER;
|
||||||
if (!state -> options [i]) {
|
if (!state -> options.dhcp_options [i]) {
|
||||||
state -> options [i] = new_tree_cache ("server-id");
|
state -> options.dhcp_options [i] =
|
||||||
state -> options [i] -> value =
|
(option_cache
|
||||||
(unsigned char *)&state ->
|
(make_const_data
|
||||||
ip -> primary_address;
|
((unsigned char *)
|
||||||
state -> options [i] -> len =
|
&state -> ip -> primary_address,
|
||||||
sizeof state -> ip -> primary_address;
|
sizeof state -> ip -> primary_address,
|
||||||
state -> options [i] -> buf_size
|
0, 0),
|
||||||
= state -> options [i] -> len;
|
dhcp_universe.options [i]));
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sanity check the lease time. */
|
offered_lease_time =
|
||||||
if ((state -> offered_expiry - cur_time) < 15)
|
state -> offered_expiry - cur_time;
|
||||||
offered_lease_time = (lease -> subnet ->
|
|
||||||
group -> default_lease_time);
|
|
||||||
else if (state -> offered_expiry - cur_time >
|
|
||||||
lease -> subnet -> group -> max_lease_time)
|
|
||||||
offered_lease_time = (lease -> subnet ->
|
|
||||||
group -> max_lease_time);
|
|
||||||
else
|
|
||||||
offered_lease_time =
|
|
||||||
state -> offered_expiry - cur_time;
|
|
||||||
|
|
||||||
putULong ((unsigned char *)&state -> expiry,
|
putULong ((unsigned char *)&state -> expiry,
|
||||||
offered_lease_time);
|
offered_lease_time);
|
||||||
i = DHO_DHCP_LEASE_TIME;
|
i = DHO_DHCP_LEASE_TIME;
|
||||||
if (state -> options [i])
|
if (state -> options.dhcp_options [i])
|
||||||
warn ("dhcp-lease-time option for %s overridden.",
|
warn ("dhcp-lease-time option for %s overridden.",
|
||||||
inet_ntoa (state -> ciaddr));
|
inet_ntoa (state -> ciaddr));
|
||||||
state -> options [i] = new_tree_cache ("lease-expiry");
|
state -> options.dhcp_options [i] =
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
option_cache (make_const_data ((unsigned char *)
|
||||||
state -> options [i] -> value =
|
&state -> expiry,
|
||||||
(unsigned char *)&state -> expiry;
|
sizeof state -> expiry,
|
||||||
state -> options [i] -> len = sizeof state -> expiry;
|
0, 0),
|
||||||
state -> options [i] -> buf_size = sizeof state -> expiry;
|
dhcp_universe.options [i]);
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
|
|
||||||
/* Renewal time is lease time * 0.5. */
|
/* Renewal time is lease time * 0.5. */
|
||||||
offered_lease_time /= 2;
|
offered_lease_time /= 2;
|
||||||
putULong ((unsigned char *)&state -> renewal,
|
putULong ((unsigned char *)&state -> renewal,
|
||||||
offered_lease_time);
|
offered_lease_time);
|
||||||
i = DHO_DHCP_RENEWAL_TIME;
|
i = DHO_DHCP_RENEWAL_TIME;
|
||||||
if (state -> options [i])
|
if (state -> options.dhcp_options [i])
|
||||||
warn ("dhcp-renewal-time option for %s overridden.",
|
warn ("dhcp-renewal-time option for %s overridden.",
|
||||||
inet_ntoa (state -> ciaddr));
|
inet_ntoa (state -> ciaddr));
|
||||||
state -> options [i] = new_tree_cache ("renewal-time");
|
state -> options.dhcp_options [i] =
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
option_cache (make_const_data ((unsigned char *)
|
||||||
state -> options [i] -> value =
|
&state -> renewal,
|
||||||
(unsigned char *)&state -> renewal;
|
sizeof state -> renewal,
|
||||||
state -> options [i] -> len = sizeof state -> renewal;
|
0, 0),
|
||||||
state -> options [i] -> buf_size = sizeof state -> renewal;
|
dhcp_universe.options [i]);
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
|
|
||||||
|
|
||||||
/* Rebinding time is lease time * 0.875. */
|
/* Rebinding time is lease time * 0.875. */
|
||||||
offered_lease_time += (offered_lease_time / 2
|
offered_lease_time += (offered_lease_time / 2
|
||||||
@@ -942,87 +895,95 @@ void ack_lease (packet, lease, offer, when)
|
|||||||
putULong ((unsigned char *)&state -> rebind,
|
putULong ((unsigned char *)&state -> rebind,
|
||||||
offered_lease_time);
|
offered_lease_time);
|
||||||
i = DHO_DHCP_REBINDING_TIME;
|
i = DHO_DHCP_REBINDING_TIME;
|
||||||
if (state -> options [i])
|
if (state -> options.dhcp_options [i])
|
||||||
warn ("dhcp-rebinding-time option for %s overridden.",
|
warn ("dhcp-rebinding-time option for %s overridden.",
|
||||||
inet_ntoa (state -> ciaddr));
|
inet_ntoa (state -> ciaddr));
|
||||||
state -> options [i] = new_tree_cache ("rebind-time");
|
state -> options.dhcp_options [i] =
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
option_cache (make_const_data ((unsigned char *)
|
||||||
state -> options [i] -> value =
|
&state -> rebind,
|
||||||
(unsigned char *)&state -> rebind;
|
sizeof state -> rebind,
|
||||||
state -> options [i] -> len = sizeof state -> rebind;
|
0, 0),
|
||||||
state -> options [i] -> buf_size = sizeof state -> rebind;
|
dhcp_universe.options [i]);
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
|
|
||||||
/* If we used the vendor class the client specified, we
|
|
||||||
have to return it. */
|
|
||||||
if (vendor_class) {
|
|
||||||
i = DHO_DHCP_CLASS_IDENTIFIER;
|
|
||||||
state -> options [i] =
|
|
||||||
new_tree_cache ("class-identifier");
|
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
|
||||||
state -> options [i] -> value =
|
|
||||||
(unsigned char *)vendor_class -> name;
|
|
||||||
state -> options [i] -> len =
|
|
||||||
strlen (vendor_class -> name);
|
|
||||||
state -> options [i] -> buf_size =
|
|
||||||
state -> options [i] -> len;
|
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we used the user class the client specified, we
|
|
||||||
have to return it. */
|
|
||||||
if (user_class) {
|
|
||||||
i = DHO_DHCP_USER_CLASS_ID;
|
|
||||||
state -> options [i] = new_tree_cache ("user-class");
|
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
|
||||||
state -> options [i] -> value =
|
|
||||||
(unsigned char *)user_class -> name;
|
|
||||||
state -> options [i] -> len =
|
|
||||||
strlen (user_class -> name);
|
|
||||||
state -> options [i] -> buf_size =
|
|
||||||
state -> options [i] -> len;
|
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use the subnet mask from the subnet declaration if no other
|
/* Use the subnet mask from the subnet declaration if no other
|
||||||
mask has been provided. */
|
mask has been provided. */
|
||||||
i = DHO_SUBNET_MASK;
|
i = DHO_SUBNET_MASK;
|
||||||
if (!state -> options [i]) {
|
if (!state -> options.dhcp_options [i]) {
|
||||||
state -> options [i] = new_tree_cache ("subnet-mask");
|
state -> options.dhcp_options [i] =
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
(option_cache
|
||||||
state -> options [i] -> value =
|
(make_const_data (lease -> subnet -> netmask.iabuf,
|
||||||
lease -> subnet -> netmask.iabuf;
|
lease -> subnet -> netmask.len,
|
||||||
state -> options [i] -> len = lease -> subnet -> netmask.len;
|
0, 0),
|
||||||
state -> options [i] -> buf_size =
|
dhcp_universe.options [i]));
|
||||||
lease -> subnet -> netmask.len;
|
}
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
/* Use the hostname from the host declaration if there is one
|
||||||
|
and no hostname has otherwise been provided, and if the
|
||||||
|
use-host-decl-name flag is set. */
|
||||||
|
i = DHO_HOST_NAME;
|
||||||
|
if (!state -> options.dhcp_options [i] &&
|
||||||
|
lease -> host && lease -> host -> name) {
|
||||||
|
d1 = evaluate_data_expression
|
||||||
|
(packet, (state -> options.server_options
|
||||||
|
[SV_USE_HOST_DECL_NAMES] -> expression));
|
||||||
|
if (d1.len && d1.data [0]) {
|
||||||
|
state -> options.dhcp_options [i] =
|
||||||
|
(option_cache
|
||||||
|
(make_const_data (lease -> host -> name,
|
||||||
|
strlen (lease ->
|
||||||
|
host -> name),
|
||||||
|
1, 0),
|
||||||
|
dhcp_universe.options [i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we don't have a hostname yet, and we've been asked to do
|
||||||
|
a reverse lookup to find the hostname, do it. */
|
||||||
|
if (!state -> options.dhcp_options [i]
|
||||||
|
&& state -> options.server_options [SV_GET_LEASE_HOSTNAMES]) {
|
||||||
|
d1 = evaluate_data_expression
|
||||||
|
(packet, (state -> options.server_options
|
||||||
|
[SV_GET_LEASE_HOSTNAMES] -> expression));
|
||||||
|
if (d1.len && d1.data [0]) {
|
||||||
|
struct in_addr ia;
|
||||||
|
struct hostent *h;
|
||||||
|
|
||||||
|
memcpy (&ia, lease -> ip_addr.iabuf, 4);
|
||||||
|
|
||||||
|
h = gethostbyaddr ((char *)&ia, sizeof ia, AF_INET);
|
||||||
|
if (!h)
|
||||||
|
warn ("No hostname for %s", inet_ntoa (ia));
|
||||||
|
else {
|
||||||
|
state -> options.dhcp_options [i] =
|
||||||
|
option_cache
|
||||||
|
(make_const_data
|
||||||
|
(h -> h_name,
|
||||||
|
strlen (h -> h_name) + 1,
|
||||||
|
1, 1),
|
||||||
|
dhcp_universe.options [i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If so directed, use the leased IP address as the router address.
|
/* If so directed, use the leased IP address as the router address.
|
||||||
This supposedly makes Win95 machines ARP for all IP addresses,
|
This supposedly makes Win95 machines ARP for all IP addresses,
|
||||||
so if the local router does proxy arp, you win. */
|
so if the local router does proxy arp, you win. */
|
||||||
if ((lease -> host &&
|
|
||||||
lease -> host -> group -> use_lease_addr_for_default_route) ||
|
|
||||||
(lease -> subnet -> group -> use_lease_addr_for_default_route)) {
|
|
||||||
i = DHO_ROUTERS;
|
|
||||||
|
|
||||||
if (state -> options [i] &&
|
i = SV_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE;
|
||||||
state -> options [i] -> flags & TC_TEMPORARY)
|
if (state -> options.server_options [i]) {
|
||||||
free_tree_cache (state -> options [i], "dhcp_reply");
|
d1 = evaluate_data_expression
|
||||||
|
(packet,
|
||||||
state -> options [i] = new_tree_cache ("routers");
|
state -> options.server_options [i] -> expression);
|
||||||
state -> options [i] -> flags = TC_TEMPORARY;
|
if (d1.len && d1.data) {
|
||||||
state -> options [i] -> value =
|
i = DHO_ROUTERS;
|
||||||
lease -> ip_addr.iabuf;
|
|
||||||
state -> options [i] -> len = lease -> ip_addr.len;
|
state -> options.dhcp_options [i] =
|
||||||
state -> options [i] -> buf_size = lease -> ip_addr.len;
|
option_cache (make_const_data
|
||||||
state -> options [i] -> timeout = 0xFFFFFFFF;
|
(lease -> ip_addr.iabuf,
|
||||||
state -> options [i] -> tree = (struct tree *)0;
|
lease -> ip_addr.len, 0, 0),
|
||||||
|
dhcp_universe.options [i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_PACKET
|
#ifdef DEBUG_PACKET
|
||||||
@@ -1058,6 +1019,7 @@ void dhcp_reply (lease)
|
|||||||
int nulltp, bootpp;
|
int nulltp, bootpp;
|
||||||
struct agent_options *a, *na;
|
struct agent_options *a, *na;
|
||||||
struct option_tag *ot, *not;
|
struct option_tag *ot, *not;
|
||||||
|
struct data_string d1;
|
||||||
|
|
||||||
if (!state)
|
if (!state)
|
||||||
error ("dhcp_reply was supplied lease with no state!");
|
error ("dhcp_reply was supplied lease with no state!");
|
||||||
@@ -1067,16 +1029,28 @@ void dhcp_reply (lease)
|
|||||||
|
|
||||||
/* Copy in the filename if given; otherwise, flag the filename
|
/* Copy in the filename if given; otherwise, flag the filename
|
||||||
buffer as available for options. */
|
buffer as available for options. */
|
||||||
if (state -> filename)
|
if (state -> filename.len && state -> filename.data) {
|
||||||
strncpy (raw.file, state -> filename, sizeof raw.file);
|
memcpy (raw.file,
|
||||||
else
|
state -> filename.data,
|
||||||
|
state -> filename.len > sizeof raw.file
|
||||||
|
? sizeof raw.file : state -> filename.len);
|
||||||
|
if (sizeof raw.file > state -> filename.len)
|
||||||
|
memset (&raw.file [state -> filename.len], 0,
|
||||||
|
(sizeof raw.file) - state -> filename.len);
|
||||||
|
} else
|
||||||
bufs |= 1;
|
bufs |= 1;
|
||||||
|
|
||||||
/* Copy in the server name if given; otherwise, flag the
|
/* Copy in the server name if given; otherwise, flag the
|
||||||
server_name buffer as available for options. */
|
server_name buffer as available for options. */
|
||||||
if (state -> server_name)
|
if (state -> server_name.len && state -> server_name.data) {
|
||||||
strncpy (raw.sname, state -> server_name, sizeof raw.sname);
|
memcpy (raw.sname,
|
||||||
else
|
state -> server_name.data,
|
||||||
|
state -> server_name.len > sizeof raw.sname
|
||||||
|
? sizeof raw.sname : state -> server_name.len);
|
||||||
|
if (sizeof raw.sname > state -> server_name.len)
|
||||||
|
memset (&raw.sname [state -> server_name.len], 0,
|
||||||
|
(sizeof raw.sname) - state -> server_name.len);
|
||||||
|
} else
|
||||||
bufs |= 2; /* XXX */
|
bufs |= 2; /* XXX */
|
||||||
|
|
||||||
memcpy (raw.chaddr, lease -> hardware_addr.haddr, sizeof raw.chaddr);
|
memcpy (raw.chaddr, lease -> hardware_addr.haddr, sizeof raw.chaddr);
|
||||||
@@ -1099,20 +1073,23 @@ void dhcp_reply (lease)
|
|||||||
/* Insert such options as will fit into the buffer. */
|
/* Insert such options as will fit into the buffer. */
|
||||||
packet_length = cons_options ((struct packet *)0, &raw,
|
packet_length = cons_options ((struct packet *)0, &raw,
|
||||||
state -> max_message_size,
|
state -> max_message_size,
|
||||||
state -> options,
|
&state -> options,
|
||||||
state -> agent_options,
|
|
||||||
bufs, nulltp, bootpp);
|
bufs, nulltp, bootpp);
|
||||||
|
|
||||||
/* Having done the cons_options(), we can release the tree_cache
|
/* Having done the cons_options(), we can release the tree_cache
|
||||||
entries. */
|
entries. */
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
if (state -> options [i] &&
|
if (state -> options.dhcp_options [i])
|
||||||
state -> options [i] -> flags & TC_TEMPORARY)
|
free_option_cache (state -> options.dhcp_options [i],
|
||||||
free_tree_cache (state -> options [i], "dhcp_reply");
|
"dhcp_reply");
|
||||||
|
if (state -> options.server_options [i])
|
||||||
|
free_option_cache (state -> options.dhcp_options [i],
|
||||||
|
"dhcp_reply");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We can also release the agent options, if any... */
|
/* We can also release the agent options, if any... */
|
||||||
for (a = state -> agent_options; a; a = na) {
|
for (a = state -> options.agent_options; a; a = na) {
|
||||||
na = a -> next;
|
na = a -> next;
|
||||||
for (ot = a -> first; ot; ot = not) {
|
for (ot = a -> first; ot; ot = not) {
|
||||||
not = ot -> next;
|
not = ot -> next;
|
||||||
@@ -1124,17 +1101,16 @@ void dhcp_reply (lease)
|
|||||||
memcpy (&raw.yiaddr, lease -> ip_addr.iabuf, 4);
|
memcpy (&raw.yiaddr, lease -> ip_addr.iabuf, 4);
|
||||||
|
|
||||||
/* Figure out the address of the next server. */
|
/* Figure out the address of the next server. */
|
||||||
if (lease -> host && lease -> host -> group -> next_server.len)
|
raw.siaddr = state -> ip -> primary_address;
|
||||||
memcpy (&raw.siaddr,
|
if (state -> options.server_options [SV_NEXT_SERVER]) {
|
||||||
lease -> host -> group -> next_server.iabuf, 4);
|
d1 = evaluate_data_expression
|
||||||
else if (lease -> subnet -> group -> next_server.len)
|
((struct packet *)0,
|
||||||
memcpy (&raw.siaddr,
|
(state -> options.
|
||||||
lease -> subnet -> group -> next_server.iabuf, 4);
|
server_options [SV_NEXT_SERVER] -> expression));
|
||||||
else if (lease -> subnet -> interface_address.len)
|
/* If there was more than one answer, take the first. */
|
||||||
memcpy (&raw.siaddr,
|
if (d1.len >= 4 && d1.data)
|
||||||
lease -> subnet -> interface_address.iabuf, 4);
|
memcpy (&raw.siaddr, d1.data, 4);
|
||||||
else
|
}
|
||||||
raw.siaddr = state -> ip -> primary_address;
|
|
||||||
|
|
||||||
raw.giaddr = state -> giaddr;
|
raw.giaddr = state -> giaddr;
|
||||||
|
|
||||||
@@ -1346,7 +1322,7 @@ struct lease *find_lease (packet, share, ours)
|
|||||||
int i = DHO_DHCP_CLIENT_IDENTIFIER;
|
int i = DHO_DHCP_CLIENT_IDENTIFIER;
|
||||||
/* If for some reason the client has more than one lease
|
/* If for some reason the client has more than one lease
|
||||||
on the subnet that matches its uid, pick the one that
|
on the subnet that matches its uid, pick the one that
|
||||||
it asked for. If we have a host */
|
it asked for. */
|
||||||
if (packet -> options [i].data &&
|
if (packet -> options [i].data &&
|
||||||
ip_lease -> uid_len == packet -> options [i].len &&
|
ip_lease -> uid_len == packet -> options [i].len &&
|
||||||
!memcmp (packet -> options [i].data,
|
!memcmp (packet -> options [i].data,
|
||||||
@@ -1501,17 +1477,8 @@ struct lease *mockup_lease (packet, share, hp)
|
|||||||
mock.next = mock.prev = (struct lease *)0;
|
mock.next = mock.prev = (struct lease *)0;
|
||||||
mock.shared_network = mock.subnet -> shared_network;
|
mock.shared_network = mock.subnet -> shared_network;
|
||||||
mock.host = hp;
|
mock.host = hp;
|
||||||
|
mock.uid = hp -> client_identifier.data;
|
||||||
if (hp -> group -> options [DHO_DHCP_CLIENT_IDENTIFIER]) {
|
mock.uid_len = hp -> client_identifier.len;
|
||||||
mock.uid = hp -> group ->
|
|
||||||
options [DHO_DHCP_CLIENT_IDENTIFIER] -> value;
|
|
||||||
mock.uid_len = hp -> group ->
|
|
||||||
options [DHO_DHCP_CLIENT_IDENTIFIER] -> len;
|
|
||||||
} else {
|
|
||||||
mock.uid = (unsigned char *)0;
|
|
||||||
mock.uid_len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mock.hardware_addr = hp -> interface;
|
mock.hardware_addr = hp -> interface;
|
||||||
mock.starts = mock.timestamp = mock.ends = MIN_TIME;
|
mock.starts = mock.timestamp = mock.ends = MIN_TIME;
|
||||||
mock.flags = STATIC_LEASE;
|
mock.flags = STATIC_LEASE;
|
||||||
|
Reference in New Issue
Block a user