ubus: fix invalid ipv6-prefix json
[project/odhcpd.git] / src / dhcpv4.c
index 166582e..3002c1e 100644 (file)
@@ -71,6 +71,8 @@ int dhcpv4_init(void)
 
 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);
@@ -78,57 +80,97 @@ int dhcpv4_setup_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;
 }
 
 
@@ -452,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;
@@ -723,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;
 
@@ -776,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);
@@ -872,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,