config: move pio json handling to statefiles.c
authorDavid Härdeman <[email protected]>
Fri, 28 Nov 2025 10:48:59 +0000 (11:48 +0100)
committerÁlvaro Fernández Rojas <[email protected]>
Tue, 9 Dec 2025 15:29:12 +0000 (16:29 +0100)
The prefix information files are also examples of statefiles, and there
are some things which can be shared between e.g. the state file logic
and the pio file logic (like the conversion from MONOTIME to time_t and
back, and also the use of tmp files). That's for later patches though,
this just moves the code without any code changes to make subsequent
patches clearer.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/333
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/config.c
src/odhcpd.h
src/router.c
src/statefiles.c
src/statefiles.h

index 97af99aec19de6a2d45f3a272ad14e804bfe0d24..fb492b1f42885ce636ea8b3f5e12fe9a6a0a4250 100644 (file)
@@ -1,4 +1,3 @@
-#include <errno.h>
 #include <fcntl.h>
 #include <resolv.h>
 #include <signal.h>
@@ -12,7 +11,6 @@
 
 #include <uci.h>
 #include <uci_blob.h>
-#include <json-c/json.h>
 #include <libubox/utils.h>
 #include <libubox/avl.h>
 #include <libubox/avl-cmp.h>
@@ -1996,306 +1994,6 @@ static int ipv6_pxe_from_uci(struct uci_section* s)
        return ipv6_pxe_entry_new(arch, url) ? -1 : 0;
 }
 
