2011-07-25 02:30:57 -04:00
|
|
|
/* dhcp.c - general DHCP protocol handling
|
2011-03-30 23:59:22 -04:00
|
|
|
*
|
2018-10-26 07:11:16 -04:00
|
|
|
* Copyright 2004-2018 Nicholas J. Kain <njkain at gmail dot com>
|
2011-07-25 02:30:57 -04:00
|
|
|
* All rights reserved.
|
2011-03-30 23:59:22 -04:00
|
|
|
*
|
2011-07-25 02:30:57 -04:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
2011-03-30 23:59:22 -04:00
|
|
|
*
|
2011-07-25 02:30:57 -04:00
|
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
2011-03-30 23:59:22 -04:00
|
|
|
*
|
2011-07-25 02:30:57 -04:00
|
|
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
2011-03-30 23:59:22 -04:00
|
|
|
*/
|
|
|
|
|
2010-12-24 09:41:52 -05:00
|
|
|
#include <stdlib.h>
|
2014-03-18 01:36:14 -04:00
|
|
|
#include <stddef.h>
|
2014-03-19 19:32:45 -04:00
|
|
|
#include <stdio.h>
|
2010-11-12 04:02:18 -05:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
2015-02-13 21:53:15 -05:00
|
|
|
#include <sys/ioctl.h>
|
2011-06-11 11:19:05 -04:00
|
|
|
#include <arpa/inet.h>
|
2010-11-12 04:02:18 -05:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <netpacket/packet.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <errno.h>
|
2014-03-30 17:02:48 -04:00
|
|
|
#include "nk/log.h"
|
|
|
|
#include "nk/io.h"
|
|
|
|
#include "nk/random.h"
|
2020-10-27 14:45:15 -04:00
|
|
|
#include "nk/net_checksum16.h"
|
2010-11-12 04:02:18 -05:00
|
|
|
|
2011-07-02 03:51:44 -04:00
|
|
|
#include "dhcp.h"
|
2011-06-29 23:47:31 -04:00
|
|
|
#include "state.h"
|
2010-12-24 10:40:46 -05:00
|
|
|
#include "arp.h"
|
2010-12-24 10:12:41 -05:00
|
|
|
#include "ifchange.h"
|
2010-12-24 09:41:52 -05:00
|
|
|
#include "sys.h"
|
2010-11-12 04:02:18 -05:00
|
|
|
#include "options.h"
|
2014-04-04 04:12:25 -04:00
|
|
|
#include "sockd.h"
|
2010-11-12 04:02:18 -05:00
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
static int get_udp_unicast_socket(struct client_state_t cs[static 1])
|
2011-06-25 10:40:41 -04:00
|
|
|
{
|
2014-04-15 17:55:28 -04:00
|
|
|
char buf[32];
|
|
|
|
buf[0] = 'u';
|
2014-04-15 20:54:35 -04:00
|
|
|
memcpy(buf + 1, &cs->clientAddr, sizeof cs->clientAddr);
|
2018-10-26 07:17:39 -04:00
|
|
|
return request_sockd_fd(buf, 1 + sizeof cs->clientAddr, (char *)0);
|
2014-04-04 04:12:25 -04:00
|
|
|
}
|
2011-06-25 10:40:41 -04:00
|
|
|
|
2014-04-04 04:12:25 -04:00
|
|
|
static int get_raw_broadcast_socket(void)
|
|
|
|
{
|
2018-10-26 07:17:39 -04:00
|
|
|
return request_sockd_fd("s", 1, (char *)0);
|
2014-04-04 04:12:25 -04:00
|
|
|
}
|
2011-06-25 10:40:41 -04:00
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
static int get_raw_listen_socket(struct client_state_t cs[static 1])
|
2011-06-25 10:40:41 -04:00
|
|
|
{
|
2014-04-04 04:12:25 -04:00
|
|
|
char resp;
|
|
|
|
int fd = request_sockd_fd("L", 1, &resp);
|
|
|
|
switch (resp) {
|
2017-01-19 05:01:23 -05:00
|
|
|
case 'L': cs->using_dhcp_bpf = true; break;
|
|
|
|
case 'l': cs->using_dhcp_bpf = false; break;
|
2014-04-04 04:12:25 -04:00
|
|
|
default: suicide("%s: (%s) expected l or L sockd reply but got %c",
|
|
|
|
client_config.interface, __func__, resp);
|
2011-06-25 10:40:41 -04:00
|
|
|
}
|
2011-06-28 23:56:12 -04:00
|
|
|
return fd;
|
|
|
|
}
|
2011-06-25 10:40:41 -04:00
|
|
|
|
2014-04-16 01:00:36 -04:00
|
|
|
// Unicast a DHCP message using a UDP socket.
|
2015-02-13 22:29:03 -05:00
|
|
|
static ssize_t send_dhcp_unicast(struct client_state_t cs[static 1],
|
2015-02-13 23:14:08 -05:00
|
|
|
struct dhcpmsg payload[static 1])
|
2011-06-28 23:56:12 -04:00
|
|
|
{
|
2014-04-06 06:24:13 -04:00
|
|
|
ssize_t ret = -1;
|
2014-04-15 17:55:28 -04:00
|
|
|
int fd = get_udp_unicast_socket(cs);
|
|
|
|
if (fd < 0) {
|
|
|
|
log_error("%s: (%s) get_udp_unicast_socket failed",
|
|
|
|
client_config.interface, __func__);
|
2011-06-28 23:56:12 -04:00
|
|
|
goto out;
|
2014-04-15 17:55:28 -04:00
|
|
|
}
|
2011-06-28 23:56:12 -04:00
|
|
|
|
|
|
|
struct sockaddr_in raddr = {
|
|
|
|
.sin_family = AF_INET,
|
|
|
|
.sin_port = htons(DHCP_SERVER_PORT),
|
2011-07-03 18:56:57 -04:00
|
|
|
.sin_addr.s_addr = cs->serverAddr,
|
2011-06-28 23:56:12 -04:00
|
|
|
};
|
2014-04-06 06:33:14 -04:00
|
|
|
if (connect(fd, (struct sockaddr *)&raddr, sizeof(struct sockaddr)) < 0) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_error("%s: (%s) connect failed: %s", client_config.interface,
|
|
|
|
__func__, strerror(errno));
|
2011-06-25 16:31:21 -04:00
|
|
|
goto out_fd;
|
2011-06-25 10:46:24 -04:00
|
|
|
}
|
2011-06-28 23:56:12 -04:00
|
|
|
|
2011-07-27 07:49:30 -04:00
|
|
|
// Send packets that are as short as possible.
|
2011-06-28 23:56:12 -04:00
|
|
|
ssize_t endloc = get_end_option_idx(payload);
|
|
|
|
if (endloc < 0) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_error("%s: (%s) No end marker. Not sending.",
|
|
|
|
client_config.interface, __func__);
|
2011-06-25 16:31:21 -04:00
|
|
|
goto out_fd;
|
2011-06-25 10:40:41 -04:00
|
|
|
}
|
2018-02-09 02:39:46 -05:00
|
|
|
const size_t el = (size_t)endloc + 1;
|
|
|
|
if (el > sizeof payload->options) {
|
|
|
|
log_error("%s: (%s) Invalid value of endloc. Not sending.",
|
|
|
|
client_config.interface, __func__);
|
|
|
|
goto out_fd;
|
|
|
|
}
|
2011-06-28 23:56:12 -04:00
|
|
|
size_t payload_len =
|
2018-02-09 02:39:46 -05:00
|
|
|
sizeof *payload - (sizeof payload->options - el);
|
2017-01-12 05:25:15 -05:00
|
|
|
if (!carrier_isup()) {
|
2015-02-13 21:53:15 -05:00
|
|
|
log_error("%s: (%s) carrier down; write would fail",
|
|
|
|
client_config.interface, __func__);
|
2015-02-14 05:20:04 -05:00
|
|
|
ret = -99;
|
2015-02-13 21:53:15 -05:00
|
|
|
goto out_fd;
|
|
|
|
}
|
2011-06-28 23:56:12 -04:00
|
|
|
ret = safe_write(fd, (const char *)payload, payload_len);
|
2014-04-04 04:01:49 -04:00
|
|
|
if (ret < 0 || (size_t)ret != payload_len)
|
|
|
|
log_error("%s: (%s) write failed: %d", client_config.interface,
|
|
|
|
__func__, ret);
|
2011-06-28 23:56:12 -04:00
|
|
|
out_fd:
|
2011-06-25 16:31:21 -04:00
|
|
|
close(fd);
|
2011-06-28 23:56:12 -04:00
|
|
|
out:
|
|
|
|
return ret;
|
2011-06-25 10:40:41 -04:00
|
|
|
}
|
2011-06-26 17:37:57 -04:00
|
|
|
|
2011-06-26 16:33:07 -04:00
|
|
|
// Returns 1 if IP checksum is correct, otherwise 0.
|
2015-02-13 23:14:08 -05:00
|
|
|
static int ip_checksum(struct ip_udp_dhcp_packet packet[static 1])
|
2011-06-26 16:33:07 -04:00
|
|
|
{
|
2020-10-27 14:45:15 -04:00
|
|
|
return net_checksum16(&packet->ip, sizeof packet->ip) == 0;
|
2011-06-26 16:33:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns 1 if UDP checksum is correct, otherwise 0.
|
2015-02-13 23:14:08 -05:00
|
|
|
static int udp_checksum(struct ip_udp_dhcp_packet packet[static 1])
|
2011-06-26 16:33:07 -04:00
|
|
|
{
|
|
|
|
struct iphdr ph = {
|
|
|
|
.saddr = packet->ip.saddr,
|
|
|
|
.daddr = packet->ip.daddr,
|
|
|
|
.protocol = packet->ip.protocol,
|
|
|
|
.tot_len = packet->udp.len,
|
|
|
|
};
|
2015-01-06 07:07:08 -05:00
|
|
|
uint16_t udpcs =
|
2020-10-27 14:45:15 -04:00
|
|
|
net_checksum16(&packet->udp,
|
2015-01-06 07:07:08 -05:00
|
|
|
min_size_t(ntohs(packet->udp.len),
|
|
|
|
sizeof *packet - sizeof(struct iphdr)));
|
2020-10-27 14:45:15 -04:00
|
|
|
uint16_t hdrcs = net_checksum16(&ph, sizeof ph);
|
|
|
|
uint16_t cs = net_checksum16_add(udpcs, hdrcs);
|
2011-06-26 16:33:07 -04:00
|
|
|
return cs == 0;
|
2011-06-25 16:55:00 -04:00
|
|
|
}
|
|
|
|
|
2015-02-13 23:14:08 -05:00
|
|
|
static int get_raw_packet_validate_bpf(struct ip_udp_dhcp_packet packet[static 1])
|
2011-07-01 04:14:10 -04:00
|
|
|
{
|
|
|
|
if (packet->ip.version != IPVERSION) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: IP version is not IPv4.", client_config.interface);
|
2011-07-01 04:14:10 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (packet->ip.ihl != sizeof packet->ip >> 2) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: IP header length incorrect.",
|
|
|
|
client_config.interface);
|
2011-07-01 04:14:10 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (packet->ip.protocol != IPPROTO_UDP) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: IP header is not UDP: %d",
|
|
|
|
client_config.interface, packet->ip.protocol);
|
2011-07-01 04:14:10 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (ntohs(packet->udp.dest) != DHCP_CLIENT_PORT) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: UDP destination port incorrect: %d",
|
|
|
|
client_config.interface, ntohs(packet->udp.dest));
|
2011-07-01 04:14:10 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (ntohs(packet->udp.len) !=
|
|
|
|
ntohs(packet->ip.tot_len) - sizeof packet->ip) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: UDP header length incorrect.",
|
|
|
|
client_config.interface);
|
2011-07-01 04:14:10 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-06-11 11:05:53 -04:00
|
|
|
// Read a packet from a raw socket. Returns -1 on fatal error, -2 on
|
|
|
|
// transient error.
|
2015-02-13 22:29:03 -05:00
|
|
|
static ssize_t get_raw_packet(struct client_state_t cs[static 1],
|
2015-02-13 23:14:08 -05:00
|
|
|
struct dhcpmsg payload[static 1],
|
2015-02-12 23:28:54 -05:00
|
|
|
uint32_t *srcaddr)
|
2011-06-11 11:05:53 -04:00
|
|
|
{
|
|
|
|
struct ip_udp_dhcp_packet packet;
|
2011-06-26 17:25:00 -04:00
|
|
|
memset(&packet, 0, sizeof packet);
|
2011-06-26 16:33:07 -04:00
|
|
|
|
2011-07-01 04:38:38 -04:00
|
|
|
ssize_t inc = safe_read(cs->listenFd, (char *)&packet, sizeof packet);
|
2014-04-06 06:24:13 -04:00
|
|
|
if (inc < 0) {
|
2011-06-11 11:05:53 -04:00
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
|
|
|
return -2;
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: (%s) read error %s", client_config.interface,
|
|
|
|
__func__, strerror(errno));
|
2011-06-11 11:05:53 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2014-04-15 15:23:52 -04:00
|
|
|
size_t iphdrlen = ntohs(packet.ip.tot_len);
|
2015-02-14 01:40:06 -05:00
|
|
|
if ((size_t)inc < iphdrlen)
|
2012-07-20 18:48:26 -04:00
|
|
|
return -2;
|
2011-07-01 04:14:10 -04:00
|
|
|
if (!cs->using_dhcp_bpf && !get_raw_packet_validate_bpf(&packet))
|
2011-06-11 11:05:53 -04:00
|
|
|
return -2;
|
2011-07-01 04:14:10 -04:00
|
|
|
|
2011-06-26 16:33:07 -04:00
|
|
|
if (!ip_checksum(&packet)) {
|
2014-07-25 20:34:01 -04:00
|
|
|
log_error("%s: IP header checksum incorrect.",
|
|
|
|
client_config.interface);
|
2011-06-11 11:05:53 -04:00
|
|
|
return -2;
|
|
|
|
}
|
2014-07-25 20:34:01 -04:00
|
|
|
if (iphdrlen <= sizeof packet.ip + sizeof packet.udp) {
|
|
|
|
log_error("%s: Packet received that is too small (%zu bytes).",
|
|
|
|
iphdrlen);
|
|
|
|
return -2;
|
|
|
|
}
|
2014-04-15 15:23:52 -04:00
|
|
|
size_t l = iphdrlen - sizeof packet.ip - sizeof packet.udp;
|
2014-07-25 20:34:01 -04:00
|
|
|
if (l > sizeof *payload) {
|
2018-02-09 02:39:46 -05:00
|
|
|
log_error("%s: Packet received that is too long (%zu bytes).", l);
|
2014-07-25 20:34:01 -04:00
|
|
|
return -2;
|
2015-01-06 04:32:58 -05:00
|
|
|
}
|
|
|
|
if (packet.udp.check && !udp_checksum(&packet)) {
|
|
|
|
log_error("%s: Packet with bad UDP checksum received. Ignoring.",
|
|
|
|
client_config.interface);
|
|
|
|
return -2;
|
2014-07-25 20:34:01 -04:00
|
|
|
}
|
2015-02-12 23:28:54 -05:00
|
|
|
if (srcaddr)
|
|
|
|
*srcaddr = packet.ip.saddr;
|
2011-06-26 17:25:00 -04:00
|
|
|
memcpy(payload, &packet.data, l);
|
2018-02-09 02:39:46 -05:00
|
|
|
return (ssize_t)l;
|
2011-06-11 11:05:53 -04:00
|
|
|
}
|
|
|
|
|
2011-06-28 23:56:12 -04:00
|
|
|
// Broadcast a DHCP message using a raw socket.
|
2015-02-13 23:14:08 -05:00
|
|
|
static ssize_t send_dhcp_raw(struct dhcpmsg payload[static 1])
|
2011-06-28 23:56:12 -04:00
|
|
|
{
|
2014-04-06 06:24:13 -04:00
|
|
|
ssize_t ret = -1;
|
2014-04-04 04:12:25 -04:00
|
|
|
int fd = get_raw_broadcast_socket();
|
2014-04-15 17:55:28 -04:00
|
|
|
if (fd < 0) {
|
|
|
|
log_error("%s: (%s) get_raw_broadcast_socket failed",
|
|
|
|
client_config.interface, __func__);
|
2011-06-28 23:56:12 -04:00
|
|
|
return ret;
|
2014-04-15 17:55:28 -04:00
|
|
|
}
|
2010-11-12 14:33:17 -05:00
|
|
|
|
2011-07-27 07:49:30 -04:00
|
|
|
// Send packets that are as short as possible.
|
2011-06-27 12:53:35 -04:00
|
|
|
ssize_t endloc = get_end_option_idx(payload);
|
2011-06-26 17:25:00 -04:00
|
|
|
if (endloc < 0) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_error("%s: (%s) No end marker. Not sending.",
|
|
|
|
client_config.interface, __func__);
|
2011-06-28 23:56:12 -04:00
|
|
|
close(fd);
|
|
|
|
return ret;
|
2011-03-30 15:57:01 -04:00
|
|
|
}
|
2018-02-09 02:39:46 -05:00
|
|
|
const size_t el = (size_t)endloc + 1;
|
|
|
|
if (el > sizeof payload->options) {
|
|
|
|
log_error("%s: (%s) Invalid value of endloc. Not sending.",
|
|
|
|
client_config.interface, __func__);
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
size_t padding = sizeof payload->options - el;
|
2011-06-26 17:25:00 -04:00
|
|
|
size_t iud_len = sizeof(struct ip_udp_dhcp_packet) - padding;
|
|
|
|
size_t ud_len = sizeof(struct udp_dhcp_packet) - padding;
|
2011-06-26 17:50:35 -04:00
|
|
|
|
|
|
|
struct iphdr ph = {
|
|
|
|
.saddr = INADDR_ANY,
|
|
|
|
.daddr = INADDR_BROADCAST,
|
|
|
|
.protocol = IPPROTO_UDP,
|
|
|
|
.tot_len = htons(ud_len),
|
|
|
|
};
|
2011-06-26 17:25:00 -04:00
|
|
|
struct ip_udp_dhcp_packet iudmsg = {
|
2011-06-26 10:21:02 -04:00
|
|
|
.ip = {
|
|
|
|
.saddr = INADDR_ANY,
|
|
|
|
.daddr = INADDR_BROADCAST,
|
2011-06-26 17:50:35 -04:00
|
|
|
.protocol = IPPROTO_UDP,
|
|
|
|
.tot_len = htons(iud_len),
|
|
|
|
.ihl = sizeof iudmsg.ip >> 2,
|
|
|
|
.version = IPVERSION,
|
|
|
|
.ttl = IPDEFTTL,
|
2011-06-26 10:21:02 -04:00
|
|
|
},
|
|
|
|
.data = *payload,
|
|
|
|
};
|
2014-04-04 04:12:25 -04:00
|
|
|
iudmsg.udp.source = htons(DHCP_CLIENT_PORT);
|
|
|
|
iudmsg.udp.dest = htons(DHCP_SERVER_PORT);
|
|
|
|
iudmsg.udp.len = htons(ud_len);
|
|
|
|
iudmsg.udp.check = 0;
|
2020-10-27 14:45:15 -04:00
|
|
|
uint16_t udpcs = net_checksum16(&iudmsg.udp, ud_len);
|
|
|
|
uint16_t phcs = net_checksum16(&ph, sizeof ph);
|
|
|
|
iudmsg.udp.check = net_checksum16_add(udpcs, phcs);
|
|
|
|
iudmsg.ip.check = net_checksum16(&iudmsg.ip, sizeof iudmsg.ip);
|
2011-06-26 17:25:00 -04:00
|
|
|
|
2014-04-04 04:12:25 -04:00
|
|
|
struct sockaddr_ll da = {
|
|
|
|
.sll_family = AF_PACKET,
|
|
|
|
.sll_protocol = htons(ETH_P_IP),
|
|
|
|
.sll_pkttype = PACKET_BROADCAST,
|
|
|
|
.sll_ifindex = client_config.ifindex,
|
|
|
|
.sll_halen = 6,
|
|
|
|
};
|
|
|
|
memcpy(da.sll_addr, "\xff\xff\xff\xff\xff\xff", 6);
|
2017-01-12 05:25:15 -05:00
|
|
|
if (!carrier_isup()) {
|
2015-02-13 21:53:15 -05:00
|
|
|
log_error("%s: (%s) carrier down; sendto would fail",
|
|
|
|
client_config.interface, __func__);
|
2015-02-14 05:20:04 -05:00
|
|
|
ret = -99;
|
2015-02-13 21:53:15 -05:00
|
|
|
goto carrier_down;
|
|
|
|
}
|
2011-06-28 23:56:12 -04:00
|
|
|
ret = safe_sendto(fd, (const char *)&iudmsg, iud_len, 0,
|
|
|
|
(struct sockaddr *)&da, sizeof da);
|
2014-04-07 04:15:02 -04:00
|
|
|
if (ret < 0 || (size_t)ret != iud_len) {
|
|
|
|
if (ret < 0)
|
|
|
|
log_error("%s: (%s) sendto failed: %s", client_config.interface,
|
|
|
|
__func__, strerror(errno));
|
|
|
|
else
|
|
|
|
log_error("%s: (%s) sendto short write: %z < %zu",
|
|
|
|
client_config.interface, __func__, ret, iud_len);
|
|
|
|
}
|
2015-02-13 21:53:15 -05:00
|
|
|
carrier_down:
|
2010-11-12 14:33:17 -05:00
|
|
|
close(fd);
|
2011-06-28 23:56:12 -04:00
|
|
|
return ret;
|
2010-11-12 14:33:17 -05:00
|
|
|
}
|
2010-12-24 08:55:59 -05:00
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
void start_dhcp_listen(struct client_state_t cs[static 1])
|
2010-12-24 09:41:52 -05:00
|
|
|
{
|
2014-04-16 01:00:36 -04:00
|
|
|
if (cs->listenFd >= 0)
|
|
|
|
return;
|
|
|
|
cs->listenFd = get_raw_listen_socket(cs);
|
2014-03-30 17:21:27 -04:00
|
|
|
if (cs->listenFd < 0)
|
2014-04-04 04:01:49 -04:00
|
|
|
suicide("%s: FATAL: Couldn't listen on socket: %s",
|
|
|
|
client_config.interface, strerror(errno));
|
2010-12-24 09:41:52 -05:00
|
|
|
}
|
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
void stop_dhcp_listen(struct client_state_t cs[static 1])
|
2011-06-30 21:33:38 -04:00
|
|
|
{
|
2014-04-16 01:00:36 -04:00
|
|
|
if (cs->listenFd < 0)
|
|
|
|
return;
|
|
|
|
close(cs->listenFd);
|
|
|
|
cs->listenFd = -1;
|
2011-06-30 21:33:38 -04:00
|
|
|
}
|
|
|
|
|
2015-02-13 23:14:08 -05:00
|
|
|
static int validate_dhcp_packet(struct client_state_t cs[static 1],
|
|
|
|
size_t len, struct dhcpmsg packet[static 1],
|
2015-02-14 01:46:02 -05:00
|
|
|
uint8_t msgtype[static 1])
|
2011-07-01 05:33:12 -04:00
|
|
|
{
|
2014-03-18 01:36:14 -04:00
|
|
|
if (len < offsetof(struct dhcpmsg, options)) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: Packet is too short to contain magic cookie. Ignoring.",
|
|
|
|
client_config.interface);
|
2011-07-01 05:33:12 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (ntohl(packet->cookie) != DHCP_MAGIC) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: Packet with bad magic number. Ignoring.",
|
|
|
|
client_config.interface);
|
2011-07-01 05:33:12 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (packet->xid != cs->xid) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: Packet XID %lx does not equal our XID %lx. Ignoring.",
|
|
|
|
client_config.interface, packet->xid, cs->xid);
|
2011-07-01 05:33:12 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2011-09-01 23:05:56 -04:00
|
|
|
if (memcmp(packet->chaddr, client_config.arp, sizeof client_config.arp)) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: Packet client MAC %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x does not equal our MAC %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x. Ignoring it.",
|
|
|
|
client_config.interface,
|
|
|
|
packet->chaddr[0], packet->chaddr[1], packet->chaddr[2],
|
|
|
|
packet->chaddr[3], packet->chaddr[4], packet->chaddr[5],
|
|
|
|
client_config.arp[0], client_config.arp[1],
|
|
|
|
client_config.arp[2], client_config.arp[3],
|
|
|
|
client_config.arp[4], client_config.arp[5]);
|
2011-09-01 23:05:56 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2015-01-06 04:02:52 -05:00
|
|
|
ssize_t endloc = get_end_option_idx(packet);
|
|
|
|
if (endloc < 0) {
|
|
|
|
log_warning("%s: Packet does not have an end option. Ignoring.");
|
|
|
|
return 0;
|
|
|
|
}
|
2011-07-26 01:04:59 -04:00
|
|
|
*msgtype = get_option_msgtype(packet);
|
|
|
|
if (!*msgtype) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: Packet does not specify a DHCP message type. Ignoring.",
|
|
|
|
client_config.interface);
|
2011-07-01 05:33:12 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2014-03-18 03:13:51 -04:00
|
|
|
char clientid[MAX_DOPT_SIZE];
|
|
|
|
size_t cidlen = get_option_clientid(packet, clientid, MAX_DOPT_SIZE);
|
|
|
|
if (cidlen == 0)
|
|
|
|
return 1;
|
|
|
|
if (memcmp(client_config.clientid, clientid,
|
|
|
|
min_size_t(cidlen, client_config.clientid_len))) {
|
2014-04-04 04:01:49 -04:00
|
|
|
log_warning("%s: Packet clientid does not match our clientid. Ignoring.",
|
|
|
|
client_config.interface);
|
2014-03-18 03:13:51 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2011-07-01 05:33:12 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-02-18 11:02:13 -05:00
|
|
|
bool dhcp_packet_get(struct client_state_t cs[static 1],
|
|
|
|
struct dhcpmsg packet[static 1],
|
|
|
|
uint8_t msgtype[static 1],
|
|
|
|
uint32_t srcaddr[static 1])
|
2010-12-24 08:55:59 -05:00
|
|
|
{
|
2014-04-16 01:00:36 -04:00
|
|
|
if (cs->listenFd < 0)
|
2015-02-18 11:02:13 -05:00
|
|
|
return false;
|
2015-02-15 06:38:03 -05:00
|
|
|
ssize_t r = get_raw_packet(cs, packet, srcaddr);
|
2014-03-10 18:58:53 -04:00
|
|
|
if (r < 0) {
|
2014-04-16 00:24:40 -04:00
|
|
|
// Not a transient issue handled by packet collection functions.
|
|
|
|
if (r != -2) {
|
|
|
|
log_error("%s: Error reading from listening socket: %s. Reopening.",
|
|
|
|
client_config.interface, strerror(errno));
|
2014-04-16 01:00:36 -04:00
|
|
|
stop_dhcp_listen(cs);
|
|
|
|
start_dhcp_listen(cs);
|
2014-04-16 00:24:40 -04:00
|
|
|
}
|
2015-02-18 11:02:13 -05:00
|
|
|
return false;
|
2010-12-24 08:55:59 -05:00
|
|
|
}
|
2015-02-15 06:38:03 -05:00
|
|
|
if (!validate_dhcp_packet(cs, (size_t)r, packet, msgtype))
|
2015-02-18 11:02:13 -05:00
|
|
|
return false;
|
|
|
|
return true;
|
2010-12-24 08:55:59 -05:00
|
|
|
}
|
2011-06-11 11:19:05 -04:00
|
|
|
|
2015-02-20 03:58:25 -05:00
|
|
|
static void add_options_vendor_hostname(struct dhcpmsg packet[static 1])
|
|
|
|
{
|
|
|
|
size_t vlen = strlen(client_config.vendor);
|
|
|
|
size_t hlen = strlen(client_config.hostname);
|
|
|
|
if (vlen)
|
|
|
|
add_option_vendor(packet, client_config.vendor, vlen);
|
|
|
|
else
|
|
|
|
add_option_vendor(packet, "ndhc", sizeof "ndhc" - 1);
|
|
|
|
add_option_hostname(packet, client_config.hostname, hlen);
|
|
|
|
}
|
|
|
|
|
2011-06-27 13:01:39 -04:00
|
|
|
// Initialize a DHCP client packet that will be sent to a server
|
2018-02-09 02:39:46 -05:00
|
|
|
static void init_packet(struct dhcpmsg packet[static 1], uint8_t type)
|
2011-06-11 11:19:05 -04:00
|
|
|
{
|
2015-02-14 01:41:52 -05:00
|
|
|
packet->op = 1; // BOOTREQUEST (client)
|
|
|
|
packet->htype = 1; // ETH_10MB
|
|
|
|
packet->hlen = 6; // ETH_10MB_LEN
|
|
|
|
packet->cookie = htonl(DHCP_MAGIC);
|
|
|
|
packet->options[0] = DCODE_END;
|
|
|
|
add_option_msgtype(packet, type);
|
|
|
|
memcpy(packet->chaddr, client_config.arp, 6);
|
|
|
|
add_option_clientid(packet, client_config.clientid,
|
2014-03-18 03:13:51 -04:00
|
|
|
client_config.clientid_len);
|
2011-06-11 11:19:05 -04:00
|
|
|
}
|
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
ssize_t send_discover(struct client_state_t cs[static 1])
|
2011-06-11 11:19:05 -04:00
|
|
|
{
|
2015-02-14 01:41:52 -05:00
|
|
|
struct dhcpmsg packet = {.xid = cs->xid};
|
|
|
|
init_packet(&packet, DHCPDISCOVER);
|
2011-07-01 11:37:13 -04:00
|
|
|
if (cs->clientAddr)
|
2011-07-25 23:34:32 -04:00
|
|
|
add_option_reqip(&packet, cs->clientAddr);
|
|
|
|
add_option_maxsize(&packet);
|
2011-06-27 12:53:35 -04:00
|
|
|
add_option_request_list(&packet);
|
2015-02-20 03:58:25 -05:00
|
|
|
add_options_vendor_hostname(&packet);
|
2014-04-04 04:01:49 -04:00
|
|
|
log_line("%s: Discovering DHCP servers...", client_config.interface);
|
2011-06-25 16:55:00 -04:00
|
|
|
return send_dhcp_raw(&packet);
|
2011-06-11 11:19:05 -04:00
|
|
|
}
|
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
ssize_t send_selecting(struct client_state_t cs[static 1])
|
2011-06-11 11:19:05 -04:00
|
|
|
{
|
2011-07-11 17:02:32 -04:00
|
|
|
char clibuf[INET_ADDRSTRLEN];
|
2015-02-14 01:41:52 -05:00
|
|
|
struct dhcpmsg packet = {.xid = cs->xid};
|
|
|
|
init_packet(&packet, DHCPREQUEST);
|
2011-07-25 23:34:32 -04:00
|
|
|
add_option_reqip(&packet, cs->clientAddr);
|
|
|
|
add_option_serverid(&packet, cs->serverAddr);
|
|
|
|
add_option_maxsize(&packet);
|
2011-06-27 12:53:35 -04:00
|
|
|
add_option_request_list(&packet);
|
2015-02-20 03:58:25 -05:00
|
|
|
add_options_vendor_hostname(&packet);
|
2011-07-11 17:02:32 -04:00
|
|
|
inet_ntop(AF_INET, &(struct in_addr){.s_addr = cs->clientAddr},
|
|
|
|
clibuf, sizeof clibuf);
|
2014-04-04 04:01:49 -04:00
|
|
|
log_line("%s: Sending a selection request for %s...",
|
|
|
|
client_config.interface, clibuf);
|
2011-06-25 16:55:00 -04:00
|
|
|
return send_dhcp_raw(&packet);
|
2011-06-11 11:19:05 -04:00
|
|
|
}
|
|
|
|
|
2020-10-19 05:26:47 -04:00
|
|
|
ssize_t send_renew_or_rebind(struct client_state_t cs[static 1], bool is_renew)
|
2011-06-11 11:19:05 -04:00
|
|
|
{
|
2015-02-14 01:41:52 -05:00
|
|
|
struct dhcpmsg packet = {.xid = cs->xid};
|
|
|
|
init_packet(&packet, DHCPREQUEST);
|
2011-07-01 11:37:13 -04:00
|
|
|
packet.ciaddr = cs->clientAddr;
|
2011-07-25 23:34:32 -04:00
|
|
|
add_option_maxsize(&packet);
|
2011-06-27 12:53:35 -04:00
|
|
|
add_option_request_list(&packet);
|
2015-02-20 03:58:25 -05:00
|
|
|
add_options_vendor_hostname(&packet);
|
2020-10-19 05:26:47 -04:00
|
|
|
log_line("%s: Sending a %s request...", client_config.interface,
|
|
|
|
is_renew? "renew" : "rebind");
|
|
|
|
return is_renew? send_dhcp_unicast(cs, &packet) : send_dhcp_raw(&packet);
|
2011-06-11 11:19:05 -04:00
|
|
|
}
|
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
ssize_t send_decline(struct client_state_t cs[static 1], uint32_t server)
|
2011-06-11 11:19:05 -04:00
|
|
|
{
|
2015-02-14 01:41:52 -05:00
|
|
|
struct dhcpmsg packet = {.xid = cs->xid};
|
|
|
|
init_packet(&packet, DHCPDECLINE);
|
2011-07-25 23:34:32 -04:00
|
|
|
add_option_reqip(&packet, cs->clientAddr);
|
|
|
|
add_option_serverid(&packet, server);
|
2014-04-04 04:01:49 -04:00
|
|
|
log_line("%s: Sending a decline message...", client_config.interface);
|
2011-06-25 16:55:00 -04:00
|
|
|
return send_dhcp_raw(&packet);
|
2011-06-11 11:19:05 -04:00
|
|
|
}
|
|
|
|
|
2015-02-13 22:29:03 -05:00
|
|
|
ssize_t send_release(struct client_state_t cs[static 1])
|
2011-06-11 11:19:05 -04:00
|
|
|
{
|
2017-08-24 02:36:31 -04:00
|
|
|
struct dhcpmsg packet = {.xid = nk_random_u32(&cs->rnd_state)};
|
2015-02-14 01:41:52 -05:00
|
|
|
init_packet(&packet, DHCPRELEASE);
|
2011-07-01 11:37:13 -04:00
|
|
|
packet.ciaddr = cs->clientAddr;
|
2011-07-25 23:34:32 -04:00
|
|
|
add_option_reqip(&packet, cs->clientAddr);
|
|
|
|
add_option_serverid(&packet, cs->serverAddr);
|
2014-04-04 04:01:49 -04:00
|
|
|
log_line("%s: Sending a release message...", client_config.interface);
|
2014-04-16 01:00:36 -04:00
|
|
|
return send_dhcp_unicast(cs, &packet);
|
2011-06-11 11:19:05 -04:00
|
|
|
}
|
2011-06-25 16:55:00 -04:00
|
|
|
|