Hi Ian, On Wed, 21 Aug 2019, Ian Pilcher wrote: > I would like to write a very simple daemon that exists only to add IPv4 > and IPv6 addresses to an ipset. (The daemon will run on my firewall and > be used to block IPs that are identified by fail2ban on a separate web > server.) > > Writing a fail2ban "action" to send an IP address over a socket should > be quite straightforward, but I haven't been able to figure out a good > way for the daemon (written in C) to add the IP to the set. Looking at > the libipset documentation[1], it seems to require passing in an > ipset(8)-style command string. While the daemon certainly could create > such a string and pass it to the library, this seems painfully > inelegant. > > The lower level interface seems to be libmnl. Unfortunately, I can't > really find anything that I (as a netlink newbie) can follow that tells > me how I might go about doing this at that level. > > Does anyone have any suggestions on how I might go about figuring this > out? There are multiple possibilities at different levels of complexity/simplicity, looking at it from different angles :-). - You could fork ipset in restore mode and push the commands through its stdin. Do not forget to issue the "COMMIT" pseudo-command, otherwise the commands are aggregated to fit as much as possible into a page and then sending the full buffer to the kernel for execution. - You could use libipset - the parsing is not a big overhead. Admittedly it's not elegant to print the elements internally then parse back the string, but it's the simplest way to use the library. - You can use the undocumented but public interfaces of libipset in order to skip the parsing. You must load in the set types, init a session and at the end fini. But instead of calling the ipset_parse_foo() functions, what you can do is something like this: # get reused data pointer belongig to the session struct ipset_data *data = ipset_session_data(session); # Assume IPv4... uint8_t family = NFPROTO_IPV4; # Build the command attributes: setname ipset_parse_setname(session, IPSET_SETNAME, "setname"); # family ipset_data_set(data, IPSET_OPT_FAMILY, &family); # element ipset_data_set(data, IPSET_OPT_IP, &ipaddr_in_netorder); # other parts of the element, if any... ... # Tell the system it's an add command ipset_cmd_session, IPSET_CMD_ADD, 0); # commit ipset_commit(session); Error checking is left out completely... you should check the source code. - Also, you could use libmnl directly. In the libmnl source tree there's an examples/ subdirectory to start with. Pablo wrote a nice article about netlink and libmnl: https://people.netfilter.org/pablo/netlink/netlink-libmnl-manual.pdf However, on top of that you must follow the ipset protocol which is not documented: the lib/PROTOCOL helps a bit but it's just a bare skeleton. Best regards, Jozsef - E-mail : kadlec@xxxxxxxxxxxxxxxxx, kadlecsik.jozsef@xxxxxxxxxxxxx PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences H-1525 Budapest 114, POB. 49, Hungary