dhcpv6: implement statistics for DHCPv6
authorNicolas BESNARD <[email protected]>
Wed, 20 Nov 2024 16:34:30 +0000 (16:34 +0000)
committerÁlvaro Fernández Rojas <[email protected]>
Mon, 3 Nov 2025 15:21:24 +0000 (16:21 +0100)
Problem:
    odhcp6c does not provide any statistics.

Solution:
    Count the number of packets transmitted or received by message type.
    Add a UBus RPC method to get this information.

Signed-off-by: Nicolas BESNARD <[email protected]>
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcp6c/pull/106
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/dhcpv6.c
src/odhcp6c.h
src/ubus.c

index 9637adae1f96503a279ff4f3c1bf2a07792f365a..dc48fe08450019073cb47b409c7cc98c592ab2d9 100644 (file)
@@ -118,6 +118,9 @@ static uint8_t reconf_key[16];
 // client options
 static unsigned int client_options = 0;
 
+// counters for statistics
+static struct dhcpv6_stats dhcpv6_stats = {0};
+
 static uint32_t ntohl_unaligned(const uint8_t *data)
 {
        uint32_t buf;
@@ -138,6 +141,54 @@ static void dhcpv6_prev_state(void)
        dhcpv6_reset_state_timeout();
 }
 
+static void dhcpv6_inc_counter(enum dhcpv6_msg type)
+{
+       switch (type) {
+       case DHCPV6_MSG_SOLICIT:
+               dhcpv6_stats.solicit++;
+               break;
+
+       case DHCPV6_MSG_ADVERT:
+               dhcpv6_stats.advertise++;
+               break;
+
+       case DHCPV6_MSG_REQUEST:
+               dhcpv6_stats.request++;
+               break;
+
+       case DHCPV6_MSG_RENEW:
+               dhcpv6_stats.renew++;
+               break;
+
+       case DHCPV6_MSG_REBIND:
+               dhcpv6_stats.rebind++;
+               break;
+
+       case DHCPV6_MSG_REPLY:
+               dhcpv6_stats.reply++;
+               break;
+
+       case DHCPV6_MSG_RELEASE:
+               dhcpv6_stats.release++;
+               break;
+
+       case DHCPV6_MSG_DECLINE:
+               dhcpv6_stats.decline++;
+               break;
+
+       case DHCPV6_MSG_RECONF:
+               dhcpv6_stats.reconfigure++;
+               break;
+
+       case DHCPV6_MSG_INFO_REQ:
+               dhcpv6_stats.information_request++;
+               break;
+
+       default:
+               break;
+       }
+}
+
 static char *dhcpv6_msg_to_str(enum dhcpv6_msg msg)
 {
        switch (msg) {
@@ -340,6 +391,16 @@ void dhcpv6_reset_state_timeout(void)
        dhcpv6_state_timeout = 0;
 }
 
+struct dhcpv6_stats dhcpv6_get_stats(void)
+{
+       return dhcpv6_stats;
+}
+
+void dhcpv6_reset_stats(void)
+{
+       memset(&dhcpv6_stats, 0, sizeof(dhcpv6_stats));
+}
+
 int init_dhcpv6(const char *ifname, unsigned int options, int sk_prio, int sol_timeout, unsigned int dscp)
 {
        client_options = options;
@@ -794,6 +855,9 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs)
                        dhcpv6_msg_to_str(type),
                        inet_ntop(AF_INET6, (const void *)&srv.sin6_addr,
                                in6_str, sizeof(in6_str)), strerror(errno));
+               dhcpv6_stats.transmit_failures++;
+       } else {
+               dhcpv6_inc_counter(type);
        }
 }
 
@@ -1900,14 +1964,18 @@ int dhcpv6_receive_response(enum dhcpv6_msg type)
        }
 
        if (pktinfo == NULL) {
+               dhcpv6_stats.discarded_packets++;
                return -1;
        }
 
        if (!dhcpv6_response_is_valid(buf, len, retx->tr_id, type,
                                         &pktinfo->ipi6_addr)) {
+               dhcpv6_stats.discarded_packets++;
                return -1;
        }
 
+       dhcpv6_inc_counter(hdr->msg_type);
+
        uint8_t *opt = &buf[4];
        uint8_t *opt_end = opt + len - 4;
        retx->round_start = odhcp6c_get_milli_time();
index a28decc8aa90cf566fd2375e0cc296c0e04ef273..740dce787cb0acce49b5e4b35a52ff9c2a5f65f4 100644 (file)
@@ -323,7 +323,22 @@ struct dhcpv6_server_cand {
        size_t ia_pd_len;
 };
 
