+static struct dhcp_assignment* dhcpv4_lease(struct interface *iface,
+ enum dhcpv4_msg msg, const uint8_t *mac, const uint32_t reqaddr,
+ uint32_t *leasetime, const char *hostname, const size_t hostname_len,
+ const bool accept_fr_nonce, bool *incl_fr_opt, uint32_t *fr_serverid,
+ const char *reqopts, const size_t reqopts_len);
+
+static struct netevent_handler dhcpv4_netevent_handler = { .cb = dhcpv4_netevent_cb, };
+static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb};
+static uint32_t serial = 0;
+
+struct odhcpd_ref_ip {
+ struct list_head head;
+ int ref_cnt;
+ struct odhcpd_ipaddr addr;
+};
+
+/* Create socket and register events */
+int dhcpv4_init(void)
+{
+ uloop_timeout_set(&valid_until_timeout, 1000);
+ netlink_add_netevent_handler(&dhcpv4_netevent_handler);
+
+ return 0;
+}
+
+int dhcpv4_setup_interface(struct interface *iface, bool enable)
+{
+ int ret = 0;
+
+ enable = enable && (iface->dhcpv4 != MODE_DISABLED);
+
+ if (iface->dhcpv4_event.uloop.fd >= 0) {
+ uloop_fd_delete(&iface->dhcpv4_event.uloop);
+ close(iface->dhcpv4_event.uloop.fd);
+ iface->dhcpv4_event.uloop.fd = -1;
+ }
+
+ if (enable) {
+ struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT),
+ {INADDR_ANY}, {0}};
+ int val = 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 */
+ 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;
+ 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;
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 (setup_dhcpv4_addresses(iface) < 0) {
+ ret = -1;
+ goto out;
+ }