+struct log_ctxt {
+ char *buf;
+ int buf_len;
+ int buf_idx;
+};
+
+static void dhcpv6_log_ia_addr(struct in6_addr *addr, int prefix, _unused uint32_t pref,
+ _unused uint32_t valid, void *arg)
+{
+ struct log_ctxt *ctxt = (struct log_ctxt *)arg;
+ char addrbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop(AF_INET6, addr, addrbuf, sizeof(addrbuf));
+ ctxt->buf_idx += snprintf(ctxt->buf + ctxt->buf_idx, ctxt->buf_len - ctxt->buf_idx,
+ "%s/%d ", addrbuf, prefix);
+}
+
+static void dhcpv6_log(uint8_t msgtype, struct interface *iface, time_t now,
+ const char *duidbuf, bool is_pd, struct dhcpv6_assignment *a, int code)
+{
+ const char *type = "UNKNOWN";
+ const char *status = "UNKNOWN";
+
+ if (msgtype == DHCPV6_MSG_RENEW)
+ return;
+
+ switch (msgtype) {
+ case DHCPV6_MSG_SOLICIT:
+ type = "SOLICIT";
+ break;
+ case DHCPV6_MSG_REQUEST:
+ type = "REQUEST";
+ break;
+ case DHCPV6_MSG_CONFIRM:
+ type = "CONFIRM";
+ break;
+ case DHCPV6_MSG_RENEW:
+ type = "RENEW";
+ break;
+ case DHCPV6_MSG_REBIND:
+ type = "REBIND";
+ break;
+ case DHCPV6_MSG_RELEASE:
+ type = "RELEASE";
+ break;
+ case DHCPV6_MSG_DECLINE:
+ type = "DECLINE";
+ break;
+ }
+
+ switch (code) {
+ case DHCPV6_STATUS_OK:
+ status = "ok";
+ break;
+ case DHCPV6_STATUS_NOADDRSAVAIL:
+ status = "no addresses available";
+ break;
+ case DHCPV6_STATUS_NOBINDING:
+ status = "no binding";
+ break;
+ case DHCPV6_STATUS_NOTONLINK:
+ status = "not on-link";
+ break;
+ case DHCPV6_STATUS_NOPREFIXAVAIL:
+ status = "no prefix available";
+ break;
+ }
+
+ char leasebuf[256] = "";
+
+ if (a) {
+ struct log_ctxt ctxt = {.buf = leasebuf,
+ .buf_len = sizeof(leasebuf),
+ .buf_idx = 0 };
+
+ dhcpv6_enum_ia_addrs(iface, a, now, dhcpv6_log_ia_addr, &ctxt);
+ }
+
+ syslog(LOG_WARNING, "DHCPV6 %s %s from %s on %s: %s %s", type, (is_pd) ? "IA_PD" : "IA_NA",
+ duidbuf, iface->ifname, status, leasebuf);
+}