#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "dhcp.h"
#include "tree.h"
#include "dhcpd.h"
#include "log.h"
void
bootp(struct packet *packet)
{
struct host_decl *hp, *host = NULL;
struct packet outgoing;
struct dhcp_packet raw;
struct sockaddr_in to;
struct in_addr from;
struct tree_cache *options[256];
struct shared_network *s;
struct subnet *subnet = NULL;
struct lease *lease;
struct iaddr ip_address;
int i;
if (packet->raw->op != BOOTREQUEST)
return;
log_info("BOOTREQUEST from %s via %s%s",
print_hw_addr(packet->raw->htype, packet->raw->hlen,
packet->raw->chaddr), packet->raw->giaddr.s_addr ?
inet_ntoa(packet->raw->giaddr) : packet->interface->name,
packet->options_valid ? "" : " (non-rfc1048)");
if (!locate_network(packet))
return;
hp = find_hosts_by_haddr(packet->raw->htype, packet->raw->chaddr,
packet->raw->hlen);
s = packet->shared_network;
lease = find_lease(packet, s, 0);
if (hp)
subnet = find_host_for_network(&hp, &ip_address, s);
if (!subnet) {
if (hp)
for (; hp; hp = hp->n_ipaddr)
if (!hp->fixed_addr) {
host = hp;
break;
}
if (host && (!host->group->allow_booting)) {
log_info("Ignoring excluded BOOTP client %s",
host->name ? host->name :
print_hw_addr (packet->raw->htype,
packet->raw->hlen, packet->raw->chaddr));
return;
}
if (host && (!host->group->allow_bootp)) {
log_info("Ignoring BOOTP request from client %s",
host->name ? host->name :
print_hw_addr(packet->raw->htype,
packet->raw->hlen, packet->raw->chaddr));
return;
}
if (!host && !(s->group->boot_unknown_clients)) {
log_info("Ignoring unknown BOOTP client %s via %s",
print_hw_addr(packet->raw->htype,
packet->raw->hlen, packet->raw->chaddr),
packet->raw->giaddr.s_addr ?
inet_ntoa(packet->raw->giaddr) :
packet->interface->name);
return;
}
if (!host && !(s->group->allow_bootp)) {
log_info("Ignoring BOOTP request from client %s via "
"%s", print_hw_addr(packet->raw->htype,
packet->raw->hlen, packet->raw->chaddr),
packet->raw->giaddr.s_addr ?
inet_ntoa(packet->raw->giaddr) :
packet->interface->name);
return;
}
if (!(s->group->dynamic_bootp)) {
lose:
log_info("No applicable record for BOOTP host %s via "
"%s", print_hw_addr(packet->raw->htype,
packet->raw->hlen, packet->raw->chaddr),
packet->raw->giaddr.s_addr ?
inet_ntoa(packet->raw->giaddr) :
packet->interface->name);
return;
}
if (lease) {
if ((lease->flags & DYNAMIC_BOOTP_OK)) {
if (!(lease->flags & BOOTP_LEASE))
release_lease(lease);
lease->host = host;
ack_lease(packet, lease, 0, 0);
return;
}
release_lease(lease);
}
for (lease = s->last_lease;
lease && lease->ends <= cur_time;
lease = lease->prev) {
if ((lease->flags & DYNAMIC_BOOTP_OK)) {
lease->host = host;
ack_lease(packet, lease, 0, 0);
return;
}
}
goto lose;
}
if (hp && (!hp->group->allow_booting)) {
log_info("Ignoring excluded BOOTP client %s", hp->name);
return;
}
if (hp && (!hp->group->allow_bootp)) {
log_info("Ignoring BOOTP request from client %s", hp->name);
return;
}
memset(&outgoing, 0, sizeof outgoing);
memset(&raw, 0, sizeof raw);
outgoing.raw = &raw;
if (!packet->options_valid && !subnet->group->always_reply_rfc1048 &&
(!hp || !hp->group->always_reply_rfc1048)) {
memcpy(outgoing.raw->options, packet->raw->options,
DHCP_OPTION_LEN);
outgoing.packet_length = BOOTP_MIN_LEN;
} else {
struct tree_cache netmask_tree;
memcpy(options, subnet->group->options, sizeof(options));
for (i = 0; i < 256; i++)
if (hp->group->options[i])
options[i] = hp->group->options[i];
if (!options[DHO_SUBNET_MASK]) {
options[DHO_SUBNET_MASK] = &netmask_tree;
netmask_tree.flags = TC_TEMPORARY;
netmask_tree.value = lease->subnet->netmask.iabuf;
netmask_tree.len = lease->subnet->netmask.len;
netmask_tree.buf_size = lease->subnet->netmask.len;
netmask_tree.timeout = -1;
netmask_tree.tree = NULL;
}
outgoing.packet_length = cons_options(packet, outgoing.raw,
0, options, 0, 0, 1, NULL, 0);
if (outgoing.packet_length < BOOTP_MIN_LEN)
outgoing.packet_length = BOOTP_MIN_LEN;
}
raw.op = BOOTREPLY;
raw.htype = packet->raw->htype;
raw.hlen = packet->raw->hlen;
memcpy(raw.chaddr, packet->raw->chaddr, sizeof(raw.chaddr));
raw.hops = packet->raw->hops;
raw.xid = packet->raw->xid;
raw.secs = packet->raw->secs;
raw.flags = packet->raw->flags;
raw.ciaddr = packet->raw->ciaddr;
memcpy(&raw.yiaddr, ip_address.iabuf, sizeof(raw.yiaddr));
if (hp && hp->group->next_server.len)
memcpy(&raw.siaddr, hp->group->next_server.iabuf, 4);
else if (subnet->group->next_server.len)
memcpy(&raw.siaddr, subnet->group->next_server.iabuf, 4);
else if (subnet->interface_address.len)
memcpy(&raw.siaddr, subnet->interface_address.iabuf, 4);
else
raw.siaddr = packet->interface->primary_address;
raw.giaddr = packet->raw->giaddr;
if (hp->group->server_name)
strncpy(raw.sname, hp->group->server_name, sizeof(raw.sname));
else if (subnet->group->server_name)
strncpy(raw.sname, subnet->group->server_name,
sizeof(raw.sname));
if (hp->group->filename)
strncpy(raw.file, hp->group->filename, sizeof(raw.file));
else if (subnet->group->filename)
strncpy(raw.file, subnet->group->filename, sizeof(raw.file));
else
memcpy(raw.file, packet->raw->file, sizeof(raw.file));
from = packet->interface->primary_address;
log_info("BOOTREPLY for %s to %s (%s) via %s", piaddr(ip_address),
hp->name, print_hw_addr(packet->raw->htype, packet->raw->hlen,
packet->raw->chaddr), packet->raw->giaddr.s_addr ?
inet_ntoa(packet->raw->giaddr) : packet->interface->name);
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
to.sin_len = sizeof(to);
#endif
if (raw.giaddr.s_addr) {
to.sin_addr = raw.giaddr;
to.sin_port = server_port;
(void) packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
return;
}
else if (!(raw.flags & htons(BOOTP_BROADCAST))) {
to.sin_addr = raw.yiaddr;
to.sin_port = client_port;
} else {
to.sin_addr.s_addr = INADDR_BROADCAST;
to.sin_port = client_port;
}
errno = 0;
(void) packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
}