-
+struct dhcpv6_stats {
+       uint64_t solicit;
+       uint64_t advertise;
+       uint64_t request;
+       uint64_t confirm;
+       uint64_t renew;
+       uint64_t rebind;
+       uint64_t reply;
+       uint64_t release;
+       uint64_t decline;
+       uint64_t reconfigure;
+       uint64_t information_request;
+       uint64_t discarded_packets;
+       uint64_t transmit_failures;
+};
+       
 enum odhcp6c_state {
        STATE_CLIENT_ID,
        STATE_SERVER_ID,
@@ -437,6 +452,8 @@ int dhcpv6_receive_response(enum dhcpv6_msg type);
 enum dhcpv6_state dhcpv6_get_state(void);
 void dhcpv6_set_state(enum dhcpv6_state state);
 int dhcpv6_get_socket(void);
+struct dhcpv6_stats dhcpv6_get_stats(void);
+void dhcpv6_reset_stats(void);
 int dhcpv6_state_processing(enum dhcpv6_msg type);
 int dhcpv6_get_state_timeout(void);
 void dhcpv6_set_state_timeout(int timeout);
index ebe16ae5cfc086ed0887a9caa6f0b106f989cdd4..402ba681d64669d4c8b1aab878378edab23565f8 100644 (file)
@@ -103,9 +103,15 @@ static char ubus_name[24];
 
 static int ubus_handle_get_state(struct ubus_context *ctx, struct ubus_object *obj,
                struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+static int ubus_handle_get_stats(struct ubus_context *ctx, struct ubus_object *obj,
+               struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+static int ubus_handle_reset_stats(struct ubus_context *ctx, struct ubus_object *obj,
+               struct ubus_request_data *req, const char *method, struct blob_attr *msg);
 
 static struct ubus_method odhcp6c_object_methods[] = {
        UBUS_METHOD_NOARG("get_state", ubus_handle_get_state),
+       UBUS_METHOD_NOARG("get_statistics", ubus_handle_get_stats),
+       UBUS_METHOD_NOARG("reset_statistics", ubus_handle_reset_stats),
 };
 
 static struct ubus_object_type odhcp6c_object_type = 
@@ -528,6 +534,43 @@ static int states_to_blob(void)
        return UBUS_STATUS_OK;
 }
 
+static int ubus_handle_get_stats(struct ubus_context *ctx, _unused struct ubus_object *obj,
+               struct ubus_request_data *req, _unused const char *method,
+               _unused struct blob_attr *msg)
+{
+       struct dhcpv6_stats stats = dhcpv6_get_stats();
+
+       blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
+       blobmsg_add_u64(&b, "dhcp_solicit", stats.solicit);
+       blobmsg_add_u64(&b, "dhcp_advertise", stats.advertise);
+       blobmsg_add_u64(&b, "dhcp_request", stats.request);
+       blobmsg_add_u64(&b, "dhcp_confirm", stats.confirm);
+       blobmsg_add_u64(&b, "dhcp_renew", stats.renew);
+       blobmsg_add_u64(&b, "dhcp_rebind", stats.rebind);
+       blobmsg_add_u64(&b, "dhcp_reply", stats.reply);
+       blobmsg_add_u64(&b, "dhcp_release", stats.release);
+       blobmsg_add_u64(&b, "dhcp_decline", stats.decline);
+       blobmsg_add_u64(&b, "dhcp_reconfigure", stats.reconfigure);
+       blobmsg_add_u64(&b, "dhcp_information_request", stats.information_request);
+       blobmsg_add_u64(&b, "dhcp_discarded_packets", stats.discarded_packets);
+       blobmsg_add_u64(&b, "dhcp_transmit_failures", stats.transmit_failures);
+
+       CHECK(ubus_send_reply(ctx, req, b.head));
+       blob_buf_free(&b);
+
+       return UBUS_STATUS_OK;
+}
+
+static int ubus_handle_reset_stats(_unused struct ubus_context *ctx, _unused struct ubus_object *obj,
+               _unused struct ubus_request_data *req, _unused const char *method,
+               _unused struct blob_attr *msg)
+{
+       dhcpv6_reset_stats();
+
+       return UBUS_STATUS_OK;
+}
+
+
 static int ubus_handle_get_state(struct ubus_context *ctx, _unused struct ubus_object *obj,
                struct ubus_request_data *req, _unused const char *method,
                _unused struct blob_attr *msg)