ubus: fix invalid ipv6-prefix json
[project/odhcpd.git] / src / dhcpv4.c
index 6b3d782..3002c1e 100644 (file)
 #include "dhcpv4.h"
 #include "dhcpv6.h"
 
+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 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_stop(struct dhcpv4_assignment *a);
@@ -48,6 +50,7 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                uint32_t *leasetime, const char *hostname, const size_t hostname_len,
                const bool accept_fr_nonce, bool *incl_fr_opt, uint32_t *fr_serverid);
 
+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;
 
@@ -58,14 +61,18 @@ struct odhcpd_ref_ip {
 };
 
 /* Create socket and register events */
-int init_dhcpv4(void)
+int dhcpv4_init(void)
 {
        uloop_timeout_set(&valid_until_timeout, 1000);
+       netlink_add_netevent_handler(&dhcpv4_netevent_handler);
+
        return 0;
 }
 
-int setup_dhcpv4_interface(struct interface *iface, bool enable)
+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);
@@ -73,57 +80,117 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable)
        }
 
        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;
+}
+
+
+static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info)
+{
+       struct interface *iface = info->iface;
+
+       if (!iface || iface->dhcpv4 == MODE_DISABLED)
+               return;
+
+       switch (event) {
+       case NETEV_IFINDEX_CHANGE:
+               dhcpv4_setup_interface(iface, true);
+               break;
+       case NETEV_ADDRLIST_CHANGE:
+               handle_addrlist_change(iface);
+               break;
+       default:
+               break;
+       }
 }
 
 static struct dhcpv4_assignment *find_assignment_by_hwaddr(struct interface *iface, const uint8_t *hwaddr)
@@ -293,7 +360,7 @@ static void decr_ref_cnt_ip(struct odhcpd_ref_ip **ptr, struct interface *iface)
        struct odhcpd_ref_ip *ip = *ptr;
 
        if (--ip->ref_cnt == 0) {
-               odhcpd_setup_addr(&ip->addr, iface, false, false);
+               netlink_setup_addr(&ip->addr, iface->ifindex, false, false);
 
                list_del(&ip->head);
                free(ip);
@@ -344,11 +411,8 @@ static void valid_until_cb(struct uloop_timeout *event)
        uloop_timeout_set(event, 1000);
 }
 
-void dhcpv4_addr_update(struct interface *iface)
+static void handle_addrlist_change(struct interface *iface)
 {
-       if (iface->dhcpv4 == MODE_DISABLED)
-               return;
-
        struct odhcpd_ipaddr ip;
        struct odhcpd_ref_ip *a;
        struct dhcpv4_assignment *c;
@@ -373,7 +437,7 @@ void dhcpv4_addr_update(struct interface *iface)
 
        a = list_first_entry(&iface->dhcpv4_fr_ips, struct odhcpd_ref_ip, head);
 
-       if (odhcpd_setup_addr(&a->addr, iface, false, true)) {
+       if (netlink_setup_addr(&a->addr, iface->ifindex, false, true)) {
                syslog(LOG_ERR, "Failed to add ip address");
                return;
        }
@@ -430,7 +494,9 @@ static void dhcpv4_put(struct dhcpv4_message *msg, uint8_t **cookie,
                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;
@@ -701,6 +767,11 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                        req->chaddr[0],req->chaddr[1],req->chaddr[2],
                        req->chaddr[3],req->chaddr[4],req->chaddr[5]);
 
+#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;
 
@@ -754,8 +825,10 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                }
        }
 
-       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);
@@ -850,6 +923,12 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
 
        sendto(sock, &reply, sizeof(reply), MSG_DONTWAIT,
                        (struct sockaddr*)&dest, sizeof(dest));
+
+#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,