clinux-kernelnetfilter

How to get ipset timeout programmatically?


I am trying to get the timeout value of an ipset programmatically (not using the userspace ipset tool).

The following example tries to get the timeout from set named "my_set" which had been created with this command: ipset create my_set hash:ip timeout 86400

Here's the example code where I tried a few different ways to access the timeout data field:

#include "stdlib.h"
#include <libipset/types.h>
#include <libipset/session.h>
int main(int argc, char* argv[]) {
  ipset_load_types();

  struct ipset_session *session;
  session = ipset_session_init(NULL, NULL);
  ipset_session_data_set(session, IPSET_SETNAME, "my_set");

  /* Validate set exist */
  if (ipset_type_get(session, IPSET_CMD_TEST) == NULL) {
    printf("Error: Can't find set\n");
    exit(1);
  }

  if ((const uint32_t*)ipset_session_data_get(session, IPSET_OPT_TIMEOUT) == NULL)
    printf("Timeout field is NULL\n");

  const struct ipset_data* data = ipset_session_data(session);

  if (!ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_TIMEOUT)))
    printf("Timeout field is not present\n");

  if (!ipset_data_test(data, IPSET_OPT_TIMEOUT))
    printf("Timeout field is not present\n");

  if ((const uint32_t*)ipset_session_data_get(session, IPSET_OPT_TIMEOUT) == NULL)
    printf("Timeout field is NULL\n");
}

Compile with: gcc -lipset test.c

Output is:

Timeout field is NULL
Timeout field is not present
Timeout field is not present
Timeout field is NULL

I think the timeout is not being provided by the kernel, but I don't know how to use the ipset API to ask the kernel for that information. I can't find any API documentation and I'm trying to use examples from the ipset source I found here or here

The API here is not useful for this task.


Solution

  • From what I can see, libipset has some quite silly interface. There is no decent way to obtain the properties of a given set through the library, and the associated command line tool (ipset(8)) also does not offer a simple way to query them. However, they are available when issuing a "list" command.

    It seems that the information you want is only queried on-the-fly by the library when needed and then deleted, leaving no trace. This means that even after executing something like ipset_cmd(session, IPSET_CMD_LIST, 0), checking with ipset_session_data_get(session, IPSET_OPT_TIMEOUT) will yield absolutely nothing! Pretty funny.

    The "proper" way to obtain this information would be to use a raw netlink socket and exchange the appropriate messages directly with the ipset netlink subsystem (NFNL_SUBSYS_IPSET), which is what libipset does under the hood, but that's quite the predicament...

    However, since the library allows you to create a "session" registering a callback function to call for printing information, you can [ab]use this to get what you want.

    Here's a working example:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <libipset/types.h>
    #include <libipset/session.h>
    
    struct info {
        uint32_t timeout;
        bool ok;
    };
    
    /* Callback print function executed by libipset when information is available.
     * Extract whatever information is needed and store it through the private data
     * pointer.
     */
    static int dummy_print_fn(struct ipset_session *session, void *data, const char *_fmt, ...) {
        struct info *out = data;
    
        const uint32_t *ptr = ipset_session_data_get(session, IPSET_OPT_TIMEOUT);
        if (ptr != NULL) {
            out->timeout = *ptr;
            out->ok = true;
        } else {
            out->ok = false;
        }
    
        return 0;
    }
    
    int main(int argc, char* argv[]) {
        ipset_load_types();
        struct ipset_session *session;
    
        /* Passed as private data pointer to dummy_print_fn. */
        struct info info;
    
        /* Register dummy print callback and &info as private data ptr. */
        if ((session = ipset_session_init(dummy_print_fn, &info)) == NULL) {
            printf("Error: Can't initialize session\n");
            exit(1);
        }
    
        if (ipset_session_data_set(session, IPSET_SETNAME, "my_set") != 0) {
            printf("Error: Can't give set name\n");
            exit(1);
        }
    
        /* Validate set exists */
        if (ipset_type_get(session, IPSET_CMD_TEST) == NULL) {
            printf("Error: Can't find set\n");
            exit(1);
        }
    
        /* Execute a list command to get the information you want through the
         * registered callback function.
         */
        if (ipset_cmd(session, IPSET_CMD_LIST, 0) != 0) {
            printf("Error: list command failed\n");
            exit(1);
        }
    
        if (info.ok)
            printf("Timeout: %u\n", info.timeout);
        else
            printf("Timeout: field unavailable\n");
    
        return 0;
    }