static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info);
static int setup_dhcpv4_addresses(struct interface *iface);
static void update_static_assignments(struct interface *iface);
+static bool addr_is_fr_ip(struct interface *iface, struct in_addr *addr);
static void valid_until_cb(struct uloop_timeout *event);
static void handle_addrlist_change(struct interface *iface);
static void free_dhcpv4_assignment(struct dhcpv4_assignment *a);
static void dhcpv4_fr_start(struct dhcpv4_assignment *a);
+static void dhcpv4_fr_rand_delay(struct dhcpv4_assignment *a);
static void dhcpv4_fr_stop(struct dhcpv4_assignment *a);
static void handle_dhcpv4(void *addr, void *data, size_t len,
struct interface *iface, void *dest_addr);
int dhcpv4_setup_interface(struct interface *iface, bool enable)
{
+ int ret = 0;
+
if (iface->dhcpv4_event.uloop.fd > 0) {
uloop_fd_delete(&iface->dhcpv4_event.uloop);
close(iface->dhcpv4_event.uloop.fd);
}
if (iface->dhcpv4 && enable) {
+ struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT),
+ {INADDR_ANY}, {0}};
+ int val = 1;
+
if (!iface->dhcpv4_assignments.next)
INIT_LIST_HEAD(&iface->dhcpv4_assignments);
if (!iface->dhcpv4_fr_ips.next)
INIT_LIST_HEAD(&iface->dhcpv4_fr_ips);
- 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;
+ iface->dhcpv4_event.uloop.fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if (iface->dhcpv4_event.uloop.fd < 0) {
+ syslog(LOG_ERR, "socket(AF_INET): %m");
+ ret = -1;
+ goto out;
}
/* Basic IPv4 configuration */
- int val = 1;
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
- setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val));
- setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val));
+ if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_REUSEADDR,
+ &val, sizeof(val)) < 0) {
+ syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
+ ret = -1;
+ goto out;
+ }
+
+ if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_BROADCAST,
+ &val, sizeof(val)) < 0) {
+ syslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m");
+ ret = -1;
+ goto out;
+ }
+
+ if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_PKTINFO,
+ &val, sizeof(val)) < 0) {
+ syslog(LOG_ERR, "setsockopt(IP_PKTINFO): %m");
+ ret = -1;
+ goto out;
+ }
val = IPTOS_PREC_INTERNETCONTROL;
- setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
+ if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_TOS,
+ &val, sizeof(val)) < 0) {
+ syslog(LOG_ERR, "setsockopt(IP_TOS): %m");
+ ret = -1;
+ goto out;
+ }
val = IP_PMTUDISC_DONT;
- setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));
+ if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_MTU_DISCOVER,
+ &val, sizeof(val)) < 0) {
+ syslog(LOG_ERR, "setsockopt(IP_MTU_DISCOVER): %m");
+ ret = -1;
+ goto out;
+ }
- setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
- iface->ifname, strlen(iface->ifname));
+ if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_BINDTODEVICE,
+ iface->ifname, strlen(iface->ifname)) < 0) {
+ syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m");
+ ret = -1;
+ goto out;
+ }
- struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT),
- {INADDR_ANY}, {0}};
+ if (bind(iface->dhcpv4_event.uloop.fd, (struct sockaddr*)&bind_addr,
+ sizeof(bind_addr)) < 0) {
+ syslog(LOG_ERR, "bind(): %m");
+ ret = -1;
+ goto out;
+ }
- if (bind(sock, (struct sockaddr*)&bind_addr, sizeof(bind_addr))) {
- syslog(LOG_ERR, "Failed to open DHCPv4 server socket: %s",
- strerror(errno));
- return -1;
+ if (setup_dhcpv4_addresses(iface) < 0) {
+ ret = -1;
+ goto out;
}
- iface->dhcpv4_event.uloop.fd = sock;
iface->dhcpv4_event.handle_dgram = handle_dhcpv4;
odhcpd_register(&iface->dhcpv4_event);
- if (setup_dhcpv4_addresses(iface) < 0)
- return -1;
-
update_static_assignments(iface);
} else if (iface->dhcpv4_assignments.next) {
while (!list_empty(&iface->dhcpv4_assignments))
free_dhcpv4_assignment(list_first_entry(&iface->dhcpv4_assignments,
struct dhcpv4_assignment, head));
}
- return 0;
+
+out:
+ if (ret < 0 && iface->dhcpv4_event.uloop.fd > 0) {
+ close(iface->dhcpv4_event.uloop.fd);
+ iface->dhcpv4_event.uloop.fd = -1;
+ }
+
+ return ret;
}
if (iface->dhcpv4_start.s_addr & htonl(0xffff0000) ||
iface->dhcpv4_end.s_addr & htonl(0xffff0000) ||
ntohl(iface->dhcpv4_start.s_addr) > ntohl(iface->dhcpv4_end.s_addr)) {
- syslog(LOG_ERR, "invalid DHCP range for %s", iface->name);
+ syslog(LOG_ERR, "Invalid DHCP range for %s", iface->name);
return -1;
}
if (!iface->addr4_len) {
- syslog(LOG_WARNING, "no network(s) available on %s", iface->name);
+ syslog(LOG_WARNING, "No network(s) available on %s", iface->name);
return -1;
}
struct in_addr *addr = &iface->addr4[i].addr.in;
struct in_addr mask;
+ if (addr_is_fr_ip(iface, addr))
+ continue;
+
odhcpd_bitlen2netmask(false, iface->addr4[i].prefix, &mask);
if ((start & ntohl(~mask.s_addr)) == start &&
(end & ntohl(~mask.s_addr)) == end) {
/* Don't allocate IP range for subnets bigger than 28 */
if (iface->addr4[0].prefix > 28) {
- syslog(LOG_WARNING, "auto allocation of DHCP range fails on %s", iface->name);
+ syslog(LOG_WARNING, "Auto allocation of DHCP range fails on %s", iface->name);
return -1;
}
/* Construct entry */
a = calloc(1, sizeof(*a));
if (!a) {
- syslog(LOG_ERR, "Calloc failed for static lease on interface %s",
- iface->ifname);
+ syslog(LOG_ERR, "Calloc failed for static lease on %s", iface->name);
continue;
}
memcpy(a->hwaddr, lease->mac.ether_addr_octet, sizeof(a->hwaddr));
*ptr = NULL;
}
+static bool addr_is_fr_ip(struct interface *iface, struct in_addr *addr)
+{
+ struct odhcpd_ref_ip *p;
+
+ list_for_each_entry(p, &iface->dhcpv4_fr_ips, head) {
+ if (addr->s_addr == p->addr.addr.in.s_addr)
+ return true;
+ }
+
+ return false;
+}
+
static bool leases_require_fr(struct interface *iface, struct odhcpd_ipaddr *addr,
uint32_t mask)
{
a = list_first_entry(&iface->dhcpv4_fr_ips, struct odhcpd_ref_ip, head);
if (netlink_setup_addr(&a->addr, iface->ifindex, false, true)) {
- syslog(LOG_ERR, "Failed to add ip address");
+ syslog(LOG_ERR, "Failed to add ip address on %s", iface->name);
return;
}
list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
if ((c->flags & OAF_BOUND) && c->fr_ip && !c->fr_cnt) {
if (c->accept_fr_nonce || iface->dhcpv4_forcereconf)
- dhcpv4_fr_start(c);
+ dhcpv4_fr_rand_delay(c);
else
dhcpv4_fr_stop(c);
}
uint8_t type, uint8_t len, const void *data)
{
uint8_t *c = *cookie;
- if (*cookie + 2 + len > (uint8_t*)&msg[1])
+ uint8_t *end = (uint8_t *)msg + sizeof(*msg);
+
+ if (*cookie + 2 + len > end)
return;
*c++ = type;
dest.sin_port = htons(DHCPV4_CLIENT_PORT);
dest.sin_addr.s_addr = a->addr;
- syslog(LOG_WARNING, "sending %s to %02x:%02x:%02x:%02x:%02x:%02x - %s",
- dhcpv4_msg_to_string(msg),
- a->hwaddr[0], a->hwaddr[1], a->hwaddr[2],
- a->hwaddr[3], a->hwaddr[4], a->hwaddr[5],
- inet_ntoa(dest.sin_addr));
-
- sendto(iface->dhcpv4_event.uloop.fd, &fr_msg, sizeof(fr_msg),
- MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest));
+ if (sendto(iface->dhcpv4_event.uloop.fd, &fr_msg, sizeof(fr_msg),
+ MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest)) < 0)
+ syslog(LOG_ERR, "Failed to send %s to %s - %s: %m", dhcpv4_msg_to_string(msg),
+ odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
+ else
+ syslog(LOG_WARNING, "Sent %s to %s - %s", dhcpv4_msg_to_string(msg),
+ odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
}
static void dhcpv4_fr_timer(struct uloop_timeout *event)
dhcpv4_fr_send(a);
}
+static void dhcpv4_fr_delay_timer(struct uloop_timeout *event)
+{
+ struct dhcpv4_assignment *a = container_of(event, struct dhcpv4_assignment, fr_timer);
+ struct interface *iface = a->iface;
+
+ (iface->dhcpv4_event.uloop.fd == -1 ? dhcpv4_fr_rand_delay(a) : dhcpv4_fr_start(a));
+}
+
+static void dhcpv4_fr_rand_delay(struct dhcpv4_assignment *a)
+{
+#define MIN_DELAY 500
+#define MAX_FUZZ 500
+ int msecs;
+
+ odhcpd_urandom(&msecs, sizeof(msecs));
+
+ msecs = labs(msecs)%MAX_FUZZ + MIN_DELAY;
+
+ uloop_timeout_set(&a->fr_timer, msecs);
+ a->fr_timer.cb = dhcpv4_fr_delay_timer;
+}
+
static void dhcpv4_fr_stop(struct dhcpv4_assignment *a)
{
uloop_timeout_cancel(&a->fr_timer);
a->fr_timer.cb = NULL;
}
-// Handler for DHCPv4 messages
+/* Handler for DHCPv4 messages */
static void handle_dhcpv4(void *addr, void *data, size_t len,
struct interface *iface, _unused void *dest_addr)
{
return;
- syslog(LOG_NOTICE, "Got DHCPv4 request");
+ syslog(LOG_NOTICE, "Got DHCPv4 request on %s", iface->name);
if (!iface->dhcpv4_start_ip.s_addr && !iface->dhcpv4_end_ip.s_addr) {
syslog(LOG_WARNING, "No DHCP range available on %s", iface->name);
req->ciaddr.s_addr = INADDR_ANY;
}
- syslog(LOG_WARNING, "received %s from %02x:%02x:%02x:%02x:%02x:%02x",
- dhcpv4_msg_to_string(reqmsg),
- req->chaddr[0],req->chaddr[1],req->chaddr[2],
- req->chaddr[3],req->chaddr[4],req->chaddr[5]);
+ syslog(LOG_WARNING, "Received %s from %s on %s", dhcpv4_msg_to_string(reqmsg),
+ odhcpd_print_mac(req->chaddr, req->hlen), iface->name);
+#ifdef WITH_UBUS
+ if (reqmsg == DHCPV4_MSG_RELEASE)
+ ubus_bcast_dhcp_event("dhcp.release", req->chaddr, req->hlen,
+ &req->ciaddr, hostname, iface->ifname);
+#endif
if (reqmsg == DHCPV4_MSG_DECLINE || reqmsg == DHCPV4_MSG_RELEASE)
return;
}
}
- struct ifreq ifr = {.ifr_name = ""};
- strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name));
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name) - 1);
if (!ioctl(sock, SIOCGIFMTU, &ifr)) {
uint16_t mtu = htons(ifr.ifr_mtu);
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(DHCPV4_CLIENT_PORT);
} else {
+ struct arpreq arp = {.arp_flags = ATF_COM};
+
/*
* send reply to the newly (in this proccess) allocated IP
*/
dest.sin_addr = reply.yiaddr;
dest.sin_port = htons(DHCPV4_CLIENT_PORT);
- struct arpreq arp = {.arp_flags = ATF_COM};
memcpy(arp.arp_ha.sa_data, req->chaddr, 6);
memcpy(&arp.arp_pa, &dest, sizeof(arp.arp_pa));
memcpy(arp.arp_dev, iface->ifname, sizeof(arp.arp_dev));
- ioctl(sock, SIOCSARP, &arp);
+
+ if (ioctl(sock, SIOCSARP, &arp) < 0)
+ syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
}
- if (dest.sin_addr.s_addr == INADDR_BROADCAST)
- /* reply goes to IP broadcast -> MAC broadcast */
- syslog(LOG_WARNING, "sending %s to ff:ff:ff:ff:ff:ff - %s",
- dhcpv4_msg_to_string(msg),
- inet_ntoa(dest.sin_addr));
+ if (sendto(sock, &reply, sizeof(reply), MSG_DONTWAIT,
+ (struct sockaddr*)&dest, sizeof(dest)) < 0)
+ syslog(LOG_ERR, "Failed to send %s to %s - %s: %m",
+ dhcpv4_msg_to_string(msg),
+ dest.sin_addr.s_addr == INADDR_BROADCAST ?
+ "ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen),
+ inet_ntoa(dest.sin_addr));
else
- /*
- * reply is send directly to IP,
- * MAC is assumed to be the same as the request
- */
- syslog(LOG_WARNING, "sending %s to %02x:%02x:%02x:%02x:%02x:%02x - %s",
- dhcpv4_msg_to_string(msg),
- req->chaddr[0],req->chaddr[1],req->chaddr[2],
- req->chaddr[3],req->chaddr[4],req->chaddr[5],
- inet_ntoa(dest.sin_addr));
-
- sendto(sock, &reply, sizeof(reply), MSG_DONTWAIT,
- (struct sockaddr*)&dest, sizeof(dest));
+ syslog(LOG_ERR, "Sent %s to %s - %s",
+ dhcpv4_msg_to_string(msg),
+ dest.sin_addr.s_addr == INADDR_BROADCAST ?
+ "ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen),
+ inet_ntoa(dest.sin_addr));
+
+
+#ifdef WITH_UBUS
+ if (msg == DHCPV4_MSG_ACK)
+ ubus_bcast_dhcp_event("dhcp.ack", req->chaddr, req->hlen, &reply.yiaddr,
+ hostname, iface->ifname);
+#endif
}
static bool dhcpv4_assign(struct interface *iface,
uint32_t end = ntohl(iface->dhcpv4_end_ip.s_addr);
uint32_t count = end - start + 1;
- // try to assign the IP the client asked for
+ /* try to assign the IP the client asked for */
if (start <= ntohl(raddr) && ntohl(raddr) <= end &&
!find_assignment_by_addr(iface, raddr)) {
assign->addr = raddr;
- syslog(LOG_INFO, "assigning the IP the client asked for: %u.%u.%u.%u",
+ syslog(LOG_INFO, "Assigning the IP the client asked for: %u.%u.%u.%u",
((uint8_t *)&assign->addr)[0],
((uint8_t *)&assign->addr)[1],
((uint8_t *)&assign->addr)[2],
return true;
}
- // Seed RNG with checksum of hwaddress
+ /* Seed RNG with checksum of hwaddress */
uint32_t seed = 0;
for (size_t i = 0; i < sizeof(assign->hwaddr); ++i) {
- // Knuth's multiplicative method
+ /* Knuth's multiplicative method */
uint8_t o = assign->hwaddr[i];
seed += (o*2654435761) % UINT32_MAX;
}
if (list_empty(&iface->dhcpv4_assignments)) {
assign->addr = htonl(try);
- syslog(LOG_INFO, "assigning mapped IP (empty list): %u.%u.%u.%u",
+ syslog(LOG_INFO, "Assigning mapped IP (empty list): %u.%u.%u.%u",
((uint8_t *)&assign->addr)[0],
((uint8_t *)&assign->addr)[1],
((uint8_t *)&assign->addr)[2],
if (!find_assignment_by_addr(iface, htonl(try))) {
/* test was successful: IP address is not assigned, assign it */
assign->addr = htonl(try);
- syslog(LOG_DEBUG, "assigning mapped IP: %u.%u.%u.%u (try %u of %u)",
+ syslog(LOG_DEBUG, "Assigning mapped IP: %u.%u.%u.%u (try %u of %u)",
((uint8_t *)&assign->addr)[0],
((uint8_t *)&assign->addr)[1],
((uint8_t *)&assign->addr)[2],
try = (((try - start) + 1) % count) + start;
}
- syslog(LOG_WARNING, "can't assign any IP address -> address space is full");
+ syslog(LOG_WARNING, "Can't assign any IP address -> address space is full");
return false;
}
if (a->hostname) {
memcpy(a->hostname, hostname, hostname_len);
a->hostname[hostname_len] = 0;
+
+ if (odhcpd_valid_hostname(a->hostname))
+ a->flags &= ~OAF_BROKEN_HOSTNAME;
+ else
+ a->flags |= OAF_BROKEN_HOSTNAME;
}
}