#include <stddef.h>
#include <stdlib.h>
#include <resolv.h>
+#include <limits.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/ip.h>
static void handle_dhcpv4(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest_addr);
static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
enum dhcpv4_msg msg, const uint8_t *mac, struct in_addr reqaddr,
const char *hostname);
-// Magic option for hnet internal (4B enterprise ID, 1B data-len, 1B subopt-code, 1B subopt-len)
-static uint8_t hnet_internal_data[7] = {0x00, 0x00, 0x76, 0xfe, 2, 1, 0};
-
// Create socket and register events
int init_dhcpv4(void)
{
INIT_LIST_HEAD(&iface->dhcpv4_assignments);
int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if (sock < 0) {
+ syslog(LOG_ERR, "Failed to create DHCPv4 server socket: %s",
+ strerror(errno));
+ return -1;
+ }
// Basic IPv6 configuration
int val = 1;
(addr.s_addr & mask.s_addr);
iface->dhcpv4_end.s_addr = htonl(end) |
(addr.s_addr & mask.s_addr);
- } else if (ntohl(mask.s_addr) <= 0xffffffc0) {
+ } else if (ntohl(mask.s_addr) <= 0xfffffff0) {
start = addr.s_addr & mask.s_addr;
end = addr.s_addr & mask.s_addr;
if (ntohl(mask.s_addr) <= 0xffffff00) {
iface->dhcpv4_start.s_addr = start | htonl(100);
iface->dhcpv4_end.s_addr = end | htonl(250);
- } else {
+ } else if (ntohl(mask.s_addr) <= 0xffffffc0) {
+ iface->dhcpv4_start.s_addr = start | htonl(10);
+ iface->dhcpv4_end.s_addr = end | htonl(60);
+ } else if (ntohl(mask.s_addr) <= 0xffffffe0) {
iface->dhcpv4_start.s_addr = start | htonl(10);
- iface->dhcpv4_end.s_addr = end | htonl(59);
+ iface->dhcpv4_end.s_addr = end | htonl(30);
+ } else {
+ iface->dhcpv4_start.s_addr = start | htonl(3);
+ iface->dhcpv4_end.s_addr = end | htonl(12);
}
}
// Construct entry
size_t hostlen = strlen(lease->hostname) + 1;
struct dhcpv4_assignment *a = calloc(1, sizeof(*a) + hostlen);
-
+ if (!a) {
+ syslog(LOG_ERR, "Calloc failed for static lease on interface %s",
+ iface->ifname);
+ return -1;
+ }
a->addr = ntohl(lease->ipaddr.s_addr);
memcpy(a->hwaddr, lease->mac.ether_addr_octet, sizeof(a->hwaddr));
memcpy(a->hostname, lease->hostname, hostlen);
+ a->valid_until = LONG_MAX;
// Assign to all interfaces
struct dhcpv4_assignment *c;
list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
if (c->addr > a->addr) {
list_add_tail(&a->head, &c->head);
+ break;
} else if (c->addr == a->addr) {
// Already an assignment with that number
break;
}
}
+ if (&c->head == &iface->dhcpv4_assignments) {
+ list_add(&a->head, &iface->dhcpv4_assignments);
+ }
if (!a->head.next)
free(a);
struct dhcpv4_assignment *a, *n;
list_for_each_entry_safe(a, n, &iface->dhcpv4_assignments, head) {
if ((htonl(a->addr) & smask->sin_addr.s_addr) !=
- (saddr->sin_addr.s_addr & smask->sin_addr.s_addr)) {
+ (iface->dhcpv4_start.s_addr & smask->sin_addr.s_addr)) {
list_del(&a->head);
free(a);
}
// Simple DHCPv6-server for information requests
static void handle_dhcpv4(void *addr, void *data, size_t len,
- struct interface *iface)
+ struct interface *iface, _unused void *dest_addr)
{
if (!iface->dhcpv4)
return;
} else if (opt->type == DHCPV4_OPT_SERVERID && opt->len == 4) {
if (memcmp(opt->data, &ifaddr.sin_addr, 4))
return;
- } else if (opt->type == DHCPV4_OPT_VENDOR_SPECIFIC_INFORMATION &&
- opt->len == sizeof(hnet_internal_data)) {
- if (!memcmp(opt->data, hnet_internal_data, sizeof(hnet_internal_data)))
- return; // Ignoring hnet internal routers
+ } else if (iface->filter_class && opt->type == DHCPV4_OPT_USER_CLASS) {
+ uint8_t *c = opt->data, *cend = &opt->data[opt->len];
+ for (; c < cend && &c[*c] < cend; c = &c[1 + *c]) {
+ size_t elen = strlen(iface->filter_class);
+ if (*c == elen && !memcmp(&c[1], iface->filter_class, elen))
+ return; // Ignore from homenet
+ }
}
}
return;
} else if (reqmsg == DHCPV4_MSG_DISCOVER) {
msg = DHCPV4_MSG_OFFER;
+ } else if (reqmsg == DHCPV4_MSG_REQUEST && reqaddr.s_addr &&
+ reqaddr.s_addr != htonl(lease->addr)) {
+ msg = DHCPV4_MSG_NAK;
+ lease = NULL;
}
if (reqmsg == DHCPV4_MSG_DECLINE || reqmsg == DHCPV4_MSG_RELEASE)
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_MTU, 2, &mtu);
}
- if (iface->search) {
- char b[256];
- if (dn_expand(iface->search, iface->search + iface->search_len,
- iface->search, b, sizeof(b)) > 0)
- dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DOMAIN, strlen(b), b);
+ if (iface->search && iface->search_len <= 255) {
+ dhcpv4_put(&reply, &cookie, DHCPV4_OPT_SEARCH_DOMAIN,
+ iface->search_len, iface->search);
} else if (!res_init() && _res.dnsrch[0] && _res.dnsrch[0][0]) {
- dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DOMAIN,
- strlen(_res.dnsrch[0]), _res.dnsrch[0]);
+ uint8_t search_buf[256];
+ int len = dn_comp(_res.dnsrch[0], search_buf,
+ sizeof(search_buf), NULL, NULL);
+ if (len > 0)
+ dhcpv4_put(&reply, &cookie, DHCPV4_OPT_SEARCH_DOMAIN,
+ len, search_buf);
}
dhcpv4_put(&reply, &cookie, DHCPV4_OPT_ROUTER, 4, &ifaddr.sin_addr);
dest.sin_addr = req->ciaddr;
dest.sin_port = htons(DHCPV4_CLIENT_PORT);
} else if ((ntohs(req->flags) & DHCPV4_FLAG_BROADCAST) ||
- req->hlen != reply.hlen) {
+ req->hlen != reply.hlen || !reply.yiaddr.s_addr) {
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(DHCPV4_CLIENT_PORT);
} else {
struct dhcpv4_assignment *c, *n, *a = NULL;
list_for_each_entry_safe(c, n, &iface->dhcpv4_assignments, head) {
- if (c->addr == raddr && !memcmp(c->hwaddr, mac, 6)) {
+ if (!memcmp(c->hwaddr, mac, 6)) {
a = c;
- break;
+ if (c->addr == raddr)
+ break;
} else if (c->valid_until < now) {
list_del(&c->head);
free(c);
if (!a && !iface->no_dynamic_dhcp) { // Create new binding
a = calloc(1, sizeof(*a) + hostlen);
+ if (!a) {
+ syslog(LOG_ERR, "Failed to calloc binding on interface %s", iface->ifname);
+ return NULL;
+ }
memcpy(a->hwaddr, mac, sizeof(a->hwaddr));
memcpy(a->hostname, hostname, hostlen);
if (assigned && !a->hostname[0] && hostname) {
a = realloc(a, sizeof(*a) + hostlen);
+ if (!a) {
+ syslog(LOG_ERR, "Failed to realloc binding on interface %s", iface->ifname);
+ return NULL;
+ }
memcpy(a->hostname, hostname, hostlen);
// Fixup list
a = NULL;
}
+ if (a)
+ update_state = true;
+
if (assigned && a)
lease = a;
} else if (msg == DHCPV4_MSG_RELEASE) {