-#define JSON_LENGTH "length"
-#define JSON_PREFIX "prefix"
-#define JSON_SLAAC "slaac"
-#define JSON_TIME "time"
-
-static inline time_t config_time_from_json(time_t json_time)
-{
-       time_t ref, now;
-
-       ref = time(NULL);
-       now = odhcpd_time();
-
-       if (now > json_time || ref > json_time)
-               return 0;
-
-       return json_time + (now - ref);
-}
-
-static inline time_t config_time_to_json(time_t config_time)
-{
-       time_t ref, now;
-
-       ref = time(NULL);
-       now = odhcpd_time();
-
-       return config_time + (ref - now);
-}
-
-static inline bool config_ra_pio_enabled(struct interface *iface)
-{
-       return config.ra_piofolder_fd >= 0 && iface->ra == MODE_SERVER && !iface->master;
-}
-
-static bool config_ra_pio_time(json_object *slaac_json, time_t *slaac_time)
-{
-       time_t pio_json_time, pio_time;
-       json_object *time_json;
-
-       time_json = json_object_object_get(slaac_json, JSON_TIME);
-       if (!time_json)
-               return true;
-
-       pio_json_time = (time_t) json_object_get_int64(time_json);
-       if (!pio_json_time)
-               return true;
-
-       pio_time = config_time_from_json(pio_json_time);
-       if (!pio_time)
-               return false;
-
-       *slaac_time = pio_time;
-
-       return true;
-}
-
-static json_object *config_load_ra_pio_json(struct interface *iface)
-{
-       json_object *json;
-       int fd;
-
-       fd = openat(config.ra_piofolder_fd, iface->ifname, O_RDONLY | O_CLOEXEC);
-       if (fd < 0)
-               return NULL;
-
-       json = json_object_from_fd(fd);
-
-       close(fd);
-
-       if (!json)
-               error("rfc9096: %s: json read error %s",
-                     iface->ifname,
-                     json_util_get_last_err());
-
-       return json;
-}
-
-void config_load_ra_pio(struct interface *iface)
-{
-       json_object *json, *slaac_json;
-       struct ra_pio *new_pios;
-       size_t pio_cnt;
-       time_t now;
-
-       if (!config_ra_pio_enabled(iface))
-               return;
-
-       json = config_load_ra_pio_json(iface);
-       if (!json)
-               return;
-
-       slaac_json = json_object_object_get(json, JSON_SLAAC);
-       if (!slaac_json) {
-               json_object_put(json);
-               return;
-       }
-
-       now = odhcpd_time();
-
-       pio_cnt = json_object_array_length(slaac_json);
-       new_pios = realloc(iface->pios, sizeof(struct ra_pio) * pio_cnt);
-       if (!new_pios) {
-               json_object_put(json);
-               return;
-       }
-
-       iface->pios = new_pios;
-       iface->pio_cnt = 0;
-       for (size_t i = 0; i < pio_cnt; i++) {
-               json_object *cur_pio_json, *length_json, *prefix_json;
-               const char *pio_str;
-               time_t pio_lt = 0;
-               struct ra_pio *pio;
-               uint8_t pio_len;
-
-               cur_pio_json = json_object_array_get_idx(slaac_json, i);
-               if (!cur_pio_json)
-                       continue;
-
-               if (!config_ra_pio_time(cur_pio_json, &pio_lt))
-                       continue;
-
-               length_json = json_object_object_get(cur_pio_json, JSON_LENGTH);
-               if (!length_json)
-                       continue;
-
-               prefix_json = json_object_object_get(cur_pio_json, JSON_PREFIX);
-               if (!prefix_json)
-                       continue;
-
-               pio_len = (uint8_t) json_object_get_uint64(length_json);
-               pio_str = json_object_get_string(prefix_json);
-               pio = &iface->pios[iface->pio_cnt];
-
-               inet_pton(AF_INET6, pio_str, &pio->prefix);
-               pio->length = pio_len;
-               pio->lifetime = pio_lt;
-               info("rfc9096: %s: load %s/%u (%u)",
-                    iface->ifname,
-                    pio_str,
-                    pio_len,
-                    ra_pio_lifetime(pio, now));
-
-               iface->pio_cnt++;
-       }
-
-       json_object_put(json);
-
-       if (!iface->pio_cnt) {
-               free(iface->pios);
-               iface->pios = NULL;
-       } else if (iface->pio_cnt != pio_cnt) {
-               struct ra_pio *tmp;
-
-               tmp = realloc(iface->pios, sizeof(struct ra_pio) * iface->pio_cnt);
-               if (tmp)
-                       iface->pios = tmp;
-       }
-}
-
-static void config_save_ra_pio_json(struct interface *iface, struct json_object *json)
-{
-       size_t tmp_piofile_strlen;
-       char *tmp_piofile;
-       int fd, ret;
-
-       tmp_piofile_strlen = strlen(iface->ifname) + 2;
-       tmp_piofile = alloca(tmp_piofile_strlen);
-       snprintf(tmp_piofile, tmp_piofile_strlen, ".%s", iface->ifname);
-
-       fd = openat(config.ra_piofolder_fd,
-               tmp_piofile,
-               O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
-               0644);
-       if (fd < 0) {
-               error("rfc9096: %s: error %m creating temporary json file",
-                     iface->ifname);
-               return;
-       }
-
-       ret = json_object_to_fd(fd, json, JSON_C_TO_STRING_PLAIN);
-       if (ret) {
-               error("rfc9096: %s: json write error %s",
-                     iface->ifname,
-                     json_util_get_last_err());
-               close(fd);
-               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
-               return;
-       }
-
-       ret = fsync(fd);
-       if (ret) {
-               error("rfc9096: %s: error %m syncing %s",
-                     iface->ifname,
-                     tmp_piofile);
-               close(fd);
-               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
-               return;
-       }
-
-       ret = close(fd);
-       if (ret) {
-               error("rfc9096: %s: error %m closing %s",
-                     iface->ifname,
-                     tmp_piofile);
-               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
-               return;
-       }
-
-       ret = renameat(config.ra_piofolder_fd,
-               tmp_piofile,
-               config.ra_piofolder_fd,
-               iface->ifname);
-       if (ret) {
-               error("rfc9096: %s: error %m renaming piofile: %s -> %s",
-                     iface->ifname,
-                     tmp_piofile,
-                     iface->ifname);
-               close(fd);
-               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
-               return;
-       }
-
-       iface->pio_update = false;
-       warn("rfc9096: %s: piofile updated", iface->ifname);
-}
-
-void config_save_ra_pio(struct interface *iface)
-{
-       struct json_object *json, *slaac_json;
-       char ipv6_str[INET6_ADDRSTRLEN];
-       time_t now;
-
-       if (!config_ra_pio_enabled(iface))
-               return;
-
-       if (!iface->pio_update)
-               return;
-
-       now = odhcpd_time();
-
-       json = json_object_new_object();
-       if (!json)
-               return;
-
-       slaac_json = json_object_new_array_ext(iface->pio_cnt);
-       if (!slaac_json) {
-               json_object_put(slaac_json);
-               return;
-       }
-
-       json_object_object_add(json, JSON_SLAAC, slaac_json);
-
-       for (size_t i = 0; i < iface->pio_cnt; i++) {
-               struct json_object *cur_pio_json, *len_json, *pfx_json;
-               const struct ra_pio *cur_pio = &iface->pios[i];
-
-               if (ra_pio_expired(cur_pio, now))
-                       continue;
-
-               cur_pio_json = json_object_new_object();
-               if (!cur_pio_json)
-                       continue;
-
-               inet_ntop(AF_INET6, &cur_pio->prefix, ipv6_str, sizeof(ipv6_str));
-
-               pfx_json = json_object_new_string(ipv6_str);
-               if (!pfx_json) {
-                       json_object_put(cur_pio_json);
-                       continue;
-               }
-
-               len_json = json_object_new_uint64(cur_pio->length);
-               if (!len_json) {
-                       json_object_put(cur_pio_json);
-                       json_object_put(pfx_json);
-                       continue;
-               }
-
-               json_object_object_add(cur_pio_json, JSON_PREFIX, pfx_json);
-               json_object_object_add(cur_pio_json, JSON_LENGTH, len_json);
-
-               if (cur_pio->lifetime) {
-                       struct json_object *time_json;
-                       time_t pio_lt;
-
-                       pio_lt = config_time_to_json(cur_pio->lifetime);
-
-                       time_json = json_object_new_int64(pio_lt);
-                       if (time_json)
-                               json_object_object_add(cur_pio_json, JSON_TIME, time_json);
-               }
-
-               json_object_array_add(slaac_json, cur_pio_json);
-       }
-
-       config_save_ra_pio_json(iface, json);
-
-       json_object_put(json);
-}
-
 void odhcpd_reload(void)
 {
        struct uci_context *uci = uci_alloc_context();
index 0b308b2d1cabcbd2ced44ee44177fbd5d35d7ba6..3ddbb0cd9b15c68b9712e2f6158bccd9d4da8945 100644 (file)
@@ -577,8 +577,6 @@ struct lease_cfg *config_find_lease_cfg_by_mac(const uint8_t *mac);
 struct lease_cfg *config_find_lease_cfg_by_hostid(const uint64_t hostid);
 struct lease_cfg *config_find_lease_cfg_by_ipv4(const struct in_addr ipv4);
 int config_set_lease_cfg_from_blobmsg(struct blob_attr *ba);
-void config_load_ra_pio(struct interface *iface);
-void config_save_ra_pio(struct interface *iface);
 
 #ifdef WITH_UBUS
 int ubus_init(void);
index afb64aec1e638fd393a5e44a951b45b258e91fea..9dde823e489db6024b4149236e8aa4848433febc 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "router.h"
 #include "odhcpd.h"
+#include "statefiles.h"
 
 
 static void forward_router_solicitation(const struct interface *iface);
index 21201cbbf55a0760a9ea17281365883b54cbcc49..a8ab28b59affd53e84d8b48aaeb673e295fc6cbc 100644 (file)
@@ -4,6 +4,7 @@
  * SPDX-FileCopyrightText: 2022 Kevin Darbyshire-Bryant <[email protected]>
  * SPDX-FileCopyrightText: 2024 Paul Donald <[email protected]>
  * SPDX-FileCopyrightText: 2024 David Härdeman <[email protected]>
+ * SPDX-FileCopyrightText: 2025 Álvaro Fernández Rojas <[email protected]>
  *
  * SPDX-License-Identifier: GPL2.0-only
  */
@@ -25,6 +26,7 @@
 #include <spawn.h>
 
 #include <libubox/md5.h>
+#include <json-c/json.h>
 
 #include "odhcpd.h"
 #include "dhcpv6-ia.h"
@@ -99,6 +101,306 @@ static void statefiles_finish_tmp_file(int dirfd, FILE **fpp, const char *prefix
        renameat(dirfd, ODHCPD_TMP_FILE, dirfd, filename);
 }
 
+#define JSON_LENGTH "length"
+#define JSON_PREFIX "prefix"
+#define JSON_SLAAC "slaac"
+#define JSON_TIME "time"
+
+static inline time_t config_time_from_json(time_t json_time)
+{
+       time_t ref, now;
+
+       ref = time(NULL);
+       now = odhcpd_time();
+
+       if (now > json_time || ref > json_time)
+               return 0;
+
+       return json_time + (now - ref);
+}
+
+static inline time_t config_time_to_json(time_t config_time)
+{
+       time_t ref, now;
+
+       ref = time(NULL);
+       now = odhcpd_time();
+
+       return config_time + (ref - now);
+}
+
+static inline bool config_ra_pio_enabled(struct interface *iface)
+{
+       return config.ra_piofolder_fd >= 0 && iface->ra == MODE_SERVER && !iface->master;
+}
+
+static bool config_ra_pio_time(json_object *slaac_json, time_t *slaac_time)
+{
+       time_t pio_json_time, pio_time;
+       json_object *time_json;
+
+       time_json = json_object_object_get(slaac_json, JSON_TIME);
+       if (!time_json)
+               return true;
+
+       pio_json_time = (time_t) json_object_get_int64(time_json);
+       if (!pio_json_time)
+               return true;
+
+       pio_time = config_time_from_json(pio_json_time);
+       if (!pio_time)
+               return false;
+
+       *slaac_time = pio_time;
+
+       return true;
+}
+
+static json_object *config_load_ra_pio_json(struct interface *iface)
+{
+       json_object *json;
+       int fd;
+
+       fd = openat(config.ra_piofolder_fd, iface->ifname, O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               return NULL;
+
+       json = json_object_from_fd(fd);
+
+       close(fd);
+
+       if (!json)
+               error("rfc9096: %s: json read error %s",
+                     iface->ifname,
+                     json_util_get_last_err());
+
+       return json;
+}
+
+void config_load_ra_pio(struct interface *iface)
+{
+       json_object *json, *slaac_json;
+       struct ra_pio *new_pios;
+       size_t pio_cnt;
+       time_t now;
+
+       if (!config_ra_pio_enabled(iface))
+               return;
+
+       json = config_load_ra_pio_json(iface);
+       if (!json)
+               return;
+
+       slaac_json = json_object_object_get(json, JSON_SLAAC);
+       if (!slaac_json) {
+               json_object_put(json);
+               return;
+       }
+
+       now = odhcpd_time();
+
+       pio_cnt = json_object_array_length(slaac_json);
+       new_pios = realloc(iface->pios, sizeof(struct ra_pio) * pio_cnt);
+       if (!new_pios) {
+               json_object_put(json);
+               return;
+       }
+
+       iface->pios = new_pios;
+       iface->pio_cnt = 0;
+       for (size_t i = 0; i < pio_cnt; i++) {
+               json_object *cur_pio_json, *length_json, *prefix_json;
+               const char *pio_str;
+               time_t pio_lt = 0;
+               struct ra_pio *pio;
+               uint8_t pio_len;
+
+               cur_pio_json = json_object_array_get_idx(slaac_json, i);
+               if (!cur_pio_json)
+                       continue;
+
+               if (!config_ra_pio_time(cur_pio_json, &pio_lt))
+                       continue;
+
+               length_json = json_object_object_get(cur_pio_json, JSON_LENGTH);
+               if (!length_json)
+                       continue;
+
+               prefix_json = json_object_object_get(cur_pio_json, JSON_PREFIX);
+               if (!prefix_json)
+                       continue;
+
+               pio_len = (uint8_t) json_object_get_uint64(length_json);
+               pio_str = json_object_get_string(prefix_json);
+               pio = &iface->pios[iface->pio_cnt];
+
+               inet_pton(AF_INET6, pio_str, &pio->prefix);
+               pio->length = pio_len;
+               pio->lifetime = pio_lt;
+               info("rfc9096: %s: load %s/%u (%u)",
+                    iface->ifname,
+                    pio_str,
+                    pio_len,
+                    ra_pio_lifetime(pio, now));
+
+               iface->pio_cnt++;
+       }
+
+       json_object_put(json);
+
+       if (!iface->pio_cnt) {
+               free(iface->pios);
+               iface->pios = NULL;
+       } else if (iface->pio_cnt != pio_cnt) {
+               struct ra_pio *tmp;
+
+               tmp = realloc(iface->pios, sizeof(struct ra_pio) * iface->pio_cnt);
+               if (tmp)
+                       iface->pios = tmp;
+       }
+}
+
+static void config_save_ra_pio_json(struct interface *iface, struct json_object *json)
+{
+       size_t tmp_piofile_strlen;
+       char *tmp_piofile;
+       int fd, ret;
+
+       tmp_piofile_strlen = strlen(iface->ifname) + 2;
+       tmp_piofile = alloca(tmp_piofile_strlen);
+       snprintf(tmp_piofile, tmp_piofile_strlen, ".%s", iface->ifname);
+
+       fd = openat(config.ra_piofolder_fd,
+               tmp_piofile,
+               O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
+               0644);
+       if (fd < 0) {
+               error("rfc9096: %s: error %m creating temporary json file",
+                     iface->ifname);
+               return;
+       }
+
+       ret = json_object_to_fd(fd, json, JSON_C_TO_STRING_PLAIN);
+       if (ret) {
+               error("rfc9096: %s: json write error %s",
+                     iface->ifname,
+                     json_util_get_last_err());
+               close(fd);
+               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
+               return;
+       }
+
+       ret = fsync(fd);
+       if (ret) {
+               error("rfc9096: %s: error %m syncing %s",
+                     iface->ifname,
+                     tmp_piofile);
+               close(fd);
+               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
+               return;
+       }
+
+       ret = close(fd);
+       if (ret) {
+               error("rfc9096: %s: error %m closing %s",
+                     iface->ifname,
+                     tmp_piofile);
+               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
+               return;
+       }
+
+       ret = renameat(config.ra_piofolder_fd,
+               tmp_piofile,
+               config.ra_piofolder_fd,
+               iface->ifname);
+       if (ret) {
+               error("rfc9096: %s: error %m renaming piofile: %s -> %s",
+                     iface->ifname,
+                     tmp_piofile,
+                     iface->ifname);
+               close(fd);
+               unlinkat(config.ra_piofolder_fd, tmp_piofile, 0);
+               return;
+       }
+
+       iface->pio_update = false;
+       warn("rfc9096: %s: piofile updated", iface->ifname);
+}
+
+void config_save_ra_pio(struct interface *iface)
+{
+       struct json_object *json, *slaac_json;
+       char ipv6_str[INET6_ADDRSTRLEN];
+       time_t now;
+
+       if (!config_ra_pio_enabled(iface))
+               return;
+
+       if (!iface->pio_update)
+               return;
+
+       now = odhcpd_time();
+
+       json = json_object_new_object();
+       if (!json)
+               return;
+
+       slaac_json = json_object_new_array_ext(iface->pio_cnt);
+       if (!slaac_json) {
+               json_object_put(slaac_json);
+               return;
+       }
+
+       json_object_object_add(json, JSON_SLAAC, slaac_json);
+
+       for (size_t i = 0; i < iface->pio_cnt; i++) {
+               struct json_object *cur_pio_json, *len_json, *pfx_json;
+               const struct ra_pio *cur_pio = &iface->pios[i];
+
+               if (ra_pio_expired(cur_pio, now))
+                       continue;
+
+               cur_pio_json = json_object_new_object();
+               if (!cur_pio_json)
+                       continue;
+
+               inet_ntop(AF_INET6, &cur_pio->prefix, ipv6_str, sizeof(ipv6_str));
+
+               pfx_json = json_object_new_string(ipv6_str);
+               if (!pfx_json) {
+                       json_object_put(cur_pio_json);
+                       continue;
+               }
+
+               len_json = json_object_new_uint64(cur_pio->length);
+               if (!len_json) {
+                       json_object_put(cur_pio_json);
+                       json_object_put(pfx_json);
+                       continue;
+               }
+
+               json_object_object_add(cur_pio_json, JSON_PREFIX, pfx_json);
+               json_object_object_add(cur_pio_json, JSON_LENGTH, len_json);
+
+               if (cur_pio->lifetime) {
+                       struct json_object *time_json;
+                       time_t pio_lt;
+
+                       pio_lt = config_time_to_json(cur_pio->lifetime);
+
+                       time_json = json_object_new_int64(pio_lt);
+                       if (time_json)
+                               json_object_object_add(cur_pio_json, JSON_TIME, time_json);
+               }
+
+               json_object_array_add(slaac_json, cur_pio_json);
+       }
+
+       config_save_ra_pio_json(iface, json);
+
+       json_object_put(json);
+}
+
 static void statefiles_write_host(const char *ipbuf, const char *hostname, struct write_ctxt *ctxt)
 {
        char exp_dn[DNS_MAX_NAME_LEN];
index de7139776573daad997351c42bab487fdc56f92a..0b715098a947b29d3fac7145a029edf08f0350fd 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2024 David Härdeman <[email protected]>
+ * SPDX-FileCopyrightText: 2025 Álvaro Fernández Rojas <[email protected]>
  *
  * SPDX-License-Identifier: GPL2.0-only
  */
 #define ODHCPD_HOSTS_FILE_PREFIX "odhcpd.hosts"
 #define ODHCPD_TMP_FILE ".odhcpd.tmp"
 
+void config_load_ra_pio(struct interface *iface);
+
+void config_save_ra_pio(struct interface *iface);
+
 bool statefiles_write(void);
 
 void statefiles_setup_dirfd(const char *path, int *dirfdp);