]> granicus.if.org Git - ipset/commitdiff
Fourth stage to ipset-5
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Thu, 22 Apr 2010 14:52:29 +0000 (16:52 +0200)
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Thu, 22 Apr 2010 14:52:29 +0000 (16:52 +0200)
Add new userspace files: include/, lib/ and plus new files in src/.

29 files changed:
include/Makefile.am [new file with mode: 0644]
include/libipset/Makefile.am [new file with mode: 0644]
include/libipset/data.h [new file with mode: 0644]
include/libipset/errcode.h [new file with mode: 0644]
include/libipset/linux_ip_set.h [new file with mode: 0644]
include/libipset/linux_ip_set_bitmap.h [new file with mode: 0644]
include/libipset/linux_ip_set_hash.h [new file with mode: 0644]
include/libipset/mnl.h [new file with mode: 0644]
include/libipset/nf_inet_addr.h [new file with mode: 0644]
include/libipset/parse.h [new file with mode: 0644]
include/libipset/pfxlen.h [new file with mode: 0644]
include/libipset/print.h [new file with mode: 0644]
include/libipset/session.h [new file with mode: 0644]
include/libipset/transport.h [new file with mode: 0644]
include/libipset/types.h [new file with mode: 0644]
include/libipset/ui.h [new file with mode: 0644]
include/libipset/utils.h [new file with mode: 0644]
lib/Makefile.am [new file with mode: 0644]
lib/PROTOCOL [new file with mode: 0644]
lib/data.c [new file with mode: 0644]
lib/mnl.c [new file with mode: 0644]
lib/parse.c [new file with mode: 0644]
lib/print.c [new file with mode: 0644]
lib/session.c [new file with mode: 0644]
lib/types.c [new file with mode: 0644]
lib/utils.c [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/errcode.c [new file with mode: 0644]
src/ui.c [new file with mode: 0644]

diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644 (file)
index 0000000..2c4eb5c
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = libipset
diff --git a/include/libipset/Makefile.am b/include/libipset/Makefile.am
new file mode 100644 (file)
index 0000000..b17293c
--- /dev/null
@@ -0,0 +1,17 @@
+pkginclude_HEADERS = \
+       data.h \
+       errcode.h \
+       linux_ip_set_bitmap.h \
+       linux_ip_set.h \
+       linux_ip_set_hash.h \
+       mnl.h \
+       nf_inet_addr.h \
+       nlattr.h \
+       parse.h \
+       pfxlen.h \
+       print.h \
+       session.h
+       transport.h \
+       types.h \
+       ui.h \
+       utils.h
diff --git a/include/libipset/data.h b/include/libipset/data.h
new file mode 100644 (file)
index 0000000..0ebc1eb
--- /dev/null
@@ -0,0 +1,124 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_DATA_H
+#define LIBIPSET_DATA_H
+
+#include <stdbool.h>                           /* bool */
+#include <libipset/nf_inet_addr.h>             /* union nf_inet_addr */
+
+/* Data options */
+enum ipset_opt {
+       IPSET_OPT_NONE = 0,
+       /* Common ones */
+       IPSET_SETNAME,
+       IPSET_OPT_TYPENAME,
+       IPSET_OPT_FAMILY,
+       /* CADT options */
+       IPSET_OPT_IP,
+       IPSET_OPT_IP_FROM = IPSET_OPT_IP,
+       IPSET_OPT_IP_TO,
+       IPSET_OPT_CIDR,
+       IPSET_OPT_PORT,
+       IPSET_OPT_PORT_FROM = IPSET_OPT_PORT,
+       IPSET_OPT_PORT_TO,
+       IPSET_OPT_TIMEOUT,
+       /* Create-specific options */
+       IPSET_OPT_GC,
+       IPSET_OPT_HASHSIZE,
+       IPSET_OPT_MAXELEM,
+       IPSET_OPT_NETMASK,
+       IPSET_OPT_PROBES,
+       IPSET_OPT_RESIZE,
+       IPSET_OPT_SIZE,
+       /* Create-specific options, filled out by the kernel */
+       IPSET_OPT_ELEMENTS,
+       IPSET_OPT_REFERENCES,
+       IPSET_OPT_MEMSIZE,
+       /* ADT-specific options */
+       IPSET_OPT_ETHER,
+       IPSET_OPT_NAME,
+       IPSET_OPT_NAMEREF,
+       IPSET_OPT_IP2,
+       IPSET_OPT_CIDR2,
+       /* Swap/rename to */
+       IPSET_OPT_SETNAME2,
+       /* Flags */
+       IPSET_OPT_EXIST,
+       IPSET_OPT_BEFORE,
+       /* Internal options */
+       IPSET_OPT_FLAGS = 48,
+       IPSET_OPT_ELEM,
+       IPSET_OPT_TYPE,
+       IPSET_OPT_LINENO,
+       IPSET_OPT_REVISION,
+       IPSET_OPT_REVISION_MIN,
+       IPSET_OPT_MAX,
+};
+
+#define IPSET_FLAG(opt)                (1LL << (opt))
+#define IPSET_FLAGS_ALL                (~0LL)
+
+#define IPSET_CREATE_FLAGS             \
+       ( IPSET_FLAG(IPSET_OPT_IP)      \
+       | IPSET_FLAG(IPSET_OPT_IP_TO)   \
+       | IPSET_FLAG(IPSET_OPT_CIDR)    \
+       | IPSET_FLAG(IPSET_OPT_PORT)    \
+       | IPSET_FLAG(IPSET_OPT_PORT_TO) \
+       | IPSET_FLAG(IPSET_OPT_TIMEOUT) \
+       | IPSET_FLAG(IPSET_OPT_GC)      \
+       | IPSET_FLAG(IPSET_OPT_HASHSIZE)\
+       | IPSET_FLAG(IPSET_OPT_MAXELEM) \
+       | IPSET_FLAG(IPSET_OPT_NETMASK) \
+       | IPSET_FLAG(IPSET_OPT_PROBES)  \
+       | IPSET_FLAG(IPSET_OPT_RESIZE)  \
+       | IPSET_FLAG(IPSET_OPT_SIZE))
+
+#define IPSET_ADT_FLAGS                        \
+       ( IPSET_FLAG(IPSET_OPT_IP)      \
+       | IPSET_FLAG(IPSET_OPT_IP_TO)   \
+       | IPSET_FLAG(IPSET_OPT_CIDR)    \
+       | IPSET_FLAG(IPSET_OPT_PORT)    \
+       | IPSET_FLAG(IPSET_OPT_PORT_TO) \
+       | IPSET_FLAG(IPSET_OPT_TIMEOUT) \
+       | IPSET_FLAG(IPSET_OPT_ETHER)   \
+       | IPSET_FLAG(IPSET_OPT_NAME)    \
+       | IPSET_FLAG(IPSET_OPT_NAMEREF) \
+       | IPSET_FLAG(IPSET_OPT_IP2)     \
+       | IPSET_FLAG(IPSET_OPT_CIDR2)   \
+       | IPSET_FLAG(IPSET_OPT_BEFORE))
+
+struct ipset_data;
+
+extern bool ipset_data_flags_test(const struct ipset_data *data,
+                                 uint64_t flags);
+extern void ipset_data_flags_set(struct ipset_data *data, uint64_t flags);
+extern void ipset_data_flags_unset(struct ipset_data *data, uint64_t flags);
+
+extern int ipset_data_set(struct ipset_data *data, enum ipset_opt opt,
+                         const void *value);
+extern const void * ipset_data_get(const struct ipset_data *data,
+                                  enum ipset_opt opt);
+
+static inline bool
+ipset_data_test(const struct ipset_data *data, enum ipset_opt opt)
+{
+       return ipset_data_flags_test(data, IPSET_FLAG(opt));
+}
+
+/* Shortcuts */
+extern const char * ipset_data_setname(const struct ipset_data *data);
+extern uint8_t ipset_data_family(const struct ipset_data *data);
+extern uint8_t ipset_data_cidr(const struct ipset_data *data);
+extern uint64_t ipset_data_flags(const struct ipset_data *data);
+
+extern void ipset_data_reset(struct ipset_data *data);
+extern struct ipset_data * ipset_data_init(void);
+extern void ipset_data_fini(struct ipset_data *data);
+
+extern size_t ipset_data_sizeof(enum ipset_opt opt, uint8_t family);
+
+#endif /* LIBIPSET_DATA_H */
diff --git a/include/libipset/errcode.h b/include/libipset/errcode.h
new file mode 100644 (file)
index 0000000..5ad41ff
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright 2007-2008 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_ERRCODE_H
+#define LIBIPSET_ERRCODE_H
+
+#include <libipset/linux_ip_set.h>             /* enum ipset_cmd */
+
+struct ipset_session;
+
+struct ipset_errcode_table {
+       int errcode;
+       enum ipset_cmd cmd;
+       const char *message;
+};
+
+extern int ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd,
+                        int errcode);
+
+#endif /* LIBIPSET_ERRCODE_H */
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
new file mode 100644 (file)
index 0000000..254fb21
--- /dev/null
@@ -0,0 +1,171 @@
+#ifndef _IP_SET_H
+#define _IP_SET_H
+
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.  
+ */
+
+#if 1
+#define IP_SET_DEBUG
+#endif
+
+/* The protocol version */
+#define IPSET_PROTOCOL         5
+
+/* The max length of strings: set and type identifiers */
+#define IPSET_MAXNAMELEN       32
+
+/* Message types and commands */
+enum ipset_cmd {
+       IPSET_CMD_NONE,
+       IPSET_CMD_CREATE,       /* Create a new (empty) set */
+       IPSET_CMD_DESTROY,      /* Remove a (empty) set */
+       IPSET_CMD_FLUSH,        /* Remove all elements from a set */
+       IPSET_CMD_RENAME,       /* Rename a set */
+       IPSET_CMD_SWAP,         /* Swap two sets */
+       IPSET_CMD_LIST,         /* List sets */
+       IPSET_CMD_SAVE,         /* Save sets */
+       IPSET_CMD_ADD,          /* Add an element to a set */
+       IPSET_CMD_DEL,          /* Delete an element from a set */
+       IPSET_CMD_TEST,         /* Test an element in a set */
+       IPSET_CMD_HEADER,       /* Get set header data only */
+       IPSET_CMD_TYPE,         /* Get set type */
+       IPSET_CMD_PROTOCOL,     /* Return protocol version */
+       IPSET_MSG_MAX,          /* Netlink message commands */
+
+       /* Commands in userspace: */
+       IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */     
+       IPSET_CMD_HELP,         /* Get help */
+       IPSET_CMD_VERSION,      /* Get program version */
+
+       IPSET_CMD_MAX,
+
+       IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* Commit buffered commands */
+};
+
+/* Attributes at command level */
+enum {
+       IPSET_ATTR_UNSPEC,
+       IPSET_ATTR_PROTOCOL,    /* Protocol version */
+       IPSET_ATTR_SETNAME,     /* Name of the set */
+       IPSET_ATTR_TYPENAME,    /* Typename */
+       IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */
+       IPSET_ATTR_REVISION,    /* Settype revision */
+       IPSET_ATTR_FAMILY,      /* Settype family */
+       IPSET_ATTR_DATA,        /* Nested attributes */
+       IPSET_ATTR_ADT,         /* Multiple data containers */
+       IPSET_ATTR_LINENO,      /* Restore lineno */
+       IPSET_ATTR_PROTOCOL_MIN,/* Minimal supported version number */
+       IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
+       __IPSET_ATTR_CMD_MAX,
+};
+#define IPSET_ATTR_CMD_MAX     (__IPSET_ATTR_CMD_MAX - 1)
+
+/* CADT specific attributes */
+enum {
+       IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1,
+       IPSET_ATTR_IP_FROM = IPSET_ATTR_IP,
+       IPSET_ATTR_IP_TO,
+       IPSET_ATTR_CIDR,
+       IPSET_ATTR_PORT,
+       IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
+       IPSET_ATTR_PORT_TO,
+       IPSET_ATTR_TIMEOUT,
+       IPSET_ATTR_FLAGS,
+       /* IPSET_ATTR_LINENO */
+       /* Reserve empty slots */
+       IPSET_ATTR_CADT_MAX = 16,
+       /* Create-only specific attributes */
+       IPSET_ATTR_GC,
+       IPSET_ATTR_HASHSIZE,
+       IPSET_ATTR_MAXELEM,
+       IPSET_ATTR_NETMASK,
+       IPSET_ATTR_PROBES,
+       IPSET_ATTR_RESIZE,
+       IPSET_ATTR_SIZE,
+       /* Kernel-only */
+       IPSET_ATTR_ELEMENTS,
+       IPSET_ATTR_REFERENCES,
+       IPSET_ATTR_MEMSIZE,
+       
+       __IPSET_ATTR_CREATE_MAX,
+};
+#define IPSET_ATTR_CREATE_MAX  (__IPSET_ATTR_CREATE_MAX - 1)
+
+/* ADT specific attributes */
+enum {
+       IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1,
+       IPSET_ATTR_NAME,
+       IPSET_ATTR_NAMEREF,
+       IPSET_ATTR_IP2,
+       IPSET_ATTR_CIDR2,
+       __IPSET_ATTR_ADT_MAX,
+};
+#define IPSET_ATTR_ADT_MAX     (__IPSET_ATTR_ADT_MAX - 1)
+
+/* Error codes */
+enum ipset_errno {
+       IPSET_ERR_PRIVATE = 128,
+       IPSET_ERR_PROTOCOL,
+       IPSET_ERR_FIND_TYPE,
+       IPSET_ERR_MAX_SETS,
+       IPSET_ERR_BUSY,
+       IPSET_ERR_EXIST_SETNAME2,
+       IPSET_ERR_TYPE_MISMATCH,
+       IPSET_ERR_EXIST,
+       IPSET_ERR_INVALID_CIDR,
+       IPSET_ERR_INVALID_NETMASK,
+       IPSET_ERR_INVALID_FAMILY,
+       IPSET_ERR_TIMEOUT,
+
+       IPSET_ERR_TYPE_SPECIFIC = 160,
+};
+                                       
+enum ipset_data_flags {
+       IPSET_FLAG_BIT_EXIST    = 0,
+       IPSET_FLAG_EXIST        = (1 << IPSET_FLAG_BIT_EXIST),
+       
+       IPSET_FLAG_BIT_BEFORE   = 2,
+       IPSET_FLAG_BEFORE       = (1 << IPSET_FLAG_BIT_BEFORE),
+};
+
+/* Commands with settype-specific attributes */
+enum ipset_adt {
+       IPSET_ADD,
+       IPSET_DEL,
+       IPSET_TEST,
+       IPSET_CREATE,
+       IPSET_CADT_MAX,
+};
+
+#ifndef __KERNEL__
+#ifdef IP_SET_DEBUG
+#include <stdio.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#define D(format, args...)     do {                            \
+       fprintf(stderr, "%s: %s: ", __FILE__, __FUNCTION__);    \
+       fprintf(stderr, format "\n" , ## args);                 \
+} while (0)
+static inline void
+dump_nla(struct  nlattr *nla[], int maxlen)
+{
+       int i;
+       
+       for (i = 0; i < maxlen; i++)
+               D("nla[%u] does%s exist", i, !nla[i] ? " NOT" : "");
+}
+
+#else
+#define D(format, args...)
+#define dump_nla(nla, maxlen)
+#endif
+#endif /* !__KERNEL__ */
+
+#endif /* __IP_SET_H */
diff --git a/include/libipset/linux_ip_set_bitmap.h b/include/libipset/linux_ip_set_bitmap.h
new file mode 100644 (file)
index 0000000..01ea534
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __IP_SET_BITMAP_H
+#define __IP_SET_BITMAP_H
+
+/* Bitmap type specific error codes */
+enum {
+       IPSET_ERR_BITMAP_RANGE = IPSET_ERR_TYPE_SPECIFIC,
+       IPSET_ERR_BITMAP_RANGE_SIZE,
+};
+
+#endif /* __IP_SET_BITMAP_H */
diff --git a/include/libipset/linux_ip_set_hash.h b/include/libipset/linux_ip_set_hash.h
new file mode 100644 (file)
index 0000000..76d2489
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __IP_SET_HASH_H
+#define __IP_SET_HASH_H
+
+/* Bitmap type specific error codes */
+enum {
+       IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC,
+       IPSET_ERR_HASH_ELEM,
+};
+
+#endif /* __IP_SET_HASH_H */
diff --git a/include/libipset/mnl.h b/include/libipset/mnl.h
new file mode 100644 (file)
index 0000000..c2b6d4c
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_MNL_H
+#define LIBIPSET_MNL_H
+
+#include <stdint.h>                            /* uintxx_t */
+#include <libmnl/libmnl.h>                     /* libmnl backend */
+
+#include <libipset/transport.h>                        /* struct ipset_transport */
+
+#ifndef NFNETLINK_V0
+#define NFNETLINK_V0           0
+
+struct nfgenmsg {
+       uint8_t nfgen_family;
+       uint8_t version;
+       uint16_t res_id;
+};
+#endif
+
+extern int ipset_get_nlmsg_type(const struct nlmsghdr *nlh);
+
+extern const struct ipset_transport ipset_mnl_transport;
+
+#endif /* LIBIPSET_MNL_H */
diff --git a/include/libipset/nf_inet_addr.h b/include/libipset/nf_inet_addr.h
new file mode 100644 (file)
index 0000000..91f1914
--- /dev/null
@@ -0,0 +1,22 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_NF_INET_ADDR_H
+#define LIBIPSET_NF_INET_ADDR_H
+
+#include <stdint.h>                            /* uint32_t */
+#include <netinet/in.h>                                /* struct in[6]_addr */
+
+/* The same structure to hold IP addresses as in linux/netfilter.h */
+union nf_inet_addr {
+       uint32_t        all[4];
+       uint32_t        ip;
+       uint32_t        ip6[4];
+       struct in_addr  in;
+       struct in6_addr in6;
+};
+
+#endif /* LIBIPSET_NF_INET_ADDR_H */
diff --git a/include/libipset/parse.h b/include/libipset/parse.h
new file mode 100644 (file)
index 0000000..09c1db4
--- /dev/null
@@ -0,0 +1,57 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_PARSE_H
+#define LIBIPSET_PARSE_H
+
+#include <libipset/data.h>                     /* enum ipset_opt */
+
+/* For parsing/printing data */
+#define IPSET_CIDR_SEPARATOR   "/"
+#define IPSET_RANGE_SEPARATOR  "-"
+#define IPSET_ELEM_SEPARATOR   ","
+#define IPSET_NAME_SEPARATOR   ","
+
+struct ipset_session;
+
+extern int ipset_parse_ether(struct ipset_session *session,
+                             enum ipset_opt opt, const char *str);
+extern int ipset_parse_single_port(struct ipset_session *session,
+                                  enum ipset_opt opt, const char *str);
+extern int ipset_parse_port(struct ipset_session *session,
+                            enum ipset_opt opt, const char *str);
+extern int ipset_parse_family(struct ipset_session *session,
+                              int opt, const char *str);
+extern int ipset_parse_ip(struct ipset_session *session,
+                          enum ipset_opt opt, const char *str);
+extern int ipset_parse_single_ip(struct ipset_session *session,
+                                enum ipset_opt opt, const char *str);
+extern int ipset_parse_net(struct ipset_session *session,
+                           enum ipset_opt opt, const char *str);
+extern int ipset_parse_range(struct ipset_session *session,
+                             enum ipset_opt opt, const char *str);
+extern int ipset_parse_netrange(struct ipset_session *session,
+                               enum ipset_opt opt, const char *str);
+extern int ipset_parse_name(struct ipset_session *session,
+                            enum ipset_opt opt, const char *str);
+extern int ipset_parse_setname(struct ipset_session *session,
+                               enum ipset_opt opt, const char *str);
+extern int ipset_parse_uint32(struct ipset_session *session,
+                              enum ipset_opt opt, const char *str);
+extern int ipset_parse_uint8(struct ipset_session *session,
+                             enum ipset_opt opt, const char *str);
+extern int ipset_parse_netmask(struct ipset_session *session,
+                               enum ipset_opt opt, const char *str);
+extern int ipset_parse_flag(struct ipset_session *session,
+                            enum ipset_opt opt, const char *str);
+extern int ipset_parse_typename(struct ipset_session *session,
+                               enum ipset_opt opt, const char *str);
+extern int ipset_parse_output(struct ipset_session *session,
+                              int opt, const char *str);
+extern int ipset_parse_elem(struct ipset_session *session,
+                            enum ipset_opt opt, const char *str);
+
+#endif /* LIBIPSET_PARSE_H */
diff --git a/include/libipset/pfxlen.h b/include/libipset/pfxlen.h
new file mode 100644 (file)
index 0000000..ba94dd9
--- /dev/null
@@ -0,0 +1,157 @@
+#ifndef _NET_PFXLEN_H
+#define _NET_PFXLEN_H 1
+
+#include <asm/byteorder.h>
+#ifdef HAVE_PFXLEN_H
+#include <linux/netfilter/pfxlen.h>
+#else
+
+#include <libipset/nf_inet_addr.h>     /* union nf_inet_addr */
+
+#define E(a, b, c, d) \
+       {.ip6 = { \
+               __constant_htonl(a), __constant_htonl(b), \
+               __constant_htonl(c), __constant_htonl(d), \
+       }}
+
+/*
+ * This table works for both IPv4 and IPv6;
+ * just use prefixlen_netmask_map[prefixlength].ip.
+ */
+const union nf_inet_addr prefixlen_netmask_map[] = {
+       E(0x00000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0x80000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xC0000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xE0000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xF0000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xF8000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFC000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFE000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFF000000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFF800000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE),
+       E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF),
+};
+#endif /* !HAVE_PFXLEN_H */
+
+#define PFXLEN(n)      prefixlen_netmask_map[n].ip
+#define PFXLEN6(n)     prefixlen_netmask_map[n].ip6
+
+#endif
diff --git a/include/libipset/print.h b/include/libipset/print.h
new file mode 100644 (file)
index 0000000..343386b
--- /dev/null
@@ -0,0 +1,49 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_PRINT_H
+#define LIBIPSET_PRINT_H
+
+#include <libipset/data.h>                     /* enum ipset_opt */
+
+extern int ipset_print_ether(char *buf, unsigned int len,
+                            const struct ipset_data *data, enum ipset_opt opt,
+                            uint8_t env);
+extern int ipset_print_family(char *buf, unsigned int len,
+                             const struct ipset_data *data, int opt,
+                             uint8_t env);
+extern int ipset_print_type(char *buf, unsigned int len,
+                           const struct ipset_data *data, enum ipset_opt opt,
+                           uint8_t env);
+extern int ipset_print_ip(char *buf, unsigned int len,
+                         const struct ipset_data *data, enum ipset_opt opt,
+                         uint8_t env);
+extern int ipset_print_ipaddr(char *buf, unsigned int len,
+                             const struct ipset_data *data, enum ipset_opt opt,
+                             uint8_t env);
+extern int ipset_print_number(char *buf, unsigned int len,
+                             const struct ipset_data *data, enum ipset_opt opt,
+                             uint8_t env);
+extern int ipset_print_name(char *buf, unsigned int len,
+                           const struct ipset_data *data, enum ipset_opt opt,
+                           uint8_t env);
+extern int ipset_print_port(char *buf, unsigned int len,
+                           const struct ipset_data *data, enum ipset_opt opt,
+                           uint8_t env);
+extern int ipset_print_flag(char *buf, unsigned int len,
+                           const struct ipset_data *data, enum ipset_opt opt,
+                           uint8_t env);
+extern int ipset_print_elem(char *buf, unsigned int len,
+                           const struct ipset_data *data, enum ipset_opt opt,
+                           uint8_t env);
+
+#define ipset_print_portnum    ipset_print_number
+
+extern int ipset_print_data(char *buf, unsigned int len,
+                           const struct ipset_data *data, enum ipset_opt opt,
+                           uint8_t env);
+
+#endif /* LIBIPSET_PRINT_H */
diff --git a/include/libipset/session.h b/include/libipset/session.h
new file mode 100644 (file)
index 0000000..71b8e02
--- /dev/null
@@ -0,0 +1,81 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_SESSION_H
+#define LIBIPSET_SESSION_H
+
+#include <stdbool.h>                           /* bool */
+#include <stdint.h>                            /* uintxx_t */
+#include <stdio.h>                             /* printf */
+
+#include <libipset/linux_ip_set.h>             /* enum ipset_cmd */
+#include <libipset/ui.h>                       /* enum ipset_envopt */
+
+/* Report and output buffer sizes */
+#define IPSET_ERRORBUFLEN              1024
+#define IPSET_OUTBUFLEN                        8192
+
+struct ipset_session;
+struct ipset_data;
+struct ipset_handle;
+
+extern struct ipset_data * ipset_session_data(const struct ipset_session *session);
+extern struct ipset_handle * ipset_session_handle(const struct ipset_session *session);
+
+enum ipset_err_type {
+       IPSET_ERROR,
+       IPSET_WARNING,
+};
+
+extern int ipset_session_report(struct ipset_session *session,
+                               enum ipset_err_type type,
+                               const char *fmt, ...);
+
+#define ipset_err(session, fmt, args...) \
+       ipset_session_report(session, IPSET_ERROR, fmt , ## args)
+
+#define ipset_warn(session, fmt, args...) \
+       ipset_session_report(session, IPSET_WARNING, fmt , ## args)
+
+#define ipset_errptr(session, fmt, args...) ({                         \
+       ipset_session_report(session, IPSET_ERROR, fmt , ## args);      \
+       NULL;                                                           \
+})
+
+extern void ipset_session_report_reset(struct ipset_session *session);
+extern const char * ipset_session_error(const struct ipset_session *session);
+extern const char * ipset_session_warning(const struct ipset_session *session);
+
+#define ipset_session_data_set(session, opt, value)    \
+       ipset_data_set(ipset_session_data(session), opt, value)
+#define ipset_session_data_get(session, opt)           \
+       ipset_data_get(ipset_session_data(session), opt)
+
+enum ipset_output_mode {
+       IPSET_LIST_NONE,
+       IPSET_LIST_PLAIN,
+       IPSET_LIST_SAVE,
+       IPSET_LIST_XML,
+};
+
+extern int ipset_envopt_parse(struct ipset_session *session,
+                             int env, const char *str);
+extern bool ipset_envopt_test(struct ipset_session *session,
+                             enum ipset_envopt env);
+extern int ipset_session_output(struct ipset_session *session,
+                               enum ipset_output_mode mode);
+
+extern int ipset_commit(struct ipset_session *session);
+extern int ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd,
+                    uint32_t lineno);
+
+typedef int (*ipset_outfn)(const char *fmt, ...)
+       __attribute__ ((format (printf, 1, 2)));
+
+extern struct ipset_session * ipset_session_init(ipset_outfn outfn);
+extern int ipset_session_fini(struct ipset_session *session);
+
+#endif /* LIBIPSET_SESSION_H */
diff --git a/include/libipset/transport.h b/include/libipset/transport.h
new file mode 100644 (file)
index 0000000..b22e073
--- /dev/null
@@ -0,0 +1,27 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_TRANSPORT_H
+#define LIBIPSET_TRANSPORT_H
+
+#include <stdint.h>                            /* uintxx_t */
+#include <linux/netlink.h>                     /* struct nlmsghdr  */
+
+#include <libmnl/libmnl.h>                     /* mnl_cb_t */
+
+#include <libipset/linux_ip_set.h>             /* enum ipset_cmd */
+
+struct ipset_handle;
+
+struct ipset_transport {
+       struct ipset_handle * (*init)(mnl_cb_t *cb_ctl, void *data);
+       int (*fini)(struct ipset_handle *handle);
+       void (*fill_hdr)(struct ipset_handle *handle, enum ipset_cmd cmd,
+                        void *buffer, size_t len, uint8_t envflags);
+       int (*query)(struct ipset_handle *handle, void *buffer, size_t len);
+};
+
+#endif /* LIBIPSET_TRANSPORT_H */
diff --git a/include/libipset/types.h b/include/libipset/types.h
new file mode 100644 (file)
index 0000000..461931a
--- /dev/null
@@ -0,0 +1,128 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_TYPES_H
+#define LIBIPSET_TYPES_H
+
+#include <stdint.h>                            /* uintxx_t */
+
+#include <libipset/data.h>                     /* enum ipset_opt */
+#include <libipset/linux_ip_set.h>             /* IPSET_MAXNAMELEN */
+
+#define AF_INET46              255
+
+/* Family rules:
+ * - AF_UNSPEC:        type is family-neutral
+ * - AF_INET:  type supports IPv4 only
+ * - AF_INET6: type supports IPv6 only
+ * - AF_INET46:        type supports both IPv4 and IPv6
+ */
+
+/* Set dimensions */
+enum {
+       IPSET_DIM_ONE,                  /* foo */
+       IPSET_DIM_TWO,                  /* foo,bar */
+       IPSET_DIM_THREE,                /* foo,bar,fie */
+       IPSET_DIM_MAX,
+};
+
+/* Parser options */
+enum {
+       IPSET_NO_ARG = -1,
+       IPSET_OPTIONAL_ARG,
+       IPSET_MANDATORY_ARG,
+       IPSET_MANDATORY_ARG2,
+};
+
+struct ipset_session;
+
+typedef int (*ipset_parsefn)(struct ipset_session *s,
+                            enum ipset_opt opt, const char *str);
+typedef int (*ipset_printfn)(char *buf, unsigned int len,
+                            const struct ipset_data *data, enum ipset_opt opt,
+                            uint8_t env);
+
+/* Parse and print type-specific arguments */
+struct ipset_arg {
+       const char *name[3];            /* option names */
+       int has_arg;                    /* mandatory/optional/no arg */
+       enum ipset_opt opt;             /* argumentum type */
+       ipset_parsefn parse;            /* parser function */
+       ipset_printfn print;            /* printing function */
+};
+
+/* Type check against the kernel */
+enum {
+       IPSET_KERNEL_MISMATCH = -1,
+       IPSET_KERNEL_CHECK_NEEDED,
+       IPSET_KERNEL_OK,
+};
+
+/* Max sizes for aggregated ADD (and DEL) commands */
+enum {
+       IPSET_MAXSIZE_INET,
+       IPSET_MAXSIZE_INET6,
+       IPSET_MAXSIZE_MAX,
+};
+
+/* How element parts are parsed */
+struct ipset_elem {
+       ipset_parsefn parse;                    /* elem parser function */
+       ipset_printfn print;                    /* elem print function */
+       enum ipset_opt opt;                     /* elem option */
+};
+
+/* The set types in userspace
+ * we could collapse 'args' and 'mandatory' to two-element lists
+ * but for the readability the full list is supported.
+  */
+struct ipset_type {
+       char name[IPSET_MAXNAMELEN];                    /* type name */
+       char alias[IPSET_MAXNAMELEN];                   /* name alias */
+       uint8_t revision;                               /* revision number */
+       uint8_t family;                                 /* supported family */
+       uint8_t dimension;                              /* elem dimension */
+       int8_t kernel_check;                            /* kernel check */
+       bool last_elem_optional;                        /* last element optional */
+       struct ipset_elem elem[IPSET_DIM_MAX];          /* parse elem */
+       const struct ipset_arg *args[IPSET_CADT_MAX];   /* create/ADT args except elem */
+       uint64_t mandatory[IPSET_CADT_MAX];             /* create/ADT mandatory flags */
+       uint64_t full[IPSET_CADT_MAX];                  /* full args flags */
+       size_t maxsize[IPSET_MAXSIZE_MAX];              /* max sizes */
+       const char *usage;                              /* terse usage */
+
+       struct ipset_type *next;
+};
+
+extern int ipset_cache_add(const char *name, const struct ipset_type *type);
+extern int ipset_cache_del(const char *name);
+extern int ipset_cache_rename(const char *from, const char *to);
+extern int ipset_cache_swap(const char *from, const char *to);
+
+extern const struct ipset_type * ipset_type_get(struct ipset_session *session,
+                                               enum ipset_cmd cmd);
+extern const struct ipset_type * ipset_type_check(struct ipset_session *session);
+
+extern int ipset_type_add(struct ipset_type *type);
+extern const struct ipset_type * ipset_types(void);
+extern const char * ipset_typename_resolve(const char *str);
+
+extern int ipset_types_init(void);
+extern void ipset_types_fini(void);
+
+/* The known set types: (typename, revision, family) is unique */
+extern struct ipset_type ipset_bitmap_ip0;
+extern struct ipset_type ipset_bitmap_ipmac0;
+extern struct ipset_type ipset_bitmap_port0;
+extern struct ipset_type ipset_hash_ip0;
+extern struct ipset_type ipset_hash_net0;
+extern struct ipset_type ipset_hash_ipport0;
+extern struct ipset_type ipset_hash_ipportip0;
+extern struct ipset_type ipset_hash_ipportnet0;
+extern struct ipset_type ipset_tree_ip0;
+extern struct ipset_type ipset_list_set0;
+
+#endif /* LIBIPSET_TYPES_H */
diff --git a/include/libipset/ui.h b/include/libipset/ui.h
new file mode 100644 (file)
index 0000000..044e586
--- /dev/null
@@ -0,0 +1,47 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_UI_H
+#define LIBIPSET_UI_H
+
+/* Commands in userspace */
+struct ipset_commands {
+       const char *name[6];
+       const char *help;
+       int has_arg;
+};
+
+extern const struct ipset_commands ipset_commands[];
+
+/* Environment option flags */
+enum ipset_envopt {
+       IPSET_ENV_BIT_SORTED    = 0,
+       IPSET_ENV_SORTED        = (1 << IPSET_ENV_BIT_SORTED),
+       IPSET_ENV_BIT_QUIET     = 1,
+       IPSET_ENV_QUIET         = (1 << IPSET_ENV_BIT_QUIET),
+       IPSET_ENV_BIT_RESOLVE   = 2,
+       IPSET_ENV_RESOLVE       = (1 << IPSET_ENV_BIT_RESOLVE),
+       IPSET_ENV_BIT_EXIST     = 3,
+       IPSET_ENV_EXIST         = (1 << IPSET_ENV_BIT_EXIST),
+};
+
+struct ipset_session;
+struct ipset_data;
+
+/* Environment options */
+struct ipset_envopts {
+       int flag;
+       int has_arg;
+       const char *name[3];
+       const char *help;
+       int (*parse)(struct ipset_session *s, int flag, const char *str);
+       int (*print)(char *buf, unsigned int len,
+                    const struct ipset_data *data, int flag, uint8_t env);
+};
+
+extern const struct ipset_envopts ipset_envopts[];
+
+#endif /* LIBIPSET_UI_H */
diff --git a/include/libipset/utils.h b/include/libipset/utils.h
new file mode 100644 (file)
index 0000000..2d12e91
--- /dev/null
@@ -0,0 +1,45 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_UTILS_H
+#define LIBIPSET_UTILS_H
+
+#include <stdbool.h>                           /* bool */
+#include <string.h>                            /* strcmp */
+#include <netinet/in.h>                                /* struct in[6]_addr */
+
+/* String equality tests */
+#define STREQ(a,b)             (strcmp(a,b) == 0)
+#define STRNEQ(a,b,n)          (strncmp(a,b,n) == 0)
+
+/* Stringify tokens */
+#define _STR(c)                        #c
+#define STR(c)                 _STR(c)
+
+/* Min/max */
+#define MIN(a, b)              (a < b ? a : b)
+#define MAX(a, b)              (a > b ? a : b)
+
+#define UNUSED                 __attribute__ ((unused))
+
+static inline void
+in4cpy(struct in_addr *dest, const struct in_addr *src)
+{
+       dest->s_addr = src->s_addr;
+}
+
+static inline void
+in6cpy(struct in6_addr *dest, const struct in6_addr *src)
+{
+       memcpy(dest, src, sizeof(struct in6_addr));
+}
+
+extern char * ipset_strchr(const char *str, const char *sep);
+extern bool ipset_name_match(const char *arg, const char * const name[]);
+extern void ipset_shift_argv(int *argc, char *argv[], int from);
+extern void ipset_strncpy(char *dst, const char *src, size_t len);
+
+#endif /* LIBIPSET_UTILS_H */
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644 (file)
index 0000000..74b6651
--- /dev/null
@@ -0,0 +1,20 @@
+include $(top_srcdir)/Make_global.am
+
+AM_CFLAGS += -fPIC
+LIBS =
+
+lib_LTLIBRARIES = libipset.la
+
+libipset_la_LDFLAGS = -version-info $(LIBVERSION)
+libipset_la_SOURCES = \
+       data.c \
+       mnl.c \
+       parse.c \
+       print.c \
+       session.c \
+       types.c \
+       utils.c
+
+
+#%.o: %.c
+#      ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} ${CFLAGS} -o $@ -c $<
diff --git a/lib/PROTOCOL b/lib/PROTOCOL
new file mode 100644 (file)
index 0000000..e1a139e
--- /dev/null
@@ -0,0 +1,84 @@
+req:   msg:    IPSET_CMD_PROTOCOL
+       attr:   IPSET_ATTR_PROTOCOL
+
+resp:  attr:   IPSET_ATTR_PROTOCOL     (protocol max)
+               IPSET_ATTR_PROTOCOL_MIN (protocol min, optional)
+
+req:   msg:    IPSET_CMD_CREATE
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_SETNAME
+               IPSET_ATTR_TYPENAME
+               IPSET_ATTR_REVISION
+               IPSET_ATTR_FAMILY
+               IPSET_ATTR_FLAGS
+               IPSET_ATTR_DATA
+                       create-specific-data
+
+resp:  success/error
+
+req:   msg:    IPSET_CMD_DESTROY|IPSET_CMD_FLUSH
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_SETNAME      (optional)
+
+resp:  success/error
+
+req:   msg:    IPSET_CMD_SWAP|IPSET_CMD_RENAME
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_SETNAME
+               IPSET_ATTR_SETNAME2
+
+resp:  success/error
+
+req:   msg:    IPSET_CMD_LIST|SAVE
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_SETNAME      (optional)
+
+resp:  attr:   IPSET_ATTR_DATA
+                       create-specific-data
+               IPSET_ATTR_ADT
+                       IPSET_ATTR_DATA
+                               adt-specific-data
+                       ...
+
+req:   msg:    IPSET_CMD_ADD|DEL
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_SETNAME
+               IPSET_ATTR_FLAGS
+               IPSET_ATTR_LINENO       (for reporting error line back too)
+               IPSET_ATTR_DATA
+                       adt-specific-data
+
+               or
+
+               IPSET_ATTR_ADT
+                       IPSET_ATTR_DATA
+                               adt-specific-data
+                       ...
+
+req:   msg:    IPSET_CMD_TEST
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_SETNAME
+               IPSET_ATTR_FLAGS
+               IPSET_ATTR_DATA
+                       adt-specific-data
+
+resp:  success/error
+
+req:   msg:    IPSET_CMD_HEADER
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_SETNAME
+
+resp:  attr:   IPSET_ATTR_SETNAME
+               IPSET_ATTR_TYPENAME
+               IPSET_ATTR_REVISION
+               IPSET_ATTR_FAMILY
+
+req:   msg:    IPSET_CMD_TYPE
+       attr:   IPSET_ATTR_PROTOCOL
+               IPSET_ATTR_TYPENAME
+               IPSET_ATTR_FAMILY
+
+resp:  attr:   IPSET_ATTR_TYPENAME
+               IPSET_ATTR_FAMILY
+               IPSET_ATTR_REVISION     (version max)
+               IPSET_ATTR_REVISION_MIN (version min, optional)
diff --git a/lib/data.c b/lib/data.c
new file mode 100644 (file)
index 0000000..0de91a1
--- /dev/null
@@ -0,0 +1,505 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <arpa/inet.h>                         /* ntoh* */
+#include <net/ethernet.h>                      /* ETH_ALEN */
+#include <sys/socket.h>                                /* AF_ */
+#include <stdlib.h>                            /* malloc, free */
+#include <string.h>                            /* memset */
+
+#include <libipset/linux_ip_set.h>             /* IPSET_MAXNAMELEN */
+#include <libipset/types.h>                    /* struct ipset_type */
+#include <libipset/utils.h>                    /* inXcpy */
+#include <libipset/data.h>                     /* prototypes */
+
+/* Internal data structure to hold 
+ * a) input data entered by the user or
+ * b) data received from kernel
+ *
+ * We always store the data in host order, *except* IP addresses.
+ */
+
+struct ipset_data {
+       /* Option bits: which fields are set */
+       uint64_t bits;
+       /* Setname  */
+       char setname[IPSET_MAXNAMELEN];
+       const struct ipset_type *type;
+       /* Common CADT options */
+       uint8_t cidr;
+       uint8_t family;
+       uint32_t flags;
+       uint32_t timeout;
+       union nf_inet_addr ip;
+       union nf_inet_addr ip_to;
+       uint16_t port;
+       uint16_t port_to;
+       union {
+               /* RENAME/SWAP */
+               char setname2[IPSET_MAXNAMELEN];
+               /* CREATE/LIST/SAVE */
+               struct {
+                       uint8_t probes;
+                       uint8_t resize;
+                       uint8_t netmask;
+                       uint32_t hashsize;
+                       uint32_t maxelem;
+                       uint32_t gc;
+                       uint32_t size;
+                       /* Filled out by kernel */
+                       uint32_t references;
+                       uint32_t elements;
+                       uint32_t memsize;
+                       char typename[IPSET_MAXNAMELEN];
+                       uint8_t revision_min;
+                       uint8_t revision;
+               } create;
+               /* ADT/LIST/SAVE */
+               struct {
+                       union nf_inet_addr ip2;
+                       uint8_t cidr2;
+                       char ether[ETH_ALEN];
+                       char name[IPSET_MAXNAMELEN];
+                       char nameref[IPSET_MAXNAMELEN];
+               } adt;
+       } u;
+};
+
+static void
+copy_addr(uint8_t family, union nf_inet_addr *ip, const void *value)
+{
+       if (family == AF_INET)
+               in4cpy(&ip->in, (const struct in_addr *)value);
+       else
+               in6cpy(&ip->in6, (const struct in6_addr *)value);
+}
+
+/**
+ * ipset_data_flags_test - test option bits in the data blob
+ * @data: data blob
+ * @flags: the option flags to test
+ *
+ * Returns true if the options are already set in the data blob.
+ */
+bool
+ipset_data_flags_test(const struct ipset_data *data, uint64_t flags)
+{
+       assert(data);
+       return !!(data->bits & flags);
+}
+
+/**
+ * ipset_data_flags_set - set option bits in the data blob
+ * @data: data blob
+ * @flags: the option flags to set
+ *
+ * The function sets the flags in the data blob so that
+ * the corresponding fields are regarded as if filled with proper data.
+ */
+void
+ipset_data_flags_set(struct ipset_data *data, uint64_t flags)
+{
+       assert(data);
+       data->bits |= flags;
+}
+
+/**
+ * ipset_data_flags_unset - unset option bits in the data blob
+ * @data: data blob
+ * @flags: the option flags to unset
+ *
+ * The function unsets the flags in the data blob.
+ * This is the quick way to clear specific fields.
+ */
+void
+ipset_data_flags_unset(struct ipset_data *data, uint64_t flags)
+{
+       assert(data);
+       data->bits &= ~flags;
+}
+
+#define flag_type_attr(data, opt, flag)                                \
+do {                                                           \
+       data->flags |= (1 << flag);                             \
+       opt = IPSET_OPT_FLAGS;                                  \
+} while (0)
+
+/**
+ * ipset_data_set - put data into the data blob
+ * @data: data blob
+ * @opt: the option kind of the data
+ * @value: the value of the data
+ *
+ * Put a given kind of data into the data blob and mark the
+ * option kind as already set in the blob.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
+{
+       assert(data);
+       assert(opt != IPSET_OPT_NONE);
+       assert(value);
+
+       switch (opt) {
+       /* Common ones */
+       case IPSET_SETNAME:
+               ipset_strncpy(data->setname, value, IPSET_MAXNAMELEN);
+               break;
+       case IPSET_OPT_TYPE:
+               data->type = value;
+               break;
+       case IPSET_OPT_FAMILY:
+               data->family = *(const uint8_t *) value;
+               break;
+       /* CADT options */
+       case IPSET_OPT_IP:
+               if (!(data->family == AF_INET || data->family == AF_INET6))
+                       return -1;
+               copy_addr(data->family, &data->ip, value);
+               break;
+       case IPSET_OPT_IP_TO:
+               if (!(data->family == AF_INET || data->family == AF_INET6))
+                       return -1;
+               copy_addr(data->family, &data->ip_to, value);
+               break;
+       case IPSET_OPT_CIDR:
+               data->cidr = *(const uint8_t *) value;
+               break;
+       case IPSET_OPT_PORT:
+               data->port = *(const uint16_t *) value;
+               break;
+       case IPSET_OPT_PORT_TO:
+               data->port_to = *(const uint16_t *) value;
+               break;
+       case IPSET_OPT_TIMEOUT:
+               data->timeout = *(const uint32_t *) value;
+               break;
+       /* Create-specific options */
+       case IPSET_OPT_GC:
+               data->u.create.gc = *(const uint32_t *) value;
+               break;
+       case IPSET_OPT_HASHSIZE:
+               data->u.create.hashsize = *(const uint32_t *) value;
+               break;
+       case IPSET_OPT_MAXELEM:
+               data->u.create.maxelem = *(const uint32_t *) value;
+               break;
+       case IPSET_OPT_NETMASK:
+               data->u.create.netmask = *(const uint8_t *) value;
+               break;
+       case IPSET_OPT_PROBES:
+               data->u.create.probes = *(const uint8_t *) value;
+               break;
+       case IPSET_OPT_RESIZE:
+               data->u.create.resize = *(const uint8_t *) value;
+               break;
+       case IPSET_OPT_SIZE:
+               data->u.create.size = *(const uint32_t *) value;
+               break;
+       /* Create-specific options, filled out by the kernel */
+       case IPSET_OPT_ELEMENTS:
+               data->u.create.elements = *(const uint32_t *) value;
+               break;
+       case IPSET_OPT_REFERENCES:
+               data->u.create.references = *(const uint32_t *) value;
+               break;
+       case IPSET_OPT_MEMSIZE:
+               data->u.create.memsize = *(const uint32_t *) value;
+               break;
+       /* Create-specific options, type */
+       case IPSET_OPT_TYPENAME:
+               ipset_strncpy(data->u.create.typename, value, IPSET_MAXNAMELEN);
+               break;
+       case IPSET_OPT_REVISION:
+               data->u.create.revision = *(const uint8_t *) value;
+               break;
+       case IPSET_OPT_REVISION_MIN:
+               data->u.create.revision_min = *(const uint8_t *) value;
+               break;
+       /* ADT-specific options */
+       case IPSET_OPT_ETHER:
+               memcpy(data->u.adt.ether, value, ETH_ALEN);
+               break;
+       case IPSET_OPT_NAME:
+               ipset_strncpy(data->u.adt.name, value, IPSET_MAXNAMELEN);
+               break;
+       case IPSET_OPT_NAMEREF:
+               ipset_strncpy(data->u.adt.nameref, value, IPSET_MAXNAMELEN);
+               break;
+       case IPSET_OPT_IP2:
+               if (!(data->family == AF_INET || data->family == AF_INET6))
+                       return -1;
+               copy_addr(data->family, &data->u.adt.ip2, value);
+               break;
+       case IPSET_OPT_CIDR2:
+               data->u.adt.cidr2 = *(const uint8_t *) value;
+               break;
+       /* Swap/rename */
+       case IPSET_OPT_SETNAME2:
+               ipset_strncpy(data->u.setname2, value, IPSET_MAXNAMELEN);
+               break;
+       /* flags */
+       case IPSET_OPT_EXIST:
+               flag_type_attr(data, opt, IPSET_FLAG_EXIST);
+               break;
+       case IPSET_OPT_BEFORE:
+               flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
+               break;
+       case IPSET_OPT_FLAGS:
+               data->flags = *(const uint32_t *)value;
+               break;
+       default:
+               return -1;
+       };
+       
+       ipset_data_flags_set(data, IPSET_FLAG(opt));
+       return 0;
+}
+
+/**
+ * ipset_data_get - get data from the data blob
+ * @data: data blob
+ * @opt: option kind of the requested data
+ *
+ * Returns the pointer to the requested kind of data from the data blob
+ * if it is set. If the option kind is not set or is an unkown type,
+ * NULL is returned.
+ */
+const void *
+ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
+{
+       assert(data);
+       assert(opt != IPSET_OPT_NONE);
+       
+       if (opt != IPSET_OPT_TYPENAME && !ipset_data_test(data, opt))
+               return NULL;
+
+       switch (opt) {
+       /* Common ones */
+       case IPSET_SETNAME:
+               return data->setname;
+       case IPSET_OPT_TYPE:
+               return data->type;
+       case IPSET_OPT_TYPENAME:
+               if (ipset_data_test(data, IPSET_OPT_TYPE))
+                       return data->type->name;
+               else if (ipset_data_test(data, IPSET_OPT_TYPENAME))
+                       return data->u.create.typename;
+               return NULL;
+       case IPSET_OPT_FAMILY:
+               return &data->family;
+       /* CADT options */
+       case IPSET_OPT_IP:
+               return &data->ip;
+       case IPSET_OPT_IP_TO:
+                return &data->ip_to;
+       case IPSET_OPT_CIDR:
+               return &data->cidr;
+       case IPSET_OPT_PORT:
+               return &data->port;
+       case IPSET_OPT_PORT_TO:
+               return &data->port_to;
+       case IPSET_OPT_TIMEOUT:
+               return &data->timeout;
+       /* Create-specific options */
+       case IPSET_OPT_GC:
+               return &data->u.create.gc;
+       case IPSET_OPT_HASHSIZE:
+               return &data->u.create.hashsize;
+       case IPSET_OPT_MAXELEM:
+               return &data->u.create.maxelem;
+       case IPSET_OPT_NETMASK:
+               return &data->u.create.netmask;
+       case IPSET_OPT_PROBES:
+               return &data->u.create.probes;
+       case IPSET_OPT_RESIZE:
+               return &data->u.create.resize;
+       case IPSET_OPT_SIZE:
+               return &data->u.create.size;
+       /* Create-specific options, filled out by the kernel */
+       case IPSET_OPT_ELEMENTS:
+               return &data->u.create.elements;
+       case IPSET_OPT_REFERENCES:
+               return &data->u.create.references;
+       case IPSET_OPT_MEMSIZE:
+               return &data->u.create.memsize;
+       /* Create-specific options, TYPE */
+       case IPSET_OPT_REVISION:
+               return &data->u.create.revision;
+       case IPSET_OPT_REVISION_MIN:
+               return &data->u.create.revision_min;
+       /* ADT-specific options */
+       case IPSET_OPT_ETHER:
+               return data->u.adt.ether;
+       case IPSET_OPT_NAME:
+               return data->u.adt.name;
+       case IPSET_OPT_NAMEREF:
+               return data->u.adt.nameref;
+       case IPSET_OPT_IP2:
+               return &data->u.adt.ip2;
+       case IPSET_OPT_CIDR2:
+               return &data->u.adt.cidr2;
+       /* Swap/rename */
+       case IPSET_OPT_SETNAME2:
+               return data->u.setname2;
+       /* flags */
+       case IPSET_OPT_FLAGS:
+       case IPSET_OPT_EXIST:
+       case IPSET_OPT_BEFORE:
+               return &data->flags;
+       default:
+               return NULL;
+       }
+}
+
+/**
+ * ipset_data_sizeof - calculates the size for the type of data
+ * @opt: option kind of the data
+ * @family: INET family
+ *
+ * Returns the size required to store the given option kind.
+ */
+size_t
+ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
+{
+       assert(opt != IPSET_OPT_NONE);
+
+       switch (opt) {
+       case IPSET_OPT_IP:
+       case IPSET_OPT_IP_TO:
+       case IPSET_OPT_IP2:
+               return family == AF_INET ? sizeof(uint32_t)
+                                        : sizeof(struct in6_addr);
+       case IPSET_OPT_PORT:
+       case IPSET_OPT_PORT_TO:
+               return sizeof(uint16_t);
+       case IPSET_SETNAME:
+       case IPSET_OPT_NAME:
+       case IPSET_OPT_NAMEREF:
+               return IPSET_MAXNAMELEN;
+       case IPSET_OPT_TIMEOUT:
+       case IPSET_OPT_GC:
+       case IPSET_OPT_HASHSIZE:
+       case IPSET_OPT_MAXELEM:
+       case IPSET_OPT_SIZE:
+       case IPSET_OPT_ELEMENTS:
+       case IPSET_OPT_REFERENCES:
+       case IPSET_OPT_MEMSIZE:
+               return sizeof(uint32_t);
+       case IPSET_OPT_CIDR:
+       case IPSET_OPT_CIDR2:
+       case IPSET_OPT_NETMASK:
+       case IPSET_OPT_PROBES:
+       case IPSET_OPT_RESIZE:
+               return sizeof(uint8_t);
+       case IPSET_OPT_ETHER:
+               return ETH_ALEN;
+       /* Flags counted once */
+       case IPSET_OPT_BEFORE:
+               return sizeof(uint32_t);
+       default:
+               return 0;
+       };
+}
+
+/**
+ * ipset_setname - return the name of the set from the data blob
+ * @data: data blob
+ *
+ * Return the name of the set from the data blob or NULL if the
+ * name not set yet.
+ */
+const char *
+ipset_data_setname(const struct ipset_data *data)
+{
+       assert(data);
+       return ipset_data_test(data, IPSET_SETNAME) ? data->setname : NULL;
+}
+
+/**
+ * ipset_family - return the INET family of the set from the data blob
+ * @data: data blob
+ *
+ * Return the INET family supported by the set from the data blob.
+ * If the family is not set yet, AF_UNSPEC is returned.
+ */
+uint8_t
+ipset_data_family(const struct ipset_data *data)
+{
+       assert(data);
+       return ipset_data_test(data, IPSET_OPT_FAMILY)
+               ? data->family : AF_UNSPEC;
+}
+
+/**
+ * ipset_data_cidr - return the value of IPSET_OPT_CIDR
+ * @data: data blob
+ *
+ * Return the value of IPSET_OPT_CIDR stored in the data blob.
+ * If it is not set, the the returned value corresponds to
+ * the default one according to the family type or zero.
+ */
+uint8_t
+ipset_data_cidr(const struct ipset_data *data)
+{
+       assert(data);
+       return ipset_data_test(data, IPSET_OPT_CIDR) ? data->cidr : 
+              data->family == AF_INET ? 32 : 
+              data->family == AF_INET6 ? 128 : 0;
+}
+
+/**
+ * ipset_flags - return which fields are set in the data blob
+ * @data: data blob
+ *
+ * Returns the value of the bit field which elements are set.
+ */
+uint64_t
+ipset_data_flags(const struct ipset_data *data)
+{
+       assert(data);
+       return data->bits;
+}
+
+/**
+ * ipset_data_reset - reset the data blob to unset
+ * @data: data blob
+ *
+ * Resets the data blob to the unset state for every field.
+ */
+void
+ipset_data_reset(struct ipset_data *data)
+{
+       assert(data);
+       memset(data, 0, sizeof(*data));
+}
+
+/**
+ * ipset_data_init - create a new data blob
+ *
+ * Return the new data blob initialized to empty. In case of
+ * an error, NULL is retured.
+ */
+struct ipset_data *
+ipset_data_init(void)
+{
+       return calloc(1, sizeof(struct ipset_data));
+}
+
+/**
+ * ipset_data_fini - release a data blob created by ipset_data_init
+ *
+ * Release the data blob created by ipset_data_init previously.
+ */
+void
+ipset_data_fini(struct ipset_data *data)
+{
+       assert(data);
+       free(data);
+}
diff --git a/lib/mnl.c b/lib/mnl.c
new file mode 100644 (file)
index 0000000..5662a47
--- /dev/null
+++ b/lib/mnl.c
@@ -0,0 +1,157 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <errno.h>                             /* errno */
+#include <stdlib.h>                            /* calloc, free */
+#include <time.h>                              /* time */
+
+#include <libipset/linux_ip_set.h>             /* enum ipset_cmd */
+#include <libipset/session.h>                  /* ipset_session_handle */
+#include <libipset/ui.h>                       /* IPSET_ENV_EXIST */
+#include <libipset/utils.h>                    /* UNUSED */
+#include <libipset/mnl.h>                      /* prototypes */
+
+#ifndef NFNL_SUBSYS_IPSET
+#define NFNL_SUBSYS_IPSET      6
+#endif
+
+struct ipset_handle {
+       struct mnl_socket *h;
+       unsigned int seq;
+       unsigned int portid;
+       mnl_cb_t *cb_ctl;
+       void *data;
+};
+
+/* Netlink flags of the commands */
+static uint16_t cmdflags[] = {
+       [IPSET_CMD_CREATE-1]    = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|NLM_F_EXCL,
+       [IPSET_CMD_DESTROY-1]   = NLM_F_REQUEST|NLM_F_ACK,
+       [IPSET_CMD_FLUSH-1]     = NLM_F_REQUEST|NLM_F_ACK,
+       [IPSET_CMD_RENAME-1]    = NLM_F_REQUEST|NLM_F_ACK,
+       [IPSET_CMD_SWAP-1]      = NLM_F_REQUEST|NLM_F_ACK,
+       [IPSET_CMD_LIST-1]      = NLM_F_REQUEST|NLM_F_ROOT|NLM_F_MATCH|NLM_F_DUMP,
+       [IPSET_CMD_SAVE-1]      = NLM_F_REQUEST|NLM_F_ROOT|NLM_F_MATCH|NLM_F_DUMP,
+       [IPSET_CMD_ADD-1]       = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
+       [IPSET_CMD_DEL-1]       = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
+       [IPSET_CMD_TEST-1]      = NLM_F_REQUEST|NLM_F_ACK,
+       [IPSET_CMD_HEADER-1]    = NLM_F_REQUEST,
+       [IPSET_CMD_TYPE-1]      = NLM_F_REQUEST,
+       [IPSET_CMD_PROTOCOL-1]  = NLM_F_REQUEST,
+};
+
+int
+ipset_get_nlmsg_type(const struct nlmsghdr *nlh)
+{
+       return nlh->nlmsg_type & ~(NFNL_SUBSYS_IPSET << 8);
+}
+
+static void
+ipset_mnl_fill_hdr(struct ipset_handle *handle, enum ipset_cmd cmd,
+                  void *buffer, size_t len UNUSED, uint8_t envflags)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfg;
+
+       assert(handle);
+       assert(buffer);
+       assert(cmd > IPSET_CMD_NONE && cmd < IPSET_MSG_MAX);
+
+       nlh = mnl_nlmsg_put_header(buffer);
+       nlh->nlmsg_type = cmd | (NFNL_SUBSYS_IPSET << 8);
+       nlh->nlmsg_flags = cmdflags[cmd - 1];
+       if (envflags & IPSET_ENV_EXIST)
+               nlh->nlmsg_flags &=  ~NLM_F_EXCL;
+       nlh->nlmsg_seq = handle->seq = time(NULL);
+
+       nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+       nfg->nfgen_family = AF_INET;
+       nfg->version = NFNETLINK_V0;
+       nfg->res_id = htons(0);
+}
+
+static int
+ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len)
+{
+       struct nlmsghdr *nlh = buffer;
+       int ret;
+
+       assert(handle);
+       assert(buffer);
+
+       if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0)
+               return -ECOMM;
+
+       D("message sent");
+       ret = mnl_socket_recvfrom(handle->h, buffer, len);
+       D("message received, ret: %d", ret);
+       while (ret > 0) {
+               ret = mnl_cb_run2(buffer, ret,
+                                 handle->seq, handle->portid,
+                                 handle->cb_ctl[NLMSG_MIN_TYPE],
+                                 handle->data,
+                                 handle->cb_ctl, NLMSG_MIN_TYPE);
+               D("nfln_cb_run2, ret: %d", ret);
+               if (ret <= 0)
+                       break;
+               ret = mnl_socket_recvfrom(handle->h, buffer, len);
+               D("message received, ret: %d", ret);
+       }
+       return ret > 0 ? 0 : ret;
+}
+
+static struct ipset_handle *
+ipset_mnl_init(mnl_cb_t *cb_ctl, void *data)
+{      
+       struct ipset_handle *handle;
+       
+       assert(cb_ctl);
+       assert(data);
+
+       handle = calloc(1, sizeof(*handle));
+       if (!handle)
+               return NULL;
+               
+       handle->h = mnl_socket_open(NETLINK_NETFILTER);
+       if (!handle->h)
+               goto free_handle;
+       
+       if (mnl_socket_bind(handle->h, 0, MNL_SOCKET_AUTOPID) < 0)
+               goto close_nl;
+       
+       handle->portid = mnl_socket_get_portid(handle->h);
+       handle->cb_ctl = cb_ctl;
+       handle->data = data;
+       
+       return handle;
+
+close_nl:
+       mnl_socket_close(handle->h);
+free_handle:
+       free(handle);
+
+       return NULL;
+}
+
+static int
+ipset_mnl_fini(struct ipset_handle *handle)
+{
+       assert(handle);
+
+       if (handle->h)
+               mnl_socket_close(handle->h);
+
+       free(handle);
+       return 0;
+}
+
+const struct ipset_transport ipset_mnl_transport = {
+       .init   = ipset_mnl_init,
+       .fini   = ipset_mnl_fini,
+       .fill_hdr = ipset_mnl_fill_hdr,
+       .query  = ipset_mnl_query,
+};
diff --git a/lib/parse.c b/lib/parse.c
new file mode 100644 (file)
index 0000000..0e0e7f1
--- /dev/null
@@ -0,0 +1,963 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <errno.h>                             /* errno */
+#include <limits.h>                            /* ULLONG_MAX */
+#include <netdb.h>                             /* getservbyname, getaddrinfo */
+#include <stdlib.h>                            /* strtoull, etc. */
+#include <sys/types.h>                         /* getaddrinfo */
+#include <sys/socket.h>                                /* getaddrinfo, AF_ */
+#include <net/ethernet.h>                      /* ETH_ALEN */
+
+#include <libipset/data.h>                     /* IPSET_OPT_* */
+#include <libipset/pfxlen.h>                   /* prefixlen_netmask_map */
+#include <libipset/session.h>                  /* ipset_err */
+#include <libipset/types.h>                    /* ipset_type_get */
+#include <libipset/utils.h>                    /* string utilities */
+#include <libipset/parse.h>                    /* prototypes */
+
+/* Parse input data */
+
+#define ipset_cidr_separator(str)      ipset_strchr(str, IPSET_CIDR_SEPARATOR)
+#define ipset_range_separator(str)     ipset_strchr(str, IPSET_RANGE_SEPARATOR)
+#define ipset_elem_separator(str)      ipset_strchr(str, IPSET_ELEM_SEPARATOR)
+#define ipset_name_separator(str)      ipset_strchr(str, IPSET_NAME_SEPARATOR)
+
+#define syntax_err(fmt, args...) \
+       ipset_err(session, "Syntax error: " fmt , ## args)
+
+/* 
+ * Parser functions, shamelessly taken from iptables.c, ip6tables.c 
+ * and parser.c from libnetfilter_conntrack.
+ */
+
+/*
+ * Parse numbers
+ */
+static int
+string_to_number_ll(struct ipset_session *session,
+                   const char *str, 
+                   unsigned long long min,
+                   unsigned long long max,
+                   unsigned long long *ret)
+{
+       unsigned long long number = 0;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtoull(str, &end, 0);
+       if (*end == '\0' && end != str && errno != ERANGE) {
+               /* we parsed a number, let's see if we want this */
+               if (min <= number && (!max || number <= max)) {
+                       *ret = number;
+                       return 0;
+               } else
+                       errno = ERANGE;
+       }
+       if (errno == ERANGE && max)
+               return syntax_err("'%s' is out of range %llu-%llu",
+                                 str, min, max);
+       else if (errno == ERANGE)
+               return syntax_err("'%s' is out of range %llu-%llu",
+                                 str, min, ULLONG_MAX);
+       else
+               return syntax_err("'%s' is invalid as number", str);
+}
+
+static int
+string_to_number_l(struct ipset_session *session,
+                  const char *str, 
+                  unsigned long min,
+                  unsigned long max,
+                  unsigned long *ret)
+{
+       int err;
+       unsigned long long number = 0;
+
+       err = string_to_number_ll(session, str, min, max, &number);
+       *ret = (unsigned long) number;
+
+       return err;
+}
+
+static int
+string_to_number(struct ipset_session *session,
+                const char *str, 
+                unsigned int min, 
+                unsigned int max,
+                unsigned int *ret)
+{
+       int err;
+       unsigned long number = 0;
+
+       err = string_to_number_l(session, str, min, max, &number);
+       *ret = (unsigned int) number;
+
+       return err;
+}
+
+/**
+ * ipset_parse_ether - parse ethernet address
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an ethernet address. The parsed ethernet
+ * address is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_ether(struct ipset_session *session,
+                 enum ipset_opt opt, const char *str)
+{
+       unsigned int i = 0;
+       unsigned char ether[ETH_ALEN];
+       
+       assert(session);
+       assert(opt == IPSET_OPT_ETHER);
+       assert(str);
+
+       if (strlen(str) != ETH_ALEN * 3 - 1)
+               goto error;
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(str + i * 3, &end, 16);
+
+               if (end == str + i * 3 + 2
+                   && (*end == ':' || *end == '\0')
+                   && number >= 0 && number <= 255)
+                       ether[i] = number;
+               else
+                       goto error;
+       }
+       return ipset_session_data_set(session, opt, ether);
+
+error:
+       return syntax_err("cannot parse '%s' as ethernet address", str);
+}
+
+/*
+ * Parse TCP service names or port numbers
+ */
+static int
+parse_portname(struct ipset_session *session, const char *str, uint16_t *port)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(str, "tcp")) != NULL) {
+               *port = ntohs((uint16_t) service->s_port);
+               return 0;
+       }
+       
+       return syntax_err("cannot parse '%s' as a (TCP) port", str);
+}
+
+static int
+parse_portnum(struct ipset_session *session, const char *str, uint16_t *port)
+{
+       return string_to_number(session, str, 0, 65535, (unsigned int *)port);
+}
+
+/**
+ * ipset_parse_single_port - parse a single (TCP) port number or name
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a single (TCP) port number or name. The parsed port
+ * number is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_single_port(struct ipset_session *session,
+                       enum ipset_opt opt, const char *str)
+{
+       uint16_t port;
+       int err;
+
+       assert(session);
+       assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
+       assert(str);
+
+       if ((err = parse_portnum(session, str, &port)) == 0
+           || (err = parse_portname(session, str, &port)) == 0)
+               err = ipset_session_data_set(session, opt, &port);
+
+       if (!err)
+               /* No error, so reset session messages! */
+               ipset_session_report_reset(session);
+
+       return err;
+}
+
+/**
+ * ipset_parse_port - parse (TCP) port name, number, or range of them
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a TCP port name or number or range of them.
+ * separated by a dash. The parsed port numbers are stored
+ * in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_port(struct ipset_session *session,
+                enum ipset_opt opt, const char *str)
+{
+       char *a, *saved, *tmp;
+       int err = 0;
+
+       assert(session);
+       assert(opt == IPSET_OPT_PORT);
+       assert(str);
+
+       saved = tmp = strdup(str);
+       if (tmp == NULL)
+               return ipset_err(session,
+                                "Cannot allocate memory to duplicate %s.",
+                                str);
+
+       a = ipset_range_separator(tmp);
+       if (a != NULL) {
+               /* port-port */
+               *a++ = '\0';
+               err = ipset_parse_single_port(session, IPSET_OPT_PORT_TO, a);
+               if (err)
+                       goto error;
+       }
+       err = ipset_parse_single_port(session, opt, tmp);
+
+error:
+       free(saved);
+       return err;
+}
+
+/**
+ * ipset_parse_family - parse INET|INET6 family names
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an INET|INET6 family name.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_family(struct ipset_session *session, int opt, const char *str)
+{
+       uint8_t family;
+       
+       assert(session);
+       assert(opt == IPSET_OPT_FAMILY);
+       assert(str);
+
+       if (STREQ(str, "inet") || STREQ(str, "ipv4") || STREQ(str, "-4"))
+               family = AF_INET;
+       else if (STREQ(str, "inet6") || STREQ(str, "ipv6") || STREQ(str, "-6"))
+               family = AF_INET6;
+       else if (STREQ(str, "any") || STREQ(str, "unspec"))
+               family = AF_UNSPEC;
+       else
+               return syntax_err("unknown INET family %s", str);
+                               
+       return ipset_session_data_set(session, opt, &family);
+}
+
+/*
+ * Parse IPv4/IPv6 addresses, networks and ranges
+ * We resolve hostnames but just the first IP address is used.
+ */
+static struct addrinfo *
+get_addrinfo(struct ipset_session *session, const char *str, uint8_t family)
+{
+       struct addrinfo hints;
+        struct addrinfo *res;
+       int err;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_flags = AI_CANONNAME;
+        hints.ai_family = family;
+        hints.ai_socktype = SOCK_RAW;
+        hints.ai_protocol = 0;
+        hints.ai_next = NULL;
+
+        if ((err = getaddrinfo(str, NULL, &hints, &res)) != 0) {
+               syntax_err("cannot resolve '%s' to an %s address: %s",
+                          str, family == AF_INET6 ? "IPv6" : "IPv4",
+                          gai_strerror(err));
+               return NULL;
+       } else
+               return res;
+}
+
+#define GET_ADDRINFO(family, IP, f, n)                                 \
+static int                                                             \
+get_addrinfo##f(struct ipset_session *session,                         \
+               const char *str,                                        \
+               struct addrinfo **info,                                 \
+               struct in##n##_addr **inaddr)                           \
+{                                                                      \
+        struct addrinfo *i;                                            \
+       struct sockaddr_in##n *saddr;                                   \
+        int found;                                                     \
+                                                                       \
+       if ((*info = get_addrinfo(session, str, family)) == NULL) {     \
+               syntax_err("cannot parse %s: resolving "                \
+                          IP " failed", str);                          \
+               return EINVAL;                                          \
+       }                                                               \
+                                                                       \
+       for (i = *info, found = 0; i != NULL; i = i->ai_next) {         \
+               if (i->ai_family != family)                             \
+                       continue;                                       \
+               if (found == 0) {                                       \
+                       saddr = (struct sockaddr_in##n *)i->ai_addr;    \
+                       *inaddr = &saddr->sin##n##_addr;                \
+               } else if (found == 1) {                                        \
+                       ipset_warn(session,                             \
+                                  "%s resolves to multiple addresses: "  \
+                                  "using only the first one returned by the resolver", \
+                                  str);                        \
+               }                                                       \
+               found++;                                                \
+       }                                                               \
+       if (found == 0)                                                 \
+               return syntax_err("cannot parse %s: "                   \
+                                 IP "address could not be resolved",   \
+                                 str);                                 \
+       return 0;                                                       \
+}
+
+#define PARSE_IP(mask, f, n)                                           \
+static int                                                             \
+parse_ipv##f(struct ipset_session *session,                            \
+            enum ipset_opt opt, const char *str)                       \
+{                                                                      \
+        unsigned int m = mask;                                         \
+        int aerr = EINVAL, err = 0, range = 0;                         \
+        char *saved = strdup(str);                                     \
+        char *a, *tmp = saved;                                         \
+        struct addrinfo *info;                                         \
+        struct in##n##_addr *inaddr;                                   \
+        struct ipset_data *data = ipset_session_data(session);         \
+        enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR     \
+                               : IPSET_OPT_CIDR2;                      \
+                                                                       \
+       if (tmp == NULL)                                                \
+               return ipset_err(session,                               \
+                                "Cannot allocate memory to duplicate %s.",\
+                                str);                                  \
+       if ((a = ipset_cidr_separator(tmp)) != NULL) {                  \
+               /* IP/mask */                                           \
+               *a++ = '\0';                                            \
+                                                                       \
+               if ((err = string_to_number(session, a, 0, m, &m)) != 0 \
+                   || (err = ipset_data_set(data, copt, &m)) != 0)     \
+                       goto out;                                       \
+       } else if ((a = ipset_range_separator(tmp)) != NULL) {          \
+               /* IP-IP */                                             \
+               *a++ = '\0';                                            \
+               D("range %s", a);                                       \
+               range++;                                                \
+       }                                                               \
+       if ((aerr = get_addrinfo##f(session, tmp, &info, &inaddr)) != 0 \
+           || (err = ipset_data_set(data, opt, inaddr)) != 0           \
+           || !range)                                                  \
+               goto out;                                               \
+       freeaddrinfo(info);                                             \
+       if ((aerr = get_addrinfo##f(session, a, &info, &inaddr)) == 0)  \
+               err = ipset_data_set(data, IPSET_OPT_IP_TO, inaddr);    \
+                                                                       \
+out:                                                                   \
+       if (aerr != EINVAL)                                             \
+               /* getaddrinfo not failed */                            \
+               freeaddrinfo(info);                                     \
+       else if (aerr)                                                  \
+               err = -1;                                               \
+       free(saved);                                                    \
+       return err;                                                     \
+} 
+
+GET_ADDRINFO(AF_INET, "IPv4", 4, )
+PARSE_IP(32, 4, )
+
+GET_ADDRINFO(AF_INET6, "IPv6", 6, 6)
+PARSE_IP(128, 6, 6)
+
+enum ipaddr_type {
+       IPADDR_ANY,
+       IPADDR_PLAIN,
+       IPADDR_NET,
+       IPADDR_RANGE,
+};
+
+static int
+parse_ip(struct ipset_session *session,
+        enum ipset_opt opt, const char *str, enum ipaddr_type addrtype)
+{
+       int err = 0;
+       struct ipset_data *data = ipset_session_data(session);
+       uint8_t family = ipset_data_family(data);
+
+       if (family == AF_UNSPEC) {
+               family = AF_INET;
+               ipset_data_set(data, IPSET_OPT_FAMILY, &family);
+       }
+
+       switch (addrtype) {
+       case IPADDR_PLAIN:
+               if (ipset_range_separator(str) || ipset_cidr_separator(str))
+                       return syntax_err("plain IP address must be supplied: %s",
+                                         str);
+               break;
+       case IPADDR_NET:
+               if (!ipset_cidr_separator(str) || ipset_range_separator(str))
+                       return syntax_err("IP/netblock must be supplied: %s",
+                                         str);
+               break;
+       case IPADDR_RANGE:
+               if (!ipset_range_separator(str) || ipset_cidr_separator(str))
+                       return syntax_err("IP-IP range must supplied: %s",
+                                         str);
+               break;
+       case IPADDR_ANY:
+       default:
+               break;
+       }
+
+       if (family == AF_INET)
+               err = parse_ipv4(session, opt, str);
+       else
+               err = parse_ipv6(session, opt, str);
+
+       return err;
+}
+
+/**
+ * ipset_parse_ip - parse IPv4|IPv6 address, range or netblock
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address or address range
+ * or netblock. Hostnames are resolved. If family is not set
+ * yet in the data blob, INET is assumed.
+ * The values are stored in the data blob of the session.
+ *
+ * FIXME: if the hostname resolves to multiple addresses,
+ * the first one is used only.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_ip(struct ipset_session *session,
+              enum ipset_opt opt, const char *str)
+{
+       assert(session);
+       assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+       assert(str);
+
+       return parse_ip(session, opt, str, IPADDR_ANY);
+}
+
+/**
+ * ipset_parse_single_ip - parse a single IPv4|IPv6 address
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address or hostname. If family 
+ * is not set yet in the data blob, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_single_ip(struct ipset_session *session,
+                     enum ipset_opt opt, const char *str)
+{
+       assert(session);
+       assert(opt == IPSET_OPT_IP
+              || opt == IPSET_OPT_IP_TO
+              || opt == IPSET_OPT_IP2);
+       assert(str);
+
+       return parse_ip(session, opt, str, IPADDR_PLAIN);
+}
+
+/**
+ * ipset_parse_net - parse IPv4|IPv6 address/cidr
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address/cidr pattern. If family 
+ * is not set yet in the data blob, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_net(struct ipset_session *session,
+               enum ipset_opt opt, const char *str)
+{
+       assert(session);
+       assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+       assert(str);
+
+       return parse_ip(session, opt, str, IPADDR_NET);
+}
+
+/**
+ * ipset_parse_range - parse IPv4|IPv6 ranges
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 range separated by a dash. If family
+ * is not set yet in the data blob, INET is assumed.
+ * The values are stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_range(struct ipset_session *session,
+                 enum ipset_opt opt, const char *str)
+{
+       assert(session);
+       assert(opt == IPSET_OPT_IP);
+       assert(str);
+
+       return parse_ip(session, IPSET_OPT_IP, str, IPADDR_RANGE);
+}
+
+/**
+ * ipset_parse_netrange - parse IPv4|IPv6 address/cidr or range
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address/cidr pattern or a range
+ * of addresses separated by a dash. If family is not set yet in
+ * the data blob, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_netrange(struct ipset_session *session,
+                    enum ipset_opt opt, const char *str)
+{
+       assert(session);
+       assert(opt == IPSET_OPT_IP);
+       assert(str);
+
+       if (!(ipset_range_separator(str) || ipset_cidr_separator(str)))
+               return syntax_err("IP/net or IP-IP range must be specified: %s",
+                                 str);
+       return parse_ip(session, opt, str, IPADDR_ANY);
+}
+
+#define check_setname(str, saved)                                      \
+do {                                                                   \
+    if (strlen(str) > IPSET_MAXNAMELEN - 1) {                          \
+       if (saved != NULL)                                              \
+               free(saved);                                            \
+       return syntax_err("setname '%s' is longer than %u characters",  \
+                         str, IPSET_MAXNAMELEN - 1);                   \
+    }                                                                  \
+} while (0)
+
+
+/**
+ * ipset_parse_name - parse setname as element
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a setname or a setname element to add to a set.
+ * The pattern "setname,before|after,setname" is recognized and
+ * parsed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_name(struct ipset_session *session,
+                enum ipset_opt opt, const char *str)
+{
+       char *saved;
+       char *a = NULL, *b = NULL, *tmp;
+       int err, before = 0;
+       const char *sep = IPSET_ELEM_SEPARATOR;
+       struct ipset_data *data;
+
+       assert(session);
+       assert(opt == IPSET_OPT_NAME || opt == IPSET_OPT_SETNAME2);
+       assert(str);
+
+       data = ipset_session_data(session);
+       if (opt == IPSET_OPT_SETNAME2) {
+               check_setname(str, NULL);
+               
+               return ipset_data_set(data, opt, str);
+       }
+
+       tmp = saved = strdup(str);      
+       if (saved == NULL)
+               return ipset_err(session,
+                                "Cannot allocate memory to duplicate %s.",
+                                str);
+       if ((a = ipset_elem_separator(tmp)) != NULL) {
+               /* setname,[before|after,setname */
+               *a++ = '\0';
+               if ((b = ipset_elem_separator(a)) != NULL)
+                       *b++ = '\0';
+               if (b == NULL
+                   || !(STREQ(a, "before") || STREQ(a, "after"))) {
+                       err = ipset_err(session, "you must specify elements "
+                                       "as setname%s[before|after]%ssetname",
+                                       sep, sep);
+                       goto out;
+               }
+               before = STREQ(a, "before");
+       }
+       check_setname(tmp, saved);
+       if ((err = ipset_data_set(data, opt, tmp)) != 0 || b == NULL)
+               goto out;
+
+       check_setname(b, saved);
+       if ((err = ipset_data_set(data,
+                                 IPSET_OPT_NAMEREF, b)) != 0)
+               goto out;
+       
+       if (before)
+               err = ipset_data_set(data, IPSET_OPT_BEFORE, &before);
+
+out:
+       free(saved);
+       return err;
+}
+
+/**
+ * ipset_parse_setname - parse name as the name of the (current) set
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as the name of the (current) set.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_setname(struct ipset_session *session,
+                   enum ipset_opt opt, const char *str)
+{
+       assert(session);
+       assert(opt == IPSET_SETNAME);
+       assert(str);
+
+       check_setname(str, NULL);
+
+       return ipset_session_data_set(session, opt, str);
+}
+
+/**
+ * ipset_parse_uint32 - parse string as an unsigned integer
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an unsigned integer number.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_uint32(struct ipset_session *session,
+                  enum ipset_opt opt, const char *str)
+{
+       uint32_t value;
+       int err;
+       
+       assert(session);
+       assert(str);
+
+       if ((err = string_to_number(session, str, 0, 0, &value)) == 0)
+               return ipset_session_data_set(session, opt, &value);
+       
+       return err;
+}
+
+/**
+ * ipset_parse_uint8 - parse string as an unsigned short integer
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an unsigned short integer number.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_uint8(struct ipset_session *session,
+                 enum ipset_opt opt, const char *str)
+{
+       unsigned int value;
+       int err;
+       
+       assert(session);
+       assert(str);
+
+       if ((err = string_to_number(session, str, 0, 255, &value)) == 0)
+               return ipset_session_data_set(session, opt, &value);
+
+       return err;
+}
+
+/**
+ * ipset_parse_netmask - parse string as a CIDR netmask value
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a CIDR netmask value, depending on family type.
+ * If family is not set yet, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_netmask(struct ipset_session *session,
+                   enum ipset_opt opt, const char *str)
+{
+       unsigned int family, cidr;
+       struct ipset_data *data;
+       int err = 0;
+       
+       assert(session);
+       assert(opt == IPSET_OPT_NETMASK);
+       assert(str);
+
+       data = ipset_session_data(session);
+       family = ipset_data_family(data);
+       if (family == AF_UNSPEC) {
+               family = AF_INET;
+               ipset_data_set(data, IPSET_OPT_FAMILY, &family);
+       }
+
+       err = string_to_number(session, str,
+                              family == AF_INET ? 1 : 4, 
+                              family == AF_INET ? 31 : 124,
+                              &cidr);
+
+       if (err)
+               return syntax_err("netmask is out of the inclusive range "
+                                 "of %u-%u",
+                                 family == AF_INET ? 1 : 4,
+                                 family == AF_INET ? 31 : 124);
+
+       return ipset_data_set(data, opt, &cidr);
+}
+
+/**
+ * ipset_parse_flag - "parse" option flags
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse option flags :-)
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_flag(struct ipset_session *session,
+                enum ipset_opt opt, const char *str UNUSED)
+{
+       assert(session);
+       
+       return ipset_session_data_set(session, opt, NULL);
+}
+
+/**
+ * ipset_parse_type - parse ipset type name
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse ipset module type: supports both old and new formats.
+ * The type name is looked up and the type found is stored
+ * in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_typename(struct ipset_session *session,
+                    enum ipset_opt opt, const char *str)
+{
+       const struct ipset_type *type;
+       const char *typename;
+
+       assert(session);
+       assert(opt == IPSET_OPT_TYPENAME);
+       assert(str);
+
+       if (strlen(str) > IPSET_MAXNAMELEN - 1)
+               return syntax_err("typename '%s' is longer than %u characters",
+                                 str, IPSET_MAXNAMELEN - 1);
+
+       /* Find the corresponding type */
+       typename = ipset_typename_resolve(str);
+       if (typename == NULL)
+               return syntax_err("typename '%s' is unkown", str);
+       ipset_session_data_set(session, IPSET_OPT_TYPENAME, typename);
+       type = ipset_type_get(session, IPSET_CMD_CREATE);
+
+       if (type == NULL)
+               return -1;
+       
+       return ipset_session_data_set(session, IPSET_OPT_TYPE, type);
+}
+
+/**
+ * ipset_parse_output - parse output format name
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse output format names and set session mode.
+ * The value is stored in the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_output(struct ipset_session *session,
+                  int opt UNUSED, const char *str)
+{
+       assert(session);
+       assert(str);
+
+       if (STREQ(str, "plain"))
+               return ipset_session_output(session, IPSET_LIST_PLAIN);
+       else if (STREQ(str, "xml"))
+               return ipset_session_output(session, IPSET_LIST_XML);
+       else if (STREQ(str, "save"))
+               return ipset_session_output(session, IPSET_LIST_SAVE);
+
+       return syntax_err("unkown output mode '%s'", str);
+}
+
+#define parse_elem(s, t, d, str)                                       \
+do {                                                                   \
+       if (!t->elem[d].parse)                                          \
+               goto internal;                                          \
+       err = t->elem[d].parse(s, t->elem[d].opt, str);                 \
+       if (err)                                                        \
+               goto out;                                               \
+} while (0)
+
+/**
+ * ipset_parse_elem - parse ADT elem, depending on settype
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a (multipart) element according to the settype.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_elem(struct ipset_session *session,
+                enum ipset_opt optional, const char *str)
+{
+       const struct ipset_type *type;
+       char *a = NULL, *b = NULL, *tmp, *saved;
+       int err;
+
+       assert(session);
+       assert(str);
+
+       type = ipset_session_data_get(session, IPSET_OPT_TYPE);
+       if (!type)
+               return ipset_err(session,
+                                "Internal error: set type is unknown!");
+
+       saved = tmp = strdup(str);
+       if (tmp == NULL)
+               return ipset_err(session,
+                                "Cannot allocate memory to duplicate %s.",
+                                str);
+
+       a = ipset_elem_separator(tmp);
+       if (type->dimension > IPSET_DIM_ONE) {
+               if (a != NULL) {
+                       /* elem,elem */
+                       *a++ = '\0';
+               } else if (type->dimension > IPSET_DIM_TWO && !optional) {
+                       free(tmp);
+                       return syntax_err("Second element is missing from %s.",
+                                         str);
+               }
+       } else if (a != NULL)
+               return syntax_err("Elem separator in %s, "
+                                 "but settype %s supports none.",
+                                 str, type->name);
+
+       if (a)
+               b = ipset_elem_separator(a);
+       if (type->dimension > IPSET_DIM_TWO) {
+               if (b != NULL) {
+                       /* elem,elem,elem */
+                       *b++ = '\0';
+               } else if (!optional) {
+                       free(tmp);
+                       return syntax_err("Third element is missing from %s.",
+                                         str);
+               }
+       } else if (b != NULL)
+               return syntax_err("Two elem separators in %s, "
+                                 "but settype %s supports one.",
+                                 str, type->name);
+       if (b != NULL && ipset_elem_separator(b))
+               return syntax_err("Three elem separators in %s, "
+                                 "but settype %s supports two.",
+                                 str, type->name);
+
+       D("parse elem part one: %s", tmp);
+       parse_elem(session, type, IPSET_DIM_ONE, tmp);
+
+       if (type->dimension > IPSET_DIM_ONE && a != NULL) {
+               D("parse elem part two: %s", a);
+               parse_elem(session, type, IPSET_DIM_TWO, a);
+       }
+       if (type->dimension > IPSET_DIM_TWO && b != NULL)
+               parse_elem(session, type, IPSET_DIM_THREE, b);
+
+       goto out;
+
+internal:
+       err = ipset_err(session,
+                       "Internal error: missing parser function for %s",
+                       type->name);
+out:
+       free(saved);
+       return err;
+}
diff --git a/lib/print.c b/lib/print.c
new file mode 100644 (file)
index 0000000..4df0905
--- /dev/null
@@ -0,0 +1,577 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <errno.h>                             /* errno */
+#include <stdio.h>                             /* snprintf */
+#include <netdb.h>                             /* getservbyport */
+#include <sys/types.h>                         /* inet_ntop */
+#include <sys/socket.h>                                /* inet_ntop */
+#include <arpa/inet.h>                         /* inet_ntop */
+#include <net/ethernet.h>                      /* ETH_ALEN */
+
+#include <libipset/data.h>                     /* ipset_data_* */
+#include <libipset/parse.h>                    /* IPSET_*_SEPARATOR */
+#include <libipset/types.h>                    /* ipset set types */
+#include <libipset/session.h>                  /* IPSET_FLAG_ */
+#include <libipset/utils.h>                    /* UNUSED */
+#include <libipset/ui.h>                       /* IPSET_ENV_* */
+#include <libipset/print.h>                    /* prototypes */
+
+/* Print data (to output buffer). All function must follow snprintf. */
+
+#define SNPRINTF_FAILURE(size, len, offset)                    \
+do {                                                           \
+       if (size < 0 || (unsigned int) size >= len)             \
+               return size;                                    \
+       offset += size;                                         \
+       len -= size;                                            \
+} while (0)
+
+/**
+ * ipset_print_ether - print ethernet address to string
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print Ethernet address to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_ether(char *buf, unsigned int len,
+                 const struct ipset_data *data, enum ipset_opt opt,
+                 uint8_t env UNUSED)
+{
+       const unsigned char *ether;
+       int i, size, offset = 0;
+       
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       assert(opt == IPSET_OPT_ETHER);
+       
+       if (len < ETH_ALEN*3)
+               return -1;
+       
+       ether = ipset_data_get(data, opt);
+       assert(ether);
+
+       size = snprintf(buf, len, "%02X", ether[0]);
+       SNPRINTF_FAILURE(size, len, offset);
+       for (i = 1; i < ETH_ALEN; i++) {
+               size = snprintf(buf + offset, len, ":%02X", ether[i]);
+               SNPRINTF_FAILURE(size, len, offset);
+       }
+       
+       return offset;
+}
+
+/**
+ * ipset_print_family - print INET family
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print INET family string to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_family(char *buf, unsigned int len,
+                  const struct ipset_data *data, int opt,
+                  uint8_t env UNUSED)
+{
+       uint8_t family;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       assert(opt == IPSET_OPT_FAMILY);
+
+       if (len < strlen("inet6") + 1)
+               return -1;
+
+       family = ipset_data_family(data);
+
+       return snprintf(buf, len, "%s",
+                       family == AF_INET ? "inet" :
+                       family == AF_INET6 ? "inet6" : "any");
+}
+
+/**
+ * ipset_print_type - print ipset type string
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print ipset module string identifier to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_type(char *buf, unsigned int len,
+                const struct ipset_data *data, enum ipset_opt opt,
+                uint8_t env UNUSED)
+{
+       const struct ipset_type *type;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       assert(opt == IPSET_OPT_TYPE);
+
+       type = ipset_data_get(data, opt);
+       assert(type);
+       if (len < strlen(type->name) + 1)
+               return -1;
+               
+       return snprintf(buf, len, "%s", type->name);
+}
+
+#define GETNAMEINFO(family, f, n)                                      \
+static inline int                                                      \
+__getnameinfo##f(char *buf, unsigned int len,                          \
+                int flags, const union nf_inet_addr *addr)             \
+{                                                                      \
+       struct sockaddr_in##n saddr;                                    \
+       int err;                                                        \
+                                                                       \
+       memset(&saddr, 0, sizeof(saddr));                               \
+       in##f##cpy(&saddr.sin##n##_addr, &addr->in##n);                 \
+       saddr.sin##n##_family = family;                                 \
+                                                                       \
+       err = getnameinfo((const struct sockaddr *)&saddr,              \
+                         sizeof(saddr),                                \
+                         buf, len, NULL, 0, flags);                    \
+                                                                       \
+       if (err == EAI_AGAIN && !(flags & NI_NUMERICHOST))              \
+               err = getnameinfo((const struct sockaddr *)&saddr,      \
+                                 sizeof(saddr),                        \
+                                 buf, len, NULL, 0,                    \
+                                 flags | NI_NUMERICHOST);              \
+       return (err != 0 ? -1 : (int)strlen(buf));                      \
+}
+
+#define SNPRINTF_IP(mask, f)                                           \
+static int                                                             \
+snprintf_ipv##f(char *buf, unsigned int len, int flags,                        \
+              const union nf_inet_addr *ip, uint8_t cidr)              \
+{                                                                      \
+       int size, offset = 0;                                           \
+                                                                       \
+       size = __getnameinfo##f(buf, len, flags, ip);                   \
+       SNPRINTF_FAILURE(size, len, offset);                            \
+                                                                       \
+       if (cidr == mask)                                               \
+               return offset;                                          \
+       if ((unsigned int)(size + 5) < len)                             \
+               return -1;                                              \
+       size = snprintf(buf + offset, len,                              \
+                       "%s%u", IPSET_CIDR_SEPARATOR, cidr);            \
+       SNPRINTF_FAILURE(size, len, offset);                            \
+       return offset;                                                  \
+}
+
+GETNAMEINFO(AF_INET, 4, )
+SNPRINTF_IP(32, 4)
+
+GETNAMEINFO(AF_INET6, 6, 6)
+SNPRINTF_IP(128, 6)
+
+/**
+ * ipset_print_ip - print IPv4|IPv6 address to string
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print IPv4|IPv6 address, address/cidr or address range to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_ip(char *buf, unsigned int len,
+              const struct ipset_data *data, enum ipset_opt opt,
+              uint8_t env)
+{
+       const union nf_inet_addr *ip;
+       uint8_t family, cidr;
+       int flags, size, offset = 0;
+       enum ipset_opt cidropt;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+
+       D("len: %u", len);
+       family = ipset_data_family(data);
+       cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
+       if (ipset_data_test(data, cidropt))
+               cidr = *(uint8_t *) ipset_data_get(data, cidropt);
+       else
+               cidr = family == AF_INET6 ? 128 : 32;
+       flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
+       
+       ip = ipset_data_get(data, opt);
+       assert(ip);
+       if (family == AF_INET)
+               size = snprintf_ipv4(buf, len, flags, ip, cidr);
+       else if (family == AF_INET6)
+               size = snprintf_ipv6(buf, len, flags, ip, cidr);
+       else
+               return -1;
+       SNPRINTF_FAILURE(size, len, offset);
+
+       D("len: %u, offset %u", len, offset);
+       if (!ipset_data_test(data, IPSET_OPT_IP_TO))
+               return offset;
+
+       size = snprintf(buf + offset, len, "%s", IPSET_RANGE_SEPARATOR);
+       SNPRINTF_FAILURE(size, len, offset);
+
+       ip = ipset_data_get(data, IPSET_OPT_IP_TO);
+       if (family == AF_INET)
+               size = snprintf_ipv4(buf + offset, len, flags, ip, cidr);
+       else if (family == AF_INET6)
+               size = snprintf_ipv6(buf + offset, len, flags, ip, cidr);
+       else
+               return -1;
+       
+       SNPRINTF_FAILURE(size, len, offset);    
+       return offset;
+}
+
+/**
+ * ipset_print_ipaddr - print IPv4|IPv6 address to string
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print IPv4|IPv6 address or address/cidr to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_ipaddr(char *buf, unsigned int len,
+                  const struct ipset_data *data, enum ipset_opt opt,
+                  uint8_t env)
+{
+       const union nf_inet_addr *ip;
+       uint8_t family, cidr;
+       enum ipset_opt cidropt;
+       int flags;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       assert(opt == IPSET_OPT_IP
+              || opt == IPSET_OPT_IP_TO
+              || opt == IPSET_OPT_IP2);
+
+       family = ipset_data_family(data);
+       cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
+       if (ipset_data_test(data, cidropt))
+               cidr = *(uint8_t *) ipset_data_get(data, cidropt);
+       else
+               cidr = family == AF_INET6 ? 128 : 32;
+       flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
+
+       ip = ipset_data_get(data, opt);
+       assert(ip);
+       if (family == AF_INET)
+               return snprintf_ipv4(buf, len, flags, ip, cidr);
+       else if (family == AF_INET6)
+               return snprintf_ipv6(buf, len, flags, ip, cidr);
+
+       return -1;
+}
+
+/**
+ * ipset_print_number - print number to string
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print number to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_number(char *buf, unsigned int len,
+                  const struct ipset_data *data, enum ipset_opt opt,
+                  uint8_t env UNUSED)
+{
+       size_t maxsize;
+       const void *number;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       
+       number = ipset_data_get(data, opt);
+       maxsize = ipset_data_sizeof(opt, AF_INET);
+       D("opt: %u, maxsize %zu", opt, maxsize);
+       if (maxsize == sizeof(uint8_t))
+               return snprintf(buf, len, "%u", *(uint8_t *) number);
+       else if (maxsize == sizeof(uint16_t))
+               return snprintf(buf, len, "%u", *(uint16_t *) number);
+       else if (maxsize == sizeof(uint32_t))
+               return snprintf(buf, len, "%lu",
+                               (long unsigned) *(uint32_t *) number);
+       else
+               assert(0);
+       return 0;
+}
+
+/**
+ * ipset_print_name - print setname element string
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print setname element string to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_name(char *buf, unsigned int len,
+                const struct ipset_data *data, enum ipset_opt opt,
+                uint8_t env UNUSED)
+{
+       const char *name;
+       int size, offset = 0;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       assert(opt == IPSET_OPT_NAME);
+
+       if (len < 2*IPSET_MAXNAMELEN + 2 + strlen("before"))
+               return -1;
+
+       name = ipset_data_get(data, opt);
+       assert(name);
+       size = snprintf(buf, len, "%s", name);
+       SNPRINTF_FAILURE(size, len, offset);    
+
+       if (ipset_data_test(data, IPSET_OPT_NAMEREF)) {
+               bool before = ipset_data_test(data, IPSET_OPT_BEFORE);
+               size = snprintf(buf + offset, len,
+                               "%s%s%s%s", IPSET_ELEM_SEPARATOR,
+                               before ? "before" : "after",
+                               IPSET_ELEM_SEPARATOR,
+                               (const char *) ipset_data_get(data,
+                                       IPSET_OPT_NAMEREF));
+               SNPRINTF_FAILURE(size, len, offset);    
+       }
+
+       return offset;
+}
+
+/**
+ * ipset_print_port - print port or port range
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print port or port range to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_port(char *buf, unsigned int len,
+                const struct ipset_data *data, enum ipset_opt opt,
+                uint8_t env UNUSED)
+{
+       const uint16_t *port;
+       int size, offset = 0;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+       assert(opt == IPSET_OPT_PORT);
+
+       if (len < 2*strlen("65535") + 2)
+               return -1;
+
+       port = ipset_data_get(data, IPSET_OPT_PORT);
+       assert(port);
+       size = snprintf(buf, len, "%u", *port);
+       SNPRINTF_FAILURE(size, len, offset);    
+       
+       if (ipset_data_test(data, IPSET_OPT_PORT_TO)) {
+               port = ipset_data_get(data, IPSET_OPT_PORT_TO);
+               size = snprintf(buf + offset, len,
+                               "%s%u",
+                               IPSET_RANGE_SEPARATOR, *port);
+               SNPRINTF_FAILURE(size, len, offset);
+       }
+
+       return offset;
+}
+
+#define print_second(data)     \
+ipset_data_flags_test(data,    \
+       IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER)) 
+
+#define print_third(data)      \
+ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_IP2)) 
+
+/**
+ * ipset_print_elem - print ADT elem according to settype
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print (multipart) element according to settype
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_elem(char *buf, unsigned int len,
+                const struct ipset_data *data, enum ipset_opt opt UNUSED,
+                uint8_t env)
+{
+       const struct ipset_type *type;
+       int size, offset = 0;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+
+       type = ipset_data_get(data, IPSET_OPT_TYPE);
+       if (!type)
+               return -1;
+       
+       size = type->elem[IPSET_DIM_ONE].print(buf, len, data,
+                       type->elem[IPSET_DIM_ONE].opt, env);
+       SNPRINTF_FAILURE(size, len, offset);
+       if (ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt))
+               D("print second elem");
+       if (type->dimension == IPSET_DIM_ONE
+           || (type->last_elem_optional
+               && !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)))
+               return offset;
+       
+       size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
+       SNPRINTF_FAILURE(size, len, offset);
+       size = type->elem[IPSET_DIM_TWO].print(buf + offset, len, data,
+                       type->elem[IPSET_DIM_TWO].opt, env);
+       SNPRINTF_FAILURE(size, len, offset);
+       if (type->dimension == IPSET_DIM_TWO
+           || (type->last_elem_optional
+               && !ipset_data_test(data, type->elem[IPSET_DIM_THREE].opt)))
+               return offset;
+
+       size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
+       SNPRINTF_FAILURE(size, len, offset);
+       size = type->elem[IPSET_DIM_THREE].print(buf + offset, len, data,
+                       type->elem[IPSET_DIM_THREE].opt, env);
+       SNPRINTF_FAILURE(size, len, offset);
+
+       return offset;
+}
+
+/**
+ * ipset_print_flag - print a flag
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print a flag, i.e. option without value
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_flag(char *buf UNUSED, unsigned int len UNUSED,
+                const struct ipset_data *data UNUSED,
+                enum ipset_opt opt UNUSED, uint8_t env UNUSED)
+{      
+       return 0;
+}
+
+/**
+ * ipset_print_data - print data, generic fuction
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Generic wrapper of the printing functions.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_data(char *buf, unsigned int len,
+                const struct ipset_data *data, enum ipset_opt opt,
+                uint8_t env)
+{
+       int size = 0, offset = 0;
+
+       assert(buf);
+       assert(len > 0);
+       assert(data);
+
+       switch (opt) {
+       case IPSET_OPT_FAMILY:
+               size = ipset_print_family(buf, len, data, opt, env);
+               break;
+       case IPSET_OPT_TYPE:
+               size = ipset_print_type(buf, len, data, opt, env);
+               break;
+       case IPSET_SETNAME:
+               size = snprintf(buf, len, "%s", ipset_data_setname(data));
+               break;
+       case IPSET_OPT_ELEM:
+               size = ipset_print_elem(buf, len, data, opt, env);
+               break;
+       case IPSET_OPT_IP:
+               size = ipset_print_ip(buf, len, data, opt, env);
+               break;
+       case IPSET_OPT_PORT:
+               size = ipset_print_port(buf, len, data, opt, env);
+               break;
+       case IPSET_OPT_GC:
+       case IPSET_OPT_HASHSIZE:
+       case IPSET_OPT_MAXELEM:
+       case IPSET_OPT_NETMASK:
+       case IPSET_OPT_PROBES:
+       case IPSET_OPT_RESIZE:
+       case IPSET_OPT_TIMEOUT:
+       case IPSET_OPT_REFERENCES:
+       case IPSET_OPT_ELEMENTS:
+       case IPSET_OPT_SIZE:
+               size = ipset_print_number(buf, len, data, opt, env);
+               break;
+       default:
+               return -1;
+       }
+       SNPRINTF_FAILURE(size, len, offset);
+       
+       return offset;
+}
diff --git a/lib/session.c b/lib/session.c
new file mode 100644 (file)
index 0000000..2c4e39a
--- /dev/null
@@ -0,0 +1,1782 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <errno.h>                             /* errno */
+#include <stdio.h>                             /* snprintf */
+#include <stdarg.h>                            /* va_* */
+#include <stdlib.h>                            /* free */
+#include <string.h>                            /* str* */
+#include <unistd.h>                            /* getpagesize */
+#include <net/ethernet.h>                      /* ETH_ALEN */
+
+#include <libipset/data.h>                     /* IPSET_OPT_* */
+#include <libipset/errcode.h>                  /* ipset_errcode */
+#include <libipset/print.h>                    /* ipset_print_* */
+#include <libipset/types.h>                    /* struct ipset_type */
+#include <libipset/transport.h>                        /* transport */
+#include <libipset/mnl.h>                      /* default backend */
+#include <libipset/utils.h>                    /* STREQ */
+#include <libipset/ui.h>                       /* IPSET_ENV_* */
+#include <libipset/session.h>                  /* prototypes */
+
+#define IPSET_NEST_MAX 4
+
+/* The session structure */
+struct ipset_session {
+       const struct ipset_transport *transport;/* Transport protocol */
+       struct ipset_handle *handle;            /* Transport handler */
+       struct ipset_data *data;                /* Input/output data */
+       /* Command state */
+       enum ipset_cmd cmd;                     /* Current command */
+       uint32_t lineno;                        /* Current lineno in restore mode */
+       char saved_setname[IPSET_MAXNAMELEN];   /* Saved setname */
+       struct nlattr *nested[IPSET_NEST_MAX];  /* Pointer to nest levels */
+       uint8_t nestid;                         /* Current nest level */
+       bool version_checked;                   /* Version checked */
+       /* Output buffer */
+       char outbuf[IPSET_OUTBUFLEN];           /* Output buffer */
+       enum ipset_output_mode mode;            /* Output mode */
+       ipset_outfn outfn;                      /* Output function */
+       /* Error/warning reporting */
+       char report[IPSET_ERRORBUFLEN];         /* Error/report buffer */
+       char *errmsg;
+       char *warnmsg;
+       uint8_t envopts;                        /* Session env opts */
+       /* Kernel message buffer */
+       size_t bufsize;
+       char buffer[0];
+};
+
+/*
+ * Glue functions
+ */
+
+/**
+ * ipset_session_data - return pointer to the data
+ * @session: session structure
+ *
+ * Returns the pointer to the data structure of the session.
+ */
+struct ipset_data *
+ipset_session_data(const struct ipset_session *session)
+{
+       assert(session);
+       return session->data;
+}
+
+/**
+ * ipset_session_handle - return pointer to the handle
+ * @session: session structure
+ *
+ * Returns the pointer to the transport handle structure of the session.
+ */
+struct ipset_handle *
+ipset_session_handle(const struct ipset_session *session)
+{
+       assert(session);
+       return session->handle;
+}
+
+/*
+ * Environment options
+ */
+
+/**
+ * ipset_envopt_parse - parse/set environment option
+ * @session: session structure
+ * @opt: environment option
+ * @arg: option argument (unused)
+ *
+ * Parse and set an environment option.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_envopt_parse(struct ipset_session *session, int opt,
+                  const char *arg UNUSED)
+{
+       assert(session);
+
+       switch (opt) {
+       case IPSET_ENV_SORTED:
+       case IPSET_ENV_QUIET:
+       case IPSET_ENV_RESOLVE:
+       case IPSET_ENV_EXIST:
+               session->envopts |= opt;
+               return 0;
+       default:
+               break;
+       }
+       return -1;
+}
+
+/**
+ * ipset_envopt_test - test environment option
+ * @session: session structure
+ * @opt: environment option
+ *
+ * Test whether the environment option is set in the session.
+ *
+ * Returns true or false.
+ */
+bool
+ipset_envopt_test(struct ipset_session *session, enum ipset_envopt opt)
+{
+       assert(session);
+       return session->envopts & opt;
+}
+
+/**
+ * ipset_session_output - set the session output mode
+ * @session: session structure
+ * @mode: output mode
+ *
+ * Set the output mode for the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_session_output(struct ipset_session *session,
+                    enum ipset_output_mode mode)
+{
+       assert(session);
+       session->mode = mode;
+       return 0;
+}
+
+/*
+ * Error and warning reporting
+ */
+
+/**
+ * ipset_session_report - fill the report buffer
+ * @session: session structure
+ * @type: report type
+ * @fmt: message format
+ *
+ * Fill the report buffer with an error or warning message.
+ * Depending on the report type, set the error or warning
+ * message pointer.
+ *
+ * Returns -1.
+ */
+int __attribute__((format(printf,3,4)))
+ipset_session_report(struct ipset_session *session,
+                    enum ipset_err_type type, 
+                    const char *fmt, ...)
+{
+       int len, offset = 0;
+       va_list args;
+       
+       assert(session);
+       assert(fmt);
+
+       if (session->lineno != 0 && type == IPSET_ERROR) {
+               sprintf(session->report, "Error in line %u: ",
+                       session->lineno);
+       }
+       offset = strlen(session->report);
+       
+       va_start(args, fmt);
+       len = vsnprintf(session->report + offset,
+                       IPSET_ERRORBUFLEN - 1 - offset,
+                       fmt, args);
+       va_end(args);
+       
+       if (len >= IPSET_ERRORBUFLEN - 1 - offset)
+               session->report[IPSET_ERRORBUFLEN - 1] = '\0';
+
+       if (type == IPSET_ERROR) {
+               session->errmsg = session->report;
+               session->warnmsg = NULL;
+       } else {
+               session->errmsg = NULL;
+               session->warnmsg = session->report;
+       }
+       return -1;
+}
+
+/**
+ * ipset_session_reset - reset the report buffer
+ * @session: session structure
+ *
+ * Reset the report buffer, the error and warning pointers.
+ */
+void
+ipset_session_report_reset(struct ipset_session *session)
+{
+       assert(session);
+       session->report[0] = '\0';
+       session->errmsg = session->warnmsg = NULL;
+}
+
+/**
+ * ipset_session_error - return the report buffer as error
+ * @session: session structure
+ *
+ * Return the pointer to the report buffer as an error report.
+ * If there is no error message in the buffer, NULL returned.
+ */
+const char *
+ipset_session_error(const struct ipset_session *session)
+{
+       assert(session);
+
+       return session->errmsg;
+}
+
+/**
+ * ipset_session_warning - return the report buffer as warning
+ * @session: session structure
+ *
+ * Return the pointer to the report buffer as a warning report.
+ * If there is no warning message in the buffer, NULL returned.
+ */
+const char *
+ipset_session_warning(const struct ipset_session *session)
+{
+       assert(session);
+
+       return session->warnmsg;
+}
+
+/*
+ * Receive data from the kernel
+ */
+
+struct ipset_attr_policy {
+       uint16_t type;
+       uint16_t len;
+       enum ipset_opt opt;
+};
+
+/* Attribute policies and mapping to options */
+const struct ipset_attr_policy cmd_attrs[] = {
+       [IPSET_ATTR_PROTOCOL] = {
+               .type = MNL_TYPE_U8,
+       },
+       [IPSET_ATTR_SETNAME] = {
+               .type = MNL_TYPE_NUL_STRING,
+               .opt  = IPSET_SETNAME,
+               .len  = IPSET_MAXNAMELEN,
+       },
+       [IPSET_ATTR_TYPENAME] = {
+               .type = MNL_TYPE_NUL_STRING,
+               .opt = IPSET_OPT_TYPENAME,
+               .len  = IPSET_MAXNAMELEN,
+       },
+       /* IPSET_ATTR_SETNAME2 is an alias for IPSET_ATTR_TYPENAME */
+       [IPSET_ATTR_REVISION] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_REVISION,
+       },
+       [IPSET_ATTR_FAMILY] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_FAMILY,
+       },
+       [IPSET_ATTR_DATA] = {
+               .type = MNL_TYPE_NESTED,
+       },
+       [IPSET_ATTR_ADT] = {
+               .type = MNL_TYPE_NESTED,
+       },
+       [IPSET_ATTR_REVISION_MIN] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_REVISION_MIN,
+       },
+       /* IPSET_ATTR_PROTOCOL_MIN is an alias for IPSET_ATTR_REVISION_MIN */
+       [IPSET_ATTR_LINENO] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_LINENO,
+       },
+};
+
+const struct ipset_attr_policy create_attrs[] = {
+       [IPSET_ATTR_IP] = {
+               .type = MNL_TYPE_BINARY,
+               .opt = IPSET_OPT_IP,
+       },
+       [IPSET_ATTR_IP_TO] = {
+               .type = MNL_TYPE_BINARY,
+               .opt = IPSET_OPT_IP_TO,
+       },
+       [IPSET_ATTR_CIDR] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_CIDR,
+       },
+       [IPSET_ATTR_PORT] = {
+               .type = MNL_TYPE_U16,
+               .opt = IPSET_OPT_PORT,
+       },
+       [IPSET_ATTR_PORT_TO] = {
+               .type = MNL_TYPE_U16,
+               .opt = IPSET_OPT_PORT_TO,
+       },
+       [IPSET_ATTR_TIMEOUT] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_TIMEOUT,
+       },
+       [IPSET_ATTR_FLAGS] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_FLAGS,
+       },
+       [IPSET_ATTR_GC] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_GC,
+       },
+       [IPSET_ATTR_HASHSIZE] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_HASHSIZE,
+       },
+       [IPSET_ATTR_MAXELEM] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_MAXELEM,
+       },
+       [IPSET_ATTR_NETMASK] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_NETMASK,
+       },
+       [IPSET_ATTR_PROBES] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_PROBES,
+       },
+       [IPSET_ATTR_RESIZE] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_RESIZE,
+       },
+       [IPSET_ATTR_SIZE] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_SIZE,
+       },
+       [IPSET_ATTR_ELEMENTS] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_ELEMENTS,
+       },
+       [IPSET_ATTR_REFERENCES] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_REFERENCES,
+       },
+       [IPSET_ATTR_MEMSIZE] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_MEMSIZE,
+       },
+};
+
+const struct ipset_attr_policy adt_attrs[] = {
+       [IPSET_ATTR_IP] = {
+               .type = MNL_TYPE_BINARY,
+               .opt = IPSET_OPT_IP,
+       },
+       [IPSET_ATTR_IP_TO] = {
+               .type = MNL_TYPE_BINARY,
+               .opt = IPSET_OPT_IP_TO,
+       },
+       [IPSET_ATTR_CIDR] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_CIDR,
+       },
+       [IPSET_ATTR_PORT] = {
+               .type = MNL_TYPE_U16,
+               .opt = IPSET_OPT_PORT,
+       },
+       [IPSET_ATTR_PORT_TO] = {
+               .type = MNL_TYPE_U16,
+               .opt = IPSET_OPT_PORT_TO,
+       },
+       [IPSET_ATTR_TIMEOUT] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_TIMEOUT,
+       },
+       [IPSET_ATTR_FLAGS] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_FLAGS,
+       },
+       [IPSET_ATTR_LINENO] = {
+               .type = MNL_TYPE_U32,
+               .opt = IPSET_OPT_LINENO,
+       },
+       [IPSET_ATTR_ETHER] = {
+               .type = MNL_TYPE_BINARY,
+               .opt = IPSET_OPT_ETHER,
+               .len  = ETH_ALEN,
+       },
+       [IPSET_ATTR_NAME] = {
+               .type = MNL_TYPE_NUL_STRING,
+               .opt = IPSET_OPT_NAME,
+               .len  = IPSET_MAXNAMELEN,
+       },
+       [IPSET_ATTR_NAMEREF] = {
+               .type = MNL_TYPE_NUL_STRING,
+               .opt = IPSET_OPT_NAMEREF,
+               .len  = IPSET_MAXNAMELEN,
+       },
+       [IPSET_ATTR_IP2] = {
+               .type = MNL_TYPE_BINARY,
+               .opt = IPSET_OPT_IP2,
+       },
+       [IPSET_ATTR_CIDR2] = {
+               .type = MNL_TYPE_U8,
+               .opt = IPSET_OPT_CIDR2,
+       },
+};
+
+#define FAILURE(format, args...) \
+       { ipset_err(session, format  , ## args); return MNL_CB_ERROR; }
+
+static int
+attr2data(struct ipset_session *session, struct nlattr *nla[],
+         int type, const struct ipset_attr_policy attrs[])
+{
+       struct ipset_data *data = session->data;
+       const struct ipset_attr_policy *attr;
+       const void *d;
+
+       attr = &attrs[type];
+       d = mnl_attr_get_payload(nla[type]);
+
+       if (attr->type == MNL_TYPE_BINARY && !attr->len) {
+               uint8_t family = ipset_data_family(data);
+
+               /* Validate by hand */
+               switch (family) {
+               case AF_INET:
+                       if (nla[type]->nla_len < sizeof(uint32_t))
+                               FAILURE("Broken kernel message: "
+                                       "cannot validate IPv4 address attribute!");
+                       break;
+               case AF_INET6:
+                       if (nla[type]->nla_len < sizeof(struct in6_addr))
+                               FAILURE("Broken kernel message: "
+                                       "cannot validate IPv6 address attribute!");
+                       break;
+               default:
+                       FAILURE("Broken kernel message: "
+                               "IP address attribute but "
+                               "family is unspecified!");
+               }
+       } else if (nla[type]->nla_type & NLA_F_NET_BYTEORDER) {
+               switch (attr->type) {
+               case MNL_TYPE_U32: {
+                       uint32_t value;
+               
+                       value  = ntohl(*(uint32_t *)d);
+
+                       d = &value;
+                       break;
+               }
+               case MNL_TYPE_U16: {
+                       uint16_t value;
+               
+                       value = ntohs(*(uint16_t *)d);
+
+                       d = &value;
+                       break;
+               }
+               default:
+                       break;
+               }
+       }
+       return ipset_data_set(data, attr->opt, d);
+}
+
+#define ATTR2DATA(session, nla, type, attrs)           \
+       if (attr2data(session, nla, type, attrs) < 0)   \
+               return MNL_CB_ERROR
+
+static const char cmd2name[][9] = {
+       [IPSET_CMD_NONE]        = "NONE",
+       [IPSET_CMD_CREATE]      = "CREATE",
+       [IPSET_CMD_DESTROY]     = "DESTROY",
+       [IPSET_CMD_FLUSH]       = "FLUSH",
+       [IPSET_CMD_RENAME]      = "RENAME",
+       [IPSET_CMD_SWAP]        = "SWAP",
+       [IPSET_CMD_LIST]        = "LIST",
+       [IPSET_CMD_SAVE]        = "SAVE",
+       [IPSET_CMD_ADD]         = "ADD",
+       [IPSET_CMD_DEL]         = "DEL",
+       [IPSET_CMD_TEST]        = "TEST",
+       [IPSET_CMD_HEADER]      = "HEADER",
+       [IPSET_CMD_TYPE]        = "TYPE",
+       [IPSET_CMD_PROTOCOL]    = "PROTOCOL",
+};
+
+static inline int
+call_outfn(struct ipset_session *session)
+{
+       int ret = session->outfn("%s", session->outbuf);
+       
+       session->outbuf[0] = '\0';
+       
+       return ret < 0 ? ret : 0;
+}
+
+static int __attribute__((format(printf,2,3)))
+safe_snprintf(struct ipset_session *session, const char *fmt, ...)
+{
+       va_list args;
+       int len, ret, loop = 0;
+
+retry:
+       len = IPSET_OUTBUFLEN - strlen(session->outbuf);
+       va_start(args, fmt);
+       ret = vsnprintf(session->outbuf + IPSET_OUTBUFLEN - len, len,
+                       fmt, args);
+       va_end(args);
+       
+       if (ret < 0)
+               return ipset_err(session,
+                                "Internal error at printing to output buffer");
+
+       if (ret >= len) {
+               /* Buffer was too small, push it out and retry */
+               if (loop++)
+                       return ipset_err(session,
+                               "Internal error at printing, loop detected!");
+
+               session->outbuf[IPSET_OUTBUFLEN - len] = '\0';
+               if (!call_outfn(session))
+                       goto retry;
+       }
+       return ret;
+}
+
+static int
+safe_dprintf(struct ipset_session *session, ipset_printfn fn,
+            enum ipset_opt opt)
+{
+       int len, ret, loop = 0;
+
+retry:
+       len = IPSET_OUTBUFLEN - strlen(session->outbuf);
+       ret = fn(session->outbuf + IPSET_OUTBUFLEN - len, len,
+                session->data, opt, session->envopts);
+       
+       if (ret < 0)
+               return ipset_err(session,
+                       "Internal error at printing to output buffer");
+
+       if (ret >= len) {
+               /* Buffer was too small, push it out and retry */
+               if (loop++)
+                       return ipset_err(session,
+                       "Internal error at printing, loop detected!");
+
+               session->outbuf[IPSET_OUTBUFLEN - len] = '\0';
+               if (!call_outfn(session))
+                       goto retry;
+       }
+       return ret;
+}
+static int
+list_adt(struct ipset_session *session, struct nlattr *nla[])
+{
+       struct ipset_data *data = session->data;
+       const struct ipset_type *type;
+       const struct ipset_arg *arg;
+       uint8_t family;
+       int i;
+       
+       /* Check and load type, family */
+       if (!ipset_data_test(data, IPSET_OPT_TYPE))
+               type = ipset_type_get(session, IPSET_CMD_ADD);
+       else
+               type = ipset_data_get(data, IPSET_OPT_TYPE);
+
+       if (type == NULL)
+               return MNL_CB_ERROR;
+       family = ipset_data_family(data);
+
+       switch (session->mode) {
+       case IPSET_LIST_SAVE:
+               safe_snprintf(session, "add %s ", ipset_data_setname(data));
+               break;
+       case IPSET_LIST_XML:
+               safe_snprintf(session, "    <member>");
+               break;
+       case IPSET_LIST_PLAIN:
+       default:
+               break;
+       }
+       
+       for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
+               if (nla[i])
+                       ATTR2DATA(session, nla, i, adt_attrs);
+
+       safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM);
+
+       for (arg = type->args[IPSET_ADD]; arg != NULL && arg->print; arg++) {
+               if (!ipset_data_test(data, arg->opt))
+                       continue;
+               switch (session->mode) {
+               case IPSET_LIST_SAVE:
+               case IPSET_LIST_PLAIN:
+                       safe_snprintf(session, " %s ", arg->name[0]);
+                       if (arg->has_arg == IPSET_NO_ARG)
+                               break;
+                       safe_dprintf(session, arg->print, arg->opt);
+                       break;
+               case IPSET_LIST_XML:
+                       if (arg->has_arg == IPSET_NO_ARG) {
+                               safe_snprintf(session,
+                                             "    <%s/>\n",
+                                             arg->name[0]);
+                               break;
+                       }
+                       safe_snprintf(session, "    <%s>",
+                                     arg->name[0]);
+                       safe_dprintf(session, arg->print, arg->opt);
+                       safe_snprintf(session, "</%s>\n",
+                                     arg->name[0]);
+                       break;
+               default:
+                       break;
+               }
+       }
+       
+       if (session->mode == IPSET_LIST_XML)
+               safe_snprintf(session, "</member>\n");
+       else
+               safe_snprintf(session, "\n");
+
+       return MNL_CB_OK;
+}
+
+static int
+list_create(struct ipset_session *session, struct nlattr *nla[])
+{
+       struct ipset_data *data = session->data;
+       const struct ipset_type *type;
+       const struct ipset_arg *arg;
+       uint8_t family;
+       int i;
+
+       for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
+               if (nla[i]) {
+                       D("add attr %u, opt %u", i, create_attrs[i].opt);
+                       ATTR2DATA(session, nla, i, create_attrs);
+               }
+
+       type = ipset_type_check(session);
+       if (type == NULL)
+               return MNL_CB_ERROR;
+       family = ipset_data_family(data);
+
+       switch (session->mode) {
+       case IPSET_LIST_SAVE:
+               safe_snprintf(session, "create %s %s ",
+                             ipset_data_setname(data),
+                             type->name);
+               if (family == AF_INET6)
+                       sprintf(session->outbuf, "family inet6 ");
+               break;
+       case IPSET_LIST_PLAIN:
+               safe_snprintf(session, "Name: %s\nType: %s\n",
+                             ipset_data_setname(data),
+                             type->name);
+               if (family == AF_INET6)
+                       safe_snprintf(session, "Family: INET6\n");
+               safe_snprintf(session, "Header: ");
+               break;
+       case IPSET_LIST_XML:
+               safe_snprintf(session,
+                             "<ipset name=\"%s\">\n"
+                             "  <type>%s</type>\n",
+                             ipset_data_setname(data),
+                             type->name);
+               if (family == AF_INET6)
+                       safe_snprintf(session, "  <family>INET6</family>\n");
+               safe_snprintf(session, "  <header>\n");
+               break;
+       default:
+               break;
+       }
+
+       for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->print; arg++) {
+               if (!ipset_data_test(data, arg->opt))
+                       continue;
+               switch (session->mode) {
+               case IPSET_LIST_SAVE:
+               case IPSET_LIST_PLAIN:
+                       safe_snprintf(session, "%s ", arg->name[0]);
+                       if (arg->has_arg == IPSET_NO_ARG)
+                               break;
+                       safe_dprintf(session, arg->print, arg->opt);
+                       safe_snprintf(session, " ");
+                       break;
+               case IPSET_LIST_XML:
+                       if (arg->has_arg == IPSET_NO_ARG) {
+                               safe_snprintf(session,
+                                             "    <%s/>\n",
+                                             arg->name[0]);
+                               break;
+                       }
+                       safe_snprintf(session, "    <%s>",
+                                     arg->name[0]);
+                       safe_dprintf(session, arg->print, arg->opt);
+                       safe_snprintf(session, "</%s>\n",
+                                     arg->name[0]);
+                       break;
+               default:
+                       break;
+               }
+       }
+       switch (session->mode) {
+       case IPSET_LIST_SAVE:
+               safe_snprintf(session, "\n");
+               break;
+       case IPSET_LIST_PLAIN:
+               safe_snprintf(session, "\nElements: ");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
+               safe_snprintf(session, "\nSize in memory: ");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
+               safe_snprintf(session, "\nReferences: ");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
+               safe_snprintf(session, "\nMembers:\n");
+               break;
+       case IPSET_LIST_XML:
+               safe_snprintf(session, "    <elements>");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
+               safe_snprintf(session, "</elements>\n    <memsize>");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
+               safe_snprintf(session, "</memsize>\n    <references>");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
+               safe_snprintf(session, "</references>\n  </header>\n  <members>\n");
+               break;
+       default:
+               break;
+       }
+
+       return MNL_CB_OK;
+}
+
+static int
+print_set_done(struct ipset_session *session)
+{
+       D("called");
+       switch (session->mode) {
+       case IPSET_LIST_XML:
+               if (session->saved_setname[0] == '\0')
+                       safe_snprintf(session, "\n");
+               else
+                       safe_snprintf(session, "  </members>\n</ipset>\n");
+               break;
+       case IPSET_LIST_SAVE:
+               /* No empty lines between the sets */
+               break;
+       default:
+               safe_snprintf(session, "\n");
+               break;
+       }
+       return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
+}
+
+static int
+generic_data_attr_cb(const struct nlattr *attr, void *data,
+                    int attr_max, const struct ipset_attr_policy *policy)
+{
+       const struct nlattr **tb = (const struct nlattr **)data;
+       int type = mnl_attr_get_type(attr);
+       
+       D("attr type: %u, len %u", type, attr->nla_len);
+       if (mnl_attr_type_valid(attr, attr_max) < 0) {
+               D("attr type: %u INVALID", type);
+               return MNL_CB_ERROR;
+       }
+       if (mnl_attr_validate(attr, policy[type].type) < 0) {
+               D("attr type: %u POLICY, attrlen %u", type,
+                 mnl_attr_get_payload_len(attr));
+               return MNL_CB_ERROR;
+       }
+       if (policy[type].type == MNL_TYPE_NUL_STRING
+           && mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
+               return MNL_CB_ERROR;
+       tb[type] = attr;
+       return MNL_CB_OK;
+}
+
+static int
+create_attr_cb(const struct nlattr *attr, void *data)
+{
+       return generic_data_attr_cb(attr, data,
+                                   IPSET_ATTR_CREATE_MAX, create_attrs);
+}
+
+static int
+adt_attr_cb(const struct nlattr *attr, void *data)
+{
+       return generic_data_attr_cb(attr, data,
+                                   IPSET_ATTR_ADT_MAX, adt_attrs);
+}
+
+static int
+callback_list(struct ipset_session *session, struct nlattr *nla[],
+             enum ipset_cmd cmd)
+{
+       struct ipset_data *data = session->data;
+
+       if (!nla[IPSET_ATTR_SETNAME])
+               FAILURE("Broken %s kernel message: missing setname!",
+                       cmd2name[cmd]);
+
+       ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs);
+       if (STREQ(ipset_data_setname(data), session->saved_setname)) {
+               /* Header part already seen */
+               if (ipset_data_test(data, IPSET_OPT_TYPE)
+                   && nla[IPSET_ATTR_DATA] != NULL)
+                       FAILURE("Broken %s kernel message: "
+                               "extra DATA received!", cmd2name[cmd]);
+       } else {
+               if (nla[IPSET_ATTR_DATA] == NULL)
+                       FAILURE("Broken %s kernel message: "
+                               "missing DATA part!", cmd2name[cmd]);
+                                        
+               /* Close previous set printing */
+               if (session->saved_setname[0] != '\0')
+                       print_set_done(session);
+       }
+
+       if (nla[IPSET_ATTR_DATA] != NULL) {
+               struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {};
+
+               if (!(nla[IPSET_ATTR_TYPENAME]
+                     && nla[IPSET_ATTR_FAMILY]
+                     && nla[IPSET_ATTR_REVISION]))
+                       FAILURE("Broken %s kernel message: missing %s!",
+                               cmd2name[cmd],
+                               !nla[IPSET_ATTR_TYPENAME] ? "typename" : 
+                               !nla[IPSET_ATTR_FAMILY] ? "family" : "revision");
+
+               ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
+               ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
+               ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
+
+               /* Reset CREATE specific flags */
+               ipset_data_flags_unset(data, IPSET_CREATE_FLAGS);
+               if (mnl_attr_parse_nested(nla[IPSET_ATTR_DATA],
+                                         create_attr_cb, cattr) < 0)
+                       FAILURE("Broken %s kernel message: "
+                               "cannot validate DATA attributes!",
+                               cmd2name[cmd]);
+               if (list_create(session, cattr) != MNL_CB_OK)
+                       return MNL_CB_ERROR;
+               strcpy(session->saved_setname, ipset_data_setname(data));
+       }
+       
+       if (nla[IPSET_ATTR_ADT] != NULL) {
+               struct nlattr *tb, *adt[IPSET_ATTR_ADT_MAX+1];
+               
+               mnl_attr_for_each_nested(tb, nla[IPSET_ATTR_ADT]) {
+                       memset(adt, 0, sizeof(adt));
+                       /* Reset ADT specific flags */
+                       ipset_data_flags_unset(data, IPSET_ADT_FLAGS);
+                       if (mnl_attr_parse_nested(tb, adt_attr_cb, adt) < 0)
+                               FAILURE("Broken %s kernel message: "
+                                       "cannot validate ADT attributes!",
+                                       cmd2name[cmd]);
+                       if (list_adt(session, adt) != MNL_CB_OK)
+                               return MNL_CB_ERROR;
+               }
+       }
+       return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
+}
+
+#ifndef IPSET_PROTOCOL_MIN
+#define IPSET_PROTOCOL_MIN     IPSET_PROTOCOL
+#endif
+
+#ifndef IPSET_PROTOCOL_MAX
+#define IPSET_PROTOCOL_MAX     IPSET_PROTOCOL
+#endif
+
+static int
+callback_version(struct ipset_session *session, struct nlattr *nla[])
+{
+       uint8_t min, max;
+       
+       min = max = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
+
+       if (nla[IPSET_ATTR_PROTOCOL_MIN]) {
+               min = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL_MIN]);
+               D("min: %u", min);
+       }
+
+       if (min > IPSET_PROTOCOL_MAX || max < IPSET_PROTOCOL_MIN)
+               FAILURE("Cannot communicate with kernel: "
+                       "Kernel support protocol versions %u-%u "
+                       "while userspace supports protocol versions %u-%u",
+                       min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
+
+       if (!(session->envopts & IPSET_ENV_QUIET)
+           && max != IPSET_PROTOCOL_MAX)
+               ipset_warn(session,
+                          "Kernel support protocol versions %u-%u "
+                          "while userspace supports protocol versions %u-%u",
+                          min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
+
+       session->version_checked = true;
+
+       return MNL_CB_STOP;
+}
+
+static int
+callback_header(struct ipset_session *session, struct nlattr *nla[])
+{
+       const char *setname;
+       struct ipset_data *data = session->data;
+       
+       if (!nla[IPSET_ATTR_SETNAME])
+               FAILURE("Broken HEADER kernel message: missing setname!");
+
+       setname = mnl_attr_get_str(nla[IPSET_ATTR_SETNAME]);
+       if (!STREQ(setname, ipset_data_setname(data)))
+               FAILURE("Broken HEADER kernel message: sent setname `%s' "
+                       "does not match with received one `%s'!",
+                       ipset_data_setname(data), setname);
+       
+       if (!(nla[IPSET_ATTR_TYPENAME]
+             && nla[IPSET_ATTR_REVISION]
+             && nla[IPSET_ATTR_FAMILY]))
+               FAILURE("Broken HEADER kernel message: "
+                       "missing attribute '%s'!",
+                       !nla[IPSET_ATTR_TYPENAME] ? "typename" :
+                       !nla[IPSET_ATTR_REVISION] ? "revision" :
+                       "family");
+
+       ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
+       ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
+       ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
+
+       return MNL_CB_STOP;
+}
+
+static int
+callback_type(struct ipset_session *session, struct nlattr *nla[])
+{
+       struct ipset_data *data = session->data;
+       const char *typename, *orig;
+       
+       if (!(nla[IPSET_ATTR_TYPENAME]
+             && nla[IPSET_ATTR_REVISION]
+             && nla[IPSET_ATTR_FAMILY]))
+               FAILURE("Broken TYPE kernel message: "
+                       "missing attribute '%s'!",
+                       !nla[IPSET_ATTR_TYPENAME] ? "typename" :
+                       !nla[IPSET_ATTR_REVISION] ? "revision" :
+                       "family");
+
+       typename = mnl_attr_get_str(nla[IPSET_ATTR_TYPENAME]);
+       orig = ipset_data_get(data, IPSET_OPT_TYPENAME);
+       if (!STREQ(typename, orig))
+               FAILURE("Broken TYPE kernel message: sent typename `%s' "
+                       "does not match with received one `%s'!",
+                       orig, typename);
+       
+       ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
+       ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
+       ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
+       if (nla[IPSET_ATTR_REVISION_MIN])
+               ATTR2DATA(session, nla, IPSET_ATTR_REVISION_MIN, cmd_attrs);
+
+       return MNL_CB_STOP;
+}
+
+static int
+cmd_attr_cb(const struct nlattr *attr, void *data)
+{
+       return generic_data_attr_cb(attr, data, IPSET_ATTR_CMD_MAX, cmd_attrs);
+}
+
+#if 0
+static int
+mnl_attr_parse_dbg(const struct nlmsghdr *nlh, int offset,
+                  mnl_attr_cb_t cb, void *data)
+{
+       int ret = MNL_CB_OK;
+       struct nlattr *attr = mnl_nlmsg_get_payload_offset(nlh, offset);
+       int len = nlh->nlmsg_len - MNL_NLMSG_HDRLEN - MNL_ALIGN(offset);
+       
+       while (mnl_attr_ok(attr, len)) {
+               D("attr: type %u, attrlen %u, len %u",
+                 mnl_attr_get_type(attr), attr->nla_len, len);
+               if (cb && (ret = cb(attr, data)) <= MNL_CB_STOP)
+                       return ret;
+               attr = mnl_attr_next(attr, &len);
+       }
+       return ret;
+}
+#endif
+
+static int
+callback_data(const struct nlmsghdr *nlh, void *data)
+{
+       struct ipset_session *session = data;
+       struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
+       uint8_t proto, cmd;
+       int ret = MNL_CB_OK, nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
+       
+       D("called, nlmsg_len %u", nlh->nlmsg_len);
+       cmd = ipset_get_nlmsg_type(nlh);
+       if (cmd == IPSET_CMD_LIST && session->cmd == IPSET_CMD_SAVE)
+               /* Kernel always send IPSET_CMD_LIST */
+               cmd = IPSET_CMD_SAVE;
+
+       if (cmd != session->cmd)
+               FAILURE("Protocol error, we sent command %s "
+                       "and received %s[%u]",
+                       cmd2name[session->cmd],
+                       cmd < IPSET_MSG_MAX ? cmd2name[cmd] : "unknown", cmd);
+
+       if (mnl_attr_parse(nlh, nfmsglen, cmd_attr_cb, nla) < MNL_CB_STOP)
+               FAILURE("Broken %s kernel message: "
+                       "cannot validate and parse attributes",
+                       cmd2name[cmd]);
+
+       if (!nla[IPSET_ATTR_PROTOCOL])
+               FAILURE("Sad, sad day: kernel message %s "
+                       "does not carry the protocol version.",
+                       cmd2name[cmd]);
+
+       proto = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
+
+       /* Check protocol */
+       if (cmd != IPSET_CMD_PROTOCOL && proto != IPSET_PROTOCOL)
+               FAILURE("Giving up: kernel protocol version %u "
+                       "does not match our protocol version %u",
+                       proto, IPSET_PROTOCOL);
+
+       D("Message: %s", cmd2name[cmd]);
+       switch (cmd) {
+       case IPSET_CMD_LIST:
+       case IPSET_CMD_SAVE:
+               ret = callback_list(session, nla, cmd);
+               D("flag multi: %u", nlh->nlmsg_flags & NLM_F_MULTI);
+               if (ret >= MNL_CB_STOP && !(nlh->nlmsg_flags & NLM_F_MULTI))
+                       ret = print_set_done(session);
+               break;
+       case IPSET_CMD_PROTOCOL:
+               if (!session->version_checked)
+                       ret = callback_version(session, nla);
+               break;
+       case IPSET_CMD_HEADER:
+               ret = callback_header(session, nla);
+               break;
+       case IPSET_CMD_TYPE:
+               ret = callback_type(session, nla);
+               break;
+       default:
+               FAILURE("Data message received when not expected at %s",
+                       cmd2name[session->cmd]);
+       }
+       D("return code: %s", ret == MNL_CB_STOP ? "stop" :
+                            ret == MNL_CB_OK ? "ok" : "error");
+       return ret;
+}
+
+static int
+callback_done(const struct nlmsghdr *nlh UNUSED, void *data)
+{
+       struct ipset_session *session = data;
+
+       D(" called");
+       if (session->cmd == IPSET_CMD_LIST || session->cmd == IPSET_CMD_SAVE)
+               return print_set_done(session);
+       
+       FAILURE("Invalid message received in non LIST or SAVE state.");
+}
+
+static int
+decode_errmsg(struct ipset_session *session, const struct nlmsghdr *nlh)
+{
+       const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+       const struct nlmsghdr *msg = &err->msg;
+       struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
+       enum ipset_cmd cmd;
+       int nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
+       
+       if (nlh->nlmsg_len < (uint32_t) MNL_ALIGN(sizeof(struct nlmsgerr))
+           || nlh->nlmsg_len < MNL_ALIGN(sizeof(struct nlmsgerr))
+                               + msg->nlmsg_len)
+               FAILURE("Broken error report message received.");
+
+       cmd = ipset_get_nlmsg_type(msg);
+       D("nlsmg_len: %u", msg->nlmsg_len);
+       if (cmd != session->cmd)
+               FAILURE("Protocol error, we sent command %s "
+                       "and received error report for %s[%u]",
+                       cmd2name[session->cmd],
+                       cmd < IPSET_MSG_MAX ? cmd2name[cmd] : "unknown", cmd);
+
+       if (mnl_attr_parse(msg, nfmsglen, cmd_attr_cb, nla) < MNL_CB_STOP)
+               FAILURE("Broken %s error report message: "
+                       "cannot validate attributes",
+                       cmd2name[cmd]);
+
+       if (!nla[IPSET_ATTR_PROTOCOL])
+               FAILURE("Broken %s error report message: "
+                       "missing protocol attribute",
+                       cmd2name[cmd]);
+       
+       if (nla[IPSET_ATTR_LINENO]) {
+               session->lineno = mnl_attr_get_u32(nla[IPSET_ATTR_LINENO]);
+               if (nla[IPSET_ATTR_LINENO]->nla_type & NLA_F_NET_BYTEORDER)
+                       session->lineno = ntohl(session->lineno);
+       }
+       
+       return ipset_errcode(session, cmd, -err->error);
+}      
+
+static int
+callback_error(const struct nlmsghdr *nlh, void *cbdata)
+{
+       struct ipset_session *session = cbdata;
+       struct ipset_data *data = session->data;
+       const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+       int ret = MNL_CB_ERROR;
+
+       D(" called, cmd %s", cmd2name[session->cmd]);
+       if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr)))
+               FAILURE("Broken error message received.");
+
+       if (err->error == 0) {
+               /* ACK */
+               ret = MNL_CB_STOP;
+
+               switch (session->cmd) {
+               case IPSET_CMD_CREATE:
+                       /* Add successfully created set to the cache */
+                       ipset_cache_add(ipset_data_setname(data),
+                                       ipset_data_get(data, IPSET_OPT_TYPE));
+                       break;
+               case IPSET_CMD_DESTROY:
+                       /* Delete destroyed sets from the cache */
+                       ipset_cache_del(ipset_data_setname(data));
+                       /* Fall through */
+               case IPSET_CMD_FLUSH:
+                       break;
+               case IPSET_CMD_RENAME:
+                       ipset_cache_rename(ipset_data_setname(data),
+                                          ipset_data_get(data, IPSET_OPT_SETNAME2));
+                       break;
+               case IPSET_CMD_SWAP:
+                       ipset_cache_swap(ipset_data_setname(data),
+                                        ipset_data_get(data, IPSET_OPT_SETNAME2));
+                       break;
+               case IPSET_CMD_TEST:
+                       if (!(session->envopts & IPSET_ENV_QUIET)) {
+                               ipset_print_elem(session->report, IPSET_ERRORBUFLEN,
+                                                session->data, IPSET_OPT_NONE, 0);
+                               ipset_warn(session, " is in set %s.",
+                                          ipset_data_setname(data));
+                       }
+                       /* Fall through */
+               case IPSET_CMD_ADD:
+               case IPSET_CMD_DEL:
+                       break;
+               case IPSET_CMD_LIST:
+               case IPSET_CMD_SAVE:
+                       /* No set in kernel */
+                       print_set_done(session);
+                       break;
+               default:
+                       FAILURE("ACK message received to command %s[%u], which is not expected",
+                               session->cmd < IPSET_MSG_MAX
+                               ? cmd2name[session->cmd] : "unknown",
+                               session->cmd);
+               }
+               return ret;
+       }
+       D("nlmsgerr error: %u", -err->error);
+
+       /* Error messages */
+       
+       /* Special case for IPSET_CMD_TEST */
+       if (session->cmd == IPSET_CMD_TEST
+           && err->error == -IPSET_ERR_EXIST) {
+               if (!(session->envopts & IPSET_ENV_QUIET)) {
+                       ipset_print_elem(session->report, IPSET_ERRORBUFLEN,
+                                        session->data, IPSET_OPT_NONE, 0);
+                       ipset_warn(session, " is NOT in set %s.",
+                                  ipset_data_setname(data));
+               }
+               return ret;
+       }
+
+       decode_errmsg(session, nlh);
+       
+       return ret;
+}
+
+static int
+callback_noop(const struct nlmsghdr *nlh UNUSED, void *data UNUSED)
+{
+       return MNL_CB_OK;
+}
+/*
+ * Build and send messages
+ */
+
+static inline struct nlattr *
+nla_nest_start(struct nlmsghdr *nlh,  int attr)
+{
+       struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
+
+       start->nla_type = attr | NLA_F_NESTED;
+       start->nla_len = MNL_ALIGN(sizeof(struct nlattr));
+       
+       nlh->nlmsg_len += start->nla_len;
+
+       return start;
+}
+
+static inline void
+nla_nest_end(struct nlmsghdr *nlh, struct nlattr *start)
+{
+       start->nla_len = (void *) mnl_nlmsg_get_payload_tail(nlh)
+                        - (void *) start;
+}
+
+static inline void
+open_nested(struct ipset_session *session, struct nlmsghdr *nlh, int attr)
+{
+       session->nested[session->nestid++] = nla_nest_start(nlh, attr);
+}
+
+static inline void
+close_nested(struct ipset_session *session, struct nlmsghdr *nlh)
+{
+       nla_nest_end(nlh, session->nested[session->nestid-1]);
+       session->nested[--session->nestid] = NULL;
+}
+
+static size_t
+attr_len(const struct ipset_attr_policy *attr, uint8_t family, uint16_t *flags)
+{
+       switch (attr->type) {
+       case MNL_TYPE_BINARY:
+               if (attr->len)
+                       return attr->len;
+
+               *flags = NLA_F_NET_BYTEORDER;
+               return family == AF_INET ? sizeof(uint32_t)
+                                        : sizeof(struct in6_addr);
+       case MNL_TYPE_U32:
+               *flags = NLA_F_NET_BYTEORDER;
+               return sizeof(uint32_t);
+       case MNL_TYPE_U16:
+               *flags = NLA_F_NET_BYTEORDER;
+               return sizeof(uint16_t);
+       case MNL_TYPE_U8:
+               return sizeof(uint8_t);
+       default:
+               return attr->len;
+       }
+}
+
+static int
+rawdata2attr(struct nlmsghdr *nlh,
+            const void *d, int type, uint8_t family,
+            const struct ipset_attr_policy attrs[])
+{
+       const struct ipset_attr_policy *attr;
+       int alen;
+       uint16_t flags = 0;
+
+
+       attr = &attrs[type];
+       alen = attr_len(attr, family, &flags);
+
+       switch (attr->type) {
+       case MNL_TYPE_U32: {
+               uint32_t value = htonl(*(uint32_t *)d);
+               
+               d = &value;
+               break;
+       }
+       case MNL_TYPE_U16: {
+               uint16_t value = htons(*(uint16_t *)d);
+               
+               d = &value;
+               break;
+       }
+       default:
+               break;
+       }
+       
+       mnl_attr_put(nlh, type | flags, alen, d);
+
+       return 0;
+}
+
+static int
+data2attr(struct nlmsghdr *nlh,
+         struct ipset_data *data, int type, uint8_t family,
+         const struct ipset_attr_policy attrs[])
+{
+       const struct ipset_attr_policy *attr = &attrs[type];
+
+       return rawdata2attr(nlh, ipset_data_get(data, attr->opt),
+                           type, family, attrs);
+}
+
+#define ADDATTR_PROTOCOL(nlh)                                          \
+       mnl_attr_put_u8(nlh, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL)
+
+#define ADDATTR(nlh, data, type, family, attrs)                                \
+       data2attr(nlh, data, type, family, attrs)
+
+#define ADDATTR_SETNAME(nlh, data)                                     \
+       data2attr(nlh, data, IPSET_ATTR_SETNAME, AF_INET, cmd_attrs)
+
+#define ADDATTR_IF(nlh, data, type, family, attrs)                     \
+       if (ipset_data_test(data, attrs[type].opt)) {                   \
+               D("addattr if: %u", attrs[type].opt);                   \
+               data2attr(nlh, data, type, family, attrs); }
+
+#define ADDATTR_RAW(nlh, data, type, attrs)                            \
+       rawdata2attr(nlh, data, type, AF_INET, attrs)
+
+static void
+addattr_create(struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family)
+{
+       int i;
+
+       for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
+               ADDATTR_IF(nlh, data, i, family, create_attrs);
+}
+
+static void
+addattr_adt(struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family)
+{
+       int i;
+       
+       for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
+               ADDATTR_IF(nlh, data, i, family, adt_attrs);
+}
+
+static inline bool
+buffer_full(struct ipset_session *session)
+{
+       struct nlmsghdr *nlh;
+       const struct ipset_type *type;
+       int sizeid;
+
+       nlh = (struct nlmsghdr *) session->buffer;
+       type = ipset_data_get(session->data, IPSET_OPT_TYPE);
+       sizeid = !!(ipset_data_family(session->data) != AF_INET);
+
+       /* Current size + ADD/DEL max size + nlsmgerr must fit into buffer */
+       return session->bufsize < nlh->nlmsg_len
+                                 + type->maxsize[sizeid]
+                                 + MNL_ALIGN(sizeof(struct nlmsgerr));
+}
+
+#define PRIVATE_MSG_BUFLEN     256
+
+static int
+build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
+{
+       char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned));
+       struct nlmsghdr *nlh = (struct nlmsghdr *) buffer;
+       struct ipset_data *data = session->data;
+       int len = PRIVATE_MSG_BUFLEN, ret;
+       enum ipset_cmd saved = session->cmd;
+
+       /* Initialize header */
+       session->transport->fill_hdr(session->handle, cmd, buffer, len, 0);
+               
+       ADDATTR_PROTOCOL(nlh);
+
+       switch (cmd) {
+       case IPSET_CMD_PROTOCOL:
+               break;
+       case IPSET_CMD_HEADER:
+               if (!ipset_data_test(data, IPSET_SETNAME))
+                       return ipset_err(session,
+                               "Invalid internal HEADER command: "
+                               "missing setname");
+               ADDATTR_SETNAME(nlh, data);
+               break;
+       case IPSET_CMD_TYPE:
+               if (!ipset_data_test(data, IPSET_OPT_TYPENAME))
+                       return ipset_err(session,
+                               "Invalid internal TYPE command: "
+                               "missing settype");
+               ADDATTR(nlh, data, IPSET_ATTR_TYPENAME, AF_INET, cmd_attrs);
+               if (ipset_data_test(data, IPSET_OPT_FAMILY))
+                       ADDATTR(nlh, data, IPSET_ATTR_FAMILY, AF_INET, cmd_attrs);
+               else
+                       /* bitmap:port and list:set types */
+                       mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, AF_UNSPEC);
+               break;
+       default:
+               return ipset_err(session, "Internal error: "
+                                "unknown private command %u", cmd);
+       }
+
+       /* Backup, then restore real command */
+       session->cmd = cmd;
+       ret = session->transport->query(session->handle, buffer, len);
+       session->cmd = saved;
+
+       return ret;
+}
+
+static inline bool
+may_aggregate_ad(struct ipset_session *session, struct ipset_data *data,
+                enum ipset_cmd cmd)
+{
+       return (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)
+              && cmd == session->cmd
+              && STREQ(ipset_data_setname(data), session->saved_setname);
+} 
+
+static int
+build_msg(struct ipset_session *session, bool aggregate)
+{
+       struct nlmsghdr *nlh = (struct nlmsghdr *) session->buffer;
+       struct ipset_data *data = session->data;
+
+       /* Public commands */
+       D("cmd %s, nlmsg_len: %u", cmd2name[session->cmd], nlh->nlmsg_len);
+       if (nlh->nlmsg_len == 0) {
+               /* Initialize header */
+               aggregate = false;
+               session->transport->fill_hdr(session->handle,
+                                            session->cmd,
+                                            session->buffer,
+                                            session->bufsize,
+                                            session->envopts);
+               ADDATTR_PROTOCOL(nlh);
+       }
+       D("Protocol added, aggregate %s", aggregate ? "yes" : "no");
+       switch (session->cmd) {
+       case IPSET_CMD_CREATE: {
+               const struct ipset_type *type;
+
+               /* Sanity checkings */
+               if (!ipset_data_test(data, IPSET_SETNAME))
+                       return ipset_err(session,
+                               "Invalid create command: missing setname");
+               if (!ipset_data_test(data, IPSET_OPT_TYPE))
+                       return ipset_err(session,
+                               "Invalid create command: missing settype");
+               
+               type = ipset_data_get(data, IPSET_OPT_TYPE);
+               /* Core attributes:
+                * setname, typename, revision, family, flags (optional) */
+               ADDATTR_SETNAME(nlh, data);
+               ADDATTR(nlh, data, IPSET_ATTR_TYPENAME, AF_INET, cmd_attrs);
+               ADDATTR_RAW(nlh, &type->revision,
+                           IPSET_ATTR_REVISION, cmd_attrs);
+               D("family: %u", ipset_data_family(data));
+               ADDATTR(nlh, data, IPSET_ATTR_FAMILY, AF_INET, cmd_attrs);
+
+               /* Type-specific create attributes */
+               D("call open_nested");
+               open_nested(session, nlh, IPSET_ATTR_DATA);
+               addattr_create(nlh, data, type->family);
+               D("call close_nested");
+               close_nested(session, nlh);
+               break;
+       }
+       case IPSET_CMD_DESTROY:
+       case IPSET_CMD_FLUSH:
+       case IPSET_CMD_LIST:
+       case IPSET_CMD_SAVE:
+               if (ipset_data_test(data, IPSET_SETNAME))
+                       ADDATTR_SETNAME(nlh, data);
+               break;
+       case IPSET_CMD_RENAME:
+       case IPSET_CMD_SWAP:
+               if (!ipset_data_test(data, IPSET_SETNAME))
+                       return ipset_err(session,
+                               "Invalid %s command: missing from-setname",
+                               session->cmd == IPSET_CMD_SWAP ? "swap" : "rename");
+               if (!ipset_data_test(data, IPSET_OPT_SETNAME2))
+                       return ipset_err(session,
+                               "Invalid %s command: missing to-setname",
+                               session->cmd == IPSET_CMD_SWAP ? "swap" : "rename");
+               ADDATTR_SETNAME(nlh, data);
+               ADDATTR_RAW(nlh, ipset_data_get(data, IPSET_OPT_SETNAME2),
+                           IPSET_ATTR_SETNAME2, cmd_attrs);
+               break;
+       case IPSET_CMD_ADD:
+       case IPSET_CMD_DEL: {
+               const struct ipset_type *type;
+
+               if (!aggregate) {
+                       /* Setname, type not checked/added yet */
+                       if (!ipset_data_test(data, IPSET_SETNAME))
+                               return ipset_err(session,
+                                       "Invalid %s command: missing setname",
+                                       session->cmd == IPSET_CMD_ADD ? "add" : "del");
+
+                       if (!ipset_data_test(data, IPSET_OPT_TYPE))
+                               return ipset_err(session,
+                                       "Invalid %s command: missing settype",
+                                       session->cmd == IPSET_CMD_ADD ? "add" : "del");
+
+                       /* Core options: setname */
+                       ADDATTR_SETNAME(nlh, data);
+                       if (session->lineno != 0) {
+                               /* Restore mode */
+                               ADDATTR_RAW(nlh, &session->lineno,
+                                           IPSET_ATTR_LINENO, cmd_attrs);
+                               open_nested(session, nlh, IPSET_ATTR_ADT);
+                       }
+               }
+               type = ipset_data_get(data, IPSET_OPT_TYPE);
+               open_nested(session, nlh, IPSET_ATTR_DATA);
+               addattr_adt(nlh, data, type->family);
+               ADDATTR_RAW(nlh, &session->lineno,
+                           IPSET_ATTR_LINENO, cmd_attrs);
+               close_nested(session, nlh);
+               break;
+       }
+       case IPSET_CMD_TEST: {
+               const struct ipset_type *type;
+               /* Return codes are not aggregated, so tests cannot be either */
+
+               /* Setname, type not checked/added yet */
+               
+               if (!ipset_data_test(data, IPSET_SETNAME))
+                       return ipset_err(session,
+                               "Invalid test command: missing setname");
+
+               if (!ipset_data_test(data, IPSET_OPT_TYPE))
+                       return ipset_err(session,
+                               "Invalid test command: missing settype");
+               
+               type = ipset_data_get(data, IPSET_OPT_TYPE);
+               ADDATTR_SETNAME(nlh, data);
+               open_nested(session, nlh, IPSET_ATTR_DATA);
+               addattr_adt(nlh, data, type->family);
+               close_nested(session, nlh);
+               break;
+       }
+       default:
+               return ipset_err(session, "Internal error: unknown command %u",
+                                session->cmd);
+       }
+       return 0;
+}
+
+/**
+ * ipset_commit - commit buffered commands
+ * @session: session structure
+ *
+ * Commit buffered commands, if there are any.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_commit(struct ipset_session *session)
+{
+       struct nlmsghdr *nlh;
+       int ret = 0, i;
+
+       assert(session);
+
+       nlh = (struct nlmsghdr *) session->buffer;
+       D("send buffer: len %u, cmd %s", nlh->nlmsg_len, cmd2name[session->cmd]);
+       if (nlh->nlmsg_len == 0)
+               /* Nothing to do */
+               return 0;
+
+       /* Close nested data blocks */
+       for (i = session->nestid - 1; i >= 0; i--)
+               close_nested(session, nlh);
+
+       /* Send buffer */
+       ret = session->transport->query(session->handle,
+                                       session->buffer,
+                                       session->bufsize);
+
+       /* Reset data block and nested state */
+       for (i = session->nestid - 1; i >= 0; i--)
+               session->nested[i] = NULL;
+       session->nestid = 0;
+       nlh->nlmsg_len = 0;
+
+       D("ret: %d", ret);
+
+       if (ret < 0) {
+               if (session->report[0] != '\0')
+                       return -1;
+               else
+                       return ipset_err(session,
+                                        "Internal protocol error");
+       }
+       return 0;
+}
+
+static mnl_cb_t cb_ctl[] = {
+       [NLMSG_NOOP] = callback_noop,
+       [NLMSG_ERROR] = callback_error,
+       [NLMSG_DONE]  = callback_done,
+       [NLMSG_OVERRUN] = callback_noop,
+       [NLMSG_MIN_TYPE] = callback_data,
+};
+       
+static inline struct ipset_handle *
+init_transport(struct ipset_session *session)
+{
+       session->handle = session->transport->init(cb_ctl, session);
+
+       return session->handle;
+}
+
+/**
+ * ipset_cmd - execute a command
+ * @session: session structure
+ * @cmd: command to execute
+ * @lineno: command line number in restore mode
+ *
+ * Execute - or prepare/buffer in restore mode - a command.
+ * It is the caller responsibility that the data field be filled out
+ * with all required parameters for a successful execution.
+ * The data field is cleared after this function call for the public
+ * commands.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
+{
+       struct ipset_data *data;
+       bool aggregate = false;
+       int ret = -1;
+       
+       assert(session);
+
+       if (cmd <= IPSET_CMD_NONE || cmd >= IPSET_MSG_MAX)
+               return 0;
+
+       /* Initialize transport method if not done yet */
+       if (session->handle == NULL && init_transport(session) == NULL)
+               return ipset_err(session,
+                                "Cannot open session to kernel.");
+
+       data = session->data;
+
+       /* Check protocol version once */
+       if (!session->version_checked) {
+               if (build_send_private_msg(session, IPSET_CMD_PROTOCOL) < 0)
+                       return -1;
+       }
+
+       /* Private commands */
+       if (cmd == IPSET_CMD_TYPE || cmd == IPSET_CMD_HEADER) 
+               return build_send_private_msg(session, cmd);
+       
+       /* Check buffer and aggregatable commands */
+       if (session->lineno != 0) {
+               if (may_aggregate_ad(session, data, cmd))
+                       aggregate = true;
+               if (!aggregate || buffer_full(session)) {
+                        if (ipset_commit(session) < 0)
+                               goto cleanup;
+               }
+       }
+
+       /* Real command: update lineno too */
+       session->cmd = cmd;
+       session->lineno = lineno;
+       
+       /* Set default output mode */
+       if (cmd == IPSET_CMD_LIST) {
+               if (session->mode == IPSET_LIST_NONE)
+                       session->mode = IPSET_LIST_PLAIN;
+       } else if (cmd == IPSET_CMD_SAVE) {
+               if (session->mode == IPSET_LIST_NONE)
+                       session->mode = IPSET_LIST_SAVE;
+       }
+
+       D("next: build_msg");
+       /* Build new message or append buffered commands */
+       if (build_msg(session, aggregate) < 0)
+               goto cleanup;
+       D("past: build_msg");
+
+       /* Save setname for the next possible aggregated restore line */
+       if (session->lineno != 0
+           && (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) {
+               strcpy(session->saved_setname, ipset_data_setname(data));
+               ret = 0;
+               /* Don't commit: we may aggregate next command */
+       } else {
+               D("call commit");       
+               ret = ipset_commit(session);
+       }
+cleanup:
+       D("reset data");
+       ipset_data_reset(data);
+       return ret;
+}
+
+/**
+ * ipset_session_init - initialize an ipset session
+ *
+ * Initialize an ipset session by allocating a session structure
+ * and filling out with the initialization data.
+ *
+ * Returns the created session sctructure on success or NULL.
+ */
+struct ipset_session *
+ipset_session_init(ipset_outfn outfn)
+{
+       struct ipset_session *session;
+       size_t bufsize = getpagesize();
+       
+       /* Create session object */
+       session = calloc(1, sizeof(struct ipset_session) + bufsize);
+       if (session == NULL)
+               return NULL;
+       session->bufsize = bufsize;
+
+       /* The single transport method yet */
+       session->transport = &ipset_mnl_transport;
+       
+       /* Output function */
+       session->outfn = outfn;
+       
+       /* Initialize data structures */
+       session->data = ipset_data_init();
+       if (session->data == NULL)
+               goto free_session;
+
+       ipset_types_init();
+       return session;
+
+free_session:
+       free(session);
+       return NULL;
+}
+
+/**
+ * ipset_session_fini - destroy an ipset session
+ * @session: session structure
+ *
+ * Destroy an ipset session: release the created structures.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_session_fini(struct ipset_session *session)
+{
+       assert(session);
+
+       if (session->handle)
+               session->transport->fini(session->handle);
+       if (session->data)
+               ipset_data_fini(session->data);
+
+       ipset_types_fini();
+       free(session);
+       return 0;
+}
diff --git a/lib/types.c b/lib/types.c
new file mode 100644 (file)
index 0000000..a6476ea
--- /dev/null
@@ -0,0 +1,566 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <errno.h>                             /* errno */
+#include <net/ethernet.h>                      /* ETH_ALEN */
+#include <netinet/in.h>                                /* struct in6_addr */
+#include <sys/socket.h>                                /* AF_ */
+#include <stddef.h>                            /* NULL */
+#include <stdlib.h>                            /* malloc, free */
+#include <stdio.h>                             /* FIXME: debug */
+
+#include <libipset/data.h>                     /* ipset_data_* */
+#include <libipset/session.h>                  /* ipset_cmd */
+#include <libipset/utils.h>                    /* STREQ */
+#include <libipset/types.h>                    /* prototypes */
+
+/* Userspace cache of sets which exists in the kernel */
+
+struct ipset {
+       char name[IPSET_MAXNAMELEN];            /* set name */
+       const struct ipset_type *type;          /* set type */
+       struct ipset *next;
+};
+
+static struct ipset_type *typelist = NULL;     /* registered set types */
+static struct ipset *setlist = NULL;           /* cached sets */
+
+/**
+ * ipset_cache_add - add a set to the cache
+ * @name: set name
+ * @type: set type structure
+ *
+ * Add the named set to the internal cache with the specified
+ * set type. The set name must be unique.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_cache_add(const char *name, const struct ipset_type *type)
+{
+       struct ipset *s, *n;
+
+       assert(name);
+       assert(type);
+
+       n = malloc(sizeof(*n));
+       if (n == NULL)
+               return -ENOMEM;
+
+       ipset_strncpy(n->name, name, IPSET_MAXNAMELEN);
+       n->type = type;
+       n->next = NULL; 
+
+       if (setlist == NULL) {
+               setlist = n;
+               return 0;
+       }
+       for (s = setlist; s->next == NULL; s = s->next) {
+               if (STREQ(name, s->name)) {
+                       free(n);
+                       return -EEXIST;
+               }
+       }
+       s->next = n;
+
+       return 0;
+}
+
+/**
+ * ipset_cache_del - delete set from the cache
+ * @name: set name
+ *
+ * Delete the named set from the internal cache. If NULL is
+ * specified as setname, the whole cache is emptied.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_cache_del(const char *name)
+{
+       struct ipset *s, *match = NULL, *prev = NULL;
+
+       if (!name) {
+               for (s = setlist; s != NULL; ) {
+                       prev = s;
+                       s = s->next;
+                       free(prev);
+               }
+               setlist = NULL;
+               return 0;
+       }
+       for (s = setlist; s != NULL && match == NULL; s = s->next) {
+               if (STREQ(s->name, name)) {
+                       match = s;
+                       if (prev == NULL)
+                               setlist = match->next;
+                       else
+                               prev->next = match->next;
+               }
+               prev = s;
+       }
+       if (match == NULL)
+               return -EEXIST;
+       
+       free(match);
+       return 0;
+}
+
+/**
+ * ipset_cache_rename - rename a set in the cache
+ * @from: the set to rename
+ * @to: the new name of the set
+ *
+ * Rename the given set in the cache.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_cache_rename(const char *from, const char *to)
+{
+       struct ipset *s;
+
+       assert(from);
+       assert(to);
+
+       for (s = setlist; s != NULL; s = s->next) {
+               if (STREQ(s->name, from)) {
+                       ipset_strncpy(s->name, to, IPSET_MAXNAMELEN);
+                       return 0;
+               }
+       }
+       return -EEXIST;
+}
+
+/**
+ * ipset_cache_swap - swap two sets in the cache
+ * @from: the first set
+ * @to: the second set
+ *
+ * Swap two existing sets in the cache.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_cache_swap(const char *from, const char *to)
+{
+       struct ipset *s, *a = NULL, *b = NULL;
+
+       assert(from);
+       assert(to);
+
+       for (s = setlist; s != NULL && (a == NULL || b == NULL); s = s->next) {
+               if (a == NULL && STREQ(s->name, from))
+                       a = s;
+               if (b == NULL && STREQ(s->name, to))
+                       b = s;
+       }
+       if (a != NULL && b != NULL) {
+               ipset_strncpy(a->name, to, IPSET_MAXNAMELEN);
+               ipset_strncpy(b->name, from, IPSET_MAXNAMELEN);
+               return 0;
+       }
+               
+       return -EEXIST;
+}
+
+#define MATCH_FAMILY(type, f)  \
+       (f == AF_UNSPEC || type->family == f || type->family == AF_INET46)
+
+static inline const struct ipset_type *
+create_type_get(struct ipset_session *session)
+{
+       struct ipset_type *t, *match = NULL;
+       struct ipset_data *data;
+       const char *typename;
+       uint8_t family, tmin = 0, tmax = 0;
+       const uint8_t *kmin, *kmax;
+       int ret;
+
+       data = ipset_session_data(session);
+       assert(data);
+       typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
+       assert(typename);
+       family = ipset_data_family(data);
+
+       /* Check registered types in userspace */
+       for (t = typelist; t != NULL; t = t->next) {
+               /* Skip revisions which are unsupported by the kernel */
+               if (t->kernel_check == IPSET_KERNEL_MISMATCH)
+                       continue;
+               if ((STREQ(typename, t->name) || STREQ(typename, t->alias))
+                   && MATCH_FAMILY(t, family)) {
+                       if (match == NULL) {
+                               match = t;
+                               tmax = t->revision;
+                       } else if (t->family == match->family)
+                               tmin = t->revision;
+               }       
+       }
+       if (!match)
+               return ipset_errptr(session,
+                                   "Syntax error: unknown settype %s",
+                                   typename);
+       
+       /* Family is unspecified yet: set from matching set type */
+       if (family == AF_UNSPEC && match->family != AF_UNSPEC) {
+               family = match->family == AF_INET46 ? AF_INET : match->family;
+               ipset_data_set(data, IPSET_OPT_FAMILY, &family);
+       }
+
+       if (match->kernel_check == IPSET_KERNEL_OK)
+               goto found;
+
+       /* Check kernel */
+       ret = ipset_cmd(session, IPSET_CMD_TYPE, 0);
+       if (ret != 0)
+               return NULL;
+
+       kmax = ipset_data_get(data, IPSET_OPT_REVISION);
+       if (ipset_data_test(data, IPSET_OPT_REVISION_MIN))
+               kmin = ipset_data_get(data, IPSET_OPT_REVISION_MIN);
+       else
+               kmin = kmax;
+       if (MAX(tmin, *kmin) > MIN(tmax, *kmax)) {
+               if (*kmin > tmax)
+                       return ipset_errptr(session,
+                               "Kernel supports %s type with family %s "
+                               "in minimal revision %u while ipset library "
+                               "in maximal revision %u. "
+                               "You need to upgrade your ipset library.",
+                               typename,
+                               family == AF_INET ? "INET" :
+                               family == AF_INET6 ? "INET6" : "UNSPEC",
+                               *kmin, tmax);
+               else
+                       return ipset_errptr(session,
+                               "Kernel supports %s type with family %s "
+                               "in maximal revision %u while ipset library "
+                               "in minimal revision %u. "
+                               "You need to upgrade your kernel.",
+                               typename,
+                               family == AF_INET ? "INET" :
+                               family == AF_INET6 ? "INET6" : "UNSPEC",
+                               *kmax, tmin);
+       }
+       
+       match->kernel_check = IPSET_KERNEL_OK;
+found:
+       ipset_data_set(data, IPSET_OPT_TYPE, match);
+       
+       return match;
+}
+
+#define set_family_and_type(data, match, family) do {          \
+       if (family == AF_UNSPEC && match->family != AF_UNSPEC) {        \
+               family = match->family == AF_INET46 ? AF_INET : match->family;\
+               ipset_data_set(data, IPSET_OPT_FAMILY, &family);\
+       }                                                       \
+       ipset_data_set(data, IPSET_OPT_TYPE, match);            \
+} while (0)
+
+
+static inline const struct ipset_type *
+adt_type_get(struct ipset_session *session)
+{
+       struct ipset_data *data;
+       struct ipset *s;
+       struct ipset_type *t;
+       const struct ipset_type *match;
+       const char *setname, *typename;
+       const uint8_t *revision;
+       uint8_t family;
+       int ret;
+
+       data = ipset_session_data(session);
+       assert(data);
+       setname = ipset_data_setname(data);
+       assert(setname);
+
+       /* Check existing sets in cache */
+       for (s = setlist; s != NULL; s = s->next) {
+               if (STREQ(setname, s->name)) {
+                       match = s->type;
+                       goto found;
+               }
+       }
+
+       /* Check kernel */
+       ret = ipset_cmd(session, IPSET_CMD_HEADER, 0);
+       if (ret != 0)
+               return NULL;
+
+       typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
+       revision = ipset_data_get(data, IPSET_OPT_REVISION);    
+       family = ipset_data_family(data);
+
+       /* Check registered types */
+       for (t = typelist, match = NULL;
+            t != NULL && match == NULL; t = t->next) {
+               if (t->kernel_check == IPSET_KERNEL_MISMATCH)
+                       continue;
+               if (STREQ(typename, t->name)
+                   && MATCH_FAMILY(t, family)
+                   && *revision == t->revision) {
+                       t->kernel_check = IPSET_KERNEL_OK;
+                       match = t;
+               }
+       }
+       if (!match)
+               return ipset_errptr(session,
+                                   "Kernel-library incompatibility: "
+                                   "set %s in kernel has got settype %s "
+                                   "with family %s and revision %u while "
+                                   "ipset library does not support the "
+                                   "settype with that family and revision.",
+                                   setname, typename,
+                                   family == AF_INET ? "inet" :
+                                   family == AF_INET6 ? "inet6" : "unspec",
+                                   *revision);
+
+found:
+       set_family_and_type(data, match, family);
+
+       return match;
+}
+
+/**
+ * ipset_type_get - get a set type from the kernel
+ * @session: session structure
+ * @cmd: the command which needs the set type
+ *
+ * Build up and send a private message to the kernel in order to
+ * get the set type. When creating the set, we send the typename
+ * and family and get the supported revisions of the given set type.
+ * When adding/deleting/testing an entry, we send the setname and
+ * receive the typename, family and revision.
+ *
+ * Returns the set type for success and NULL for failure.
+ */
+const struct ipset_type *
+ipset_type_get(struct ipset_session *session, enum ipset_cmd cmd)
+{
+       assert(session);
+
+       switch (cmd) {
+       case IPSET_CMD_CREATE:
+               return create_type_get(session);
+       case IPSET_CMD_ADD:
+       case IPSET_CMD_DEL:
+       case IPSET_CMD_TEST:
+               return adt_type_get(session);
+       default:
+               break;
+       }
+
+       assert(cmd == IPSET_CMD_NONE);
+       return NULL;
+}
+
+/**
+ * ipset_type_check - check the set type received from kernel
+ * @session: session structure
+ *
+ * Check the set type received from the kernel (typename, revision,
+ * family) against the userspace types looking for a matching type.
+ *
+ * Returns the set type for success and NULL for failure.
+ */
+const struct ipset_type *
+ipset_type_check(struct ipset_session *session)
+{
+       struct ipset_type *t, *match = NULL;
+       struct ipset_data *data;
+       const char *typename;
+       uint8_t family, revision;
+
+       assert(session);
+       data = ipset_session_data(session);
+       assert(data);
+
+       typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
+       family = ipset_data_family(data);
+       revision = *(uint8_t *) ipset_data_get(data, IPSET_OPT_REVISION);
+
+       /* Check registered types */
+       for (t = typelist; t != NULL && match == NULL; t = t->next) {
+               if (t->kernel_check == IPSET_KERNEL_MISMATCH)
+                       continue;
+               if ((STREQ(typename, t->name) || STREQ(typename, t->alias))
+                   && MATCH_FAMILY(t, family) && t->revision == revision)
+                       match = t;
+       }
+       if (!match)
+               return ipset_errptr(session,
+                            "Kernel and userspace incompatible: "
+                            "settype %s with revision %u not supported ",
+                            "by userspace.", typename, revision);
+
+       set_family_and_type(data, match, family);
+
+       return match;
+}
+
+static void
+type_max_size(struct ipset_type *type, uint8_t family)
+{
+       int sizeid;
+       enum ipset_opt opt;
+       size_t max = 0;
+
+       sizeid = family == AF_INET ? IPSET_MAXSIZE_INET : IPSET_MAXSIZE_INET6;
+       for (opt = IPSET_OPT_NONE + 1; opt < IPSET_OPT_MAX; opt++) {
+               if (!(IPSET_FLAG(opt) & IPSET_ADT_FLAGS))
+                       continue;
+               if (!(IPSET_FLAG(opt) & type->full[IPSET_ADD]))
+                       continue;
+               max += ipset_data_sizeof(opt, family);
+       }
+       type->maxsize[sizeid] = max;
+}
+
+/**
+ * ipset_type_add - add (register) a userspace set type
+ * @type: set type structure
+ *
+ * Add the given set type to the type list. The types
+ * are added sorted, in descending revision number.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_type_add(struct ipset_type *type)
+{
+       struct ipset_type *t, *prev;
+
+       assert(type);
+
+       /* Fill out max sizes */
+       switch (type->family) {
+       case AF_UNSPEC:
+       case AF_INET:
+               type_max_size(type, AF_INET);
+               break;
+       case AF_INET6:
+               type_max_size(type, AF_INET6);
+               break;
+       case AF_INET46:
+               type_max_size(type, AF_INET);
+               type_max_size(type, AF_INET6);
+               break;
+       default:
+               return -1;
+       }
+
+       /* Add to the list: higher revision numbers first */
+       for (t = typelist, prev = NULL; t != NULL; t = t->next) {
+               if (STREQ(t->name, type->name)) {
+                       if (t->revision == type->revision) {
+                               errno = EEXIST;
+                               return -1;
+                       } else if (t->revision < type->revision) {
+                               type->next = t;
+                               if (prev)
+                                       prev->next = type;
+                               else
+                                       typelist = type;
+                               return 0;
+                       }
+               }
+               if (t->next != NULL && STREQ(t->next->name, type->name)) {
+                       if (t->next->revision == type->revision) {
+                               errno = EEXIST;
+                               return -1;
+                       } else if (t->next->revision < type->revision) {
+                               type->next = t->next;
+                               t->next = type;
+                               return 0;
+                       }
+               }
+               prev = t;
+       }
+       type->next = typelist;
+       typelist = type;
+       return 0;
+}
+
+/**
+ * ipset_typename_resolve - resolve typename alias
+ * @str: typename or alias
+ *
+ * Check the typenames (and aliases) and return the
+ * preferred name of the set type.
+ *
+ * Returns the name of the matching set type or NULL.
+ */
+const char *
+ipset_typename_resolve(const char *str)
+{
+       const struct ipset_type *t;
+
+       for (t = typelist; t != NULL; t = t->next)
+               if (STREQ(str, t->name) || STREQ(str, t->alias))
+                       return t->name;
+       return NULL;
+}
+
+/**
+ * ipset_types - return the list of the set types
+ *
+ * The types can be unchecked with respect of the running kernel.
+ * Only useful for type specific help.
+ *
+ * Returns the list of the set types.
+ */
+const struct ipset_type *
+ipset_types(void)
+{
+       return typelist;
+}
+
+/**
+ * ipset_types_init - initialize known set types
+ *
+ * Initialize the type list with the known, supported set types.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_types_init(void)
+{
+       if (typelist != NULL)
+               return 0;
+
+       ipset_type_add(&ipset_bitmap_ip0);
+       ipset_type_add(&ipset_bitmap_ipmac0);
+       ipset_type_add(&ipset_bitmap_port0);
+       ipset_type_add(&ipset_hash_ip0);
+       ipset_type_add(&ipset_hash_net0);
+       ipset_type_add(&ipset_hash_ipport0);
+       ipset_type_add(&ipset_hash_ipportip0);
+       ipset_type_add(&ipset_hash_ipportnet0);
+       ipset_type_add(&ipset_tree_ip0);
+       ipset_type_add(&ipset_list_set0);
+       return 0;
+}
+
+/**
+ * ipset_types_fini - release initialized known set types
+ *
+ * Release initialized known set types and remove the set cache.
+ */
+void
+ipset_types_fini(void)
+{
+       struct ipset *set;
+       
+       while (setlist) {
+               set = setlist;
+               setlist = setlist->next;
+               free(set);
+       }
+}
diff --git a/lib/utils.c b/lib/utils.c
new file mode 100644 (file)
index 0000000..bddeb87
--- /dev/null
@@ -0,0 +1,103 @@
+/* Copyright 2007-20010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <stdbool.h>                           /* bool */
+#include <stdlib.h>                            /* malloc, free */
+#include <string.h>                            /* memset, str* */
+
+#include <libipset/session.h>                  /* ipset_err */
+#include <libipset/utils.h>                    /* prototypes */
+
+/**
+ * ipset_strchr - locate character(s) in string
+ * @str: string to locate the character(s) in
+ * @sep: string of characters to locate
+ *
+ * Return a pointer to the first occurence of any of the
+ * characters to be located in the string. NULL is returned
+ * if no character is found.
+ */
+char *
+ipset_strchr(const char *str, const char *sep)
+{
+       char *match;
+       
+       assert(str);
+       assert(sep);
+       
+       for (; *sep != '\0'; sep++)
+               if ((match = strchr(str, (int)sep[0])) != NULL)
+                       return match;
+       
+       return NULL;
+}
+
+/**
+ * ipset_name_match - match a string against an array of strings
+ * @arg: string
+ * @name: array of strings, last one is a NULL pointer
+ *
+ * Return true if arg matches any of the strings in the array.
+ */
+bool
+ipset_name_match(const char *arg, const char * const name[])
+{
+       int i = 0;
+       
+       assert(arg);
+       assert(name);
+       
+       while (name[i]) {
+               if (STREQ(arg, name[i]))
+                       return true;
+               i++;
+       }
+       
+       return false;
+}
+
+/**
+ * ipset_shift_argv - shift off an argument
+ * @arc: argument count
+ * @argv: array of argument strings
+ * @from: from where shift off an argument
+ *
+ * Shift off the argument at "from" from the array of
+ * arguments argv of size argc.
+ */
+void
+ipset_shift_argv(int *argc, char *argv[], int from)
+{
+       int i;
+       
+       assert(*argc >= from + 1);
+
+       for (i = from + 1; i <= *argc; i++) {
+               argv[i-1] = argv[i];
+       }
+       (*argc)--;
+       return;
+}
+
+/**
+ * ipset_strncpy - copy the string from src to dst
+ * @dst: the target string buffer
+ * @src: the source string buffer
+ * @len: the length of bytes to copy, including the terminating null byte.
+ *
+ * Copy the string from src to destination, but at most len bytes are
+ * copied. The target is unconditionally terminated by the null byte.
+ */
+void
+ipset_strncpy(char *dst, const char *src, size_t len)
+{
+       assert(dst);
+       assert(src);
+
+       strncpy(dst, src, len);
+       dst[len - 1] = '\0';
+}
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..28b28fa
--- /dev/null
@@ -0,0 +1,21 @@
+include $(top_srcdir)/Make_global.am
+
+sbin_PROGRAMS  = ipset
+ipset_SOURCES  = ipset.c \
+       errcode.c \
+       ipset_bitmap_ip.c \
+       ipset_bitmap_ipmac.c \
+       ipset_bitmap_port.c \
+       ipset_hash_ip.c \
+       ipset_hash_ipport.c \
+       ipset_hash_ipportip.c \
+       ipset_hash_ipportnet.c \
+       ipset_hash_net.c \
+       ipset_list_set.c \
+       ipset_tree_ip.c \
+       ui.c
+ipset_LDADD    = ../lib/libipset.la
+AM_LDFLAGS     = -static
+
+#%.o: %.c
+#      ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} ${CFLAGS} -o $@ -c $<
diff --git a/src/errcode.c b/src/errcode.c
new file mode 100644 (file)
index 0000000..34b87a3
--- /dev/null
@@ -0,0 +1,158 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <assert.h>                            /* assert */
+#include <errno.h>                             /* errno */
+#include <string.h>                            /* strerror */
+
+#include <libipset/data.h>                     /* ipset_data_get */
+#include <libipset/session.h>                  /* ipset_err */
+#include <libipset/types.h>                    /* struct ipset_type */
+#include <libipset/utils.h>                    /* STRNEQ */
+#include <libipset/errcode.h>                  /* prototypes */
+#include <libipset/linux_ip_set_bitmap.h>      /* bitmap specific errcodes */
+#include <libipset/linux_ip_set_hash.h>                /* hash specific errcodes */
+
+/* Core kernel error codes */
+static const struct ipset_errcode_table core_errcode_table[] = {
+       /* Generic error codes */
+       { EEXIST, 0,
+         "The set with the given name does not exist" },
+       { IPSET_ERR_PROTOCOL,  0,
+         "Kernel error received: ipset protocol error" },
+
+       /* CREATE specific error codes */
+       { EEXIST, IPSET_CMD_CREATE,
+         "Set cannot be created: set with the same name already exists" },
+       { IPSET_ERR_FIND_TYPE, 0,
+         "Kernel error received: set type does not supported" },
+       { IPSET_ERR_MAX_SETS, 0,
+         "Kernel error received: maximal number of sets reached, cannot create more." },
+       { IPSET_ERR_INVALID_NETMASK, 0,
+         "The value of the netmask parameter is invalid" },
+       { IPSET_ERR_INVALID_FAMILY, 0,
+         "The protocol family not supported by the set type" },
+
+       /* DESTROY specific error codes */
+       { IPSET_ERR_BUSY, IPSET_CMD_DESTROY,
+         "Set cannot be destroyed: it is in use by a kernel component" },
+
+       /* FLUSH specific error codes */
+
+       /* RENAME specific error codes */
+       { IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_RENAME,
+         "Set cannot be renamed: a set with the new name already exists" },
+
+       /* SWAP specific error codes */
+       { IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_SWAP,
+         "Sets cannot be swapped: the second set does not exist" },
+       { IPSET_ERR_TYPE_MISMATCH, IPSET_CMD_SWAP,
+         "The sets cannot be swapped: they type does not match" },
+
+       /* LIST/SAVE specific error codes */
+
+       /* Generic (CADT) error codes */
+       { IPSET_ERR_INVALID_CIDR, 0,
+         "The value of the CIDR parameter of the IP address is invalid" },
+       { IPSET_ERR_TIMEOUT, 0,
+         "Timeout cannot be used: set was created without timeout support" },
+         
+       /* ADD specific error codes */
+       { IPSET_ERR_EXIST, IPSET_CMD_ADD,
+         "Element cannot be added to the set: it's already added" },
+
+       /* DEL specific error codes */
+       { IPSET_ERR_EXIST, IPSET_CMD_DEL,
+         "Element cannot be deleted from the set: it's not added" },
+
+       /* TEST specific error codes */
+
+       /* HEADER specific error codes */
+
+       /* TYPE specific error codes */
+       { EEXIST, IPSET_CMD_TYPE,
+         "Kernel error received: set type does not supported" },
+
+       /* PROTOCOL specific error codes */
+
+       { },
+};
+
+/* Bitmap type-specific error codes */
+static const struct ipset_errcode_table bitmap_errcode_table[] = {
+       /* Generic (CADT) error codes */
+       { IPSET_ERR_BITMAP_RANGE, 0,
+         "Element is out of the range of the set" },
+       { IPSET_ERR_BITMAP_RANGE_SIZE, IPSET_CMD_CREATE,
+         "The range you specified exceeds the size limit of the set type" },
+       { },
+};
+
+/* Hash type-specific error codes */
+static const struct ipset_errcode_table hash_errcode_table[] = {
+       /* Generic (CADT) error codes */
+       { IPSET_ERR_HASH_FULL, 0,
+         "Hash is full, cannot add more elements" },
+       { IPSET_ERR_HASH_ELEM, 0,
+         "Null-valued element, cannot be stored in a hash type of set" },
+       { },
+};
+
+#define MATCH_TYPENAME(a, b)   STRNEQ(a, b, strlen(b))
+
+/**
+ * ipset_errcode - interpret an error code
+ * @session: session structure
+ * @errcode: errcode
+ *
+ * Find the error code and print the appropriate
+ * error message.
+ *
+ * Returns -1.
+ */
+int
+ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode)
+{
+       const struct ipset_errcode_table *table = core_errcode_table;
+       int i, generic;
+       
+       if (errcode >= IPSET_ERR_TYPE_SPECIFIC) {
+               const struct ipset_type *type;
+               
+               type = ipset_session_data_get(session, IPSET_OPT_TYPE);
+               if (type) {
+                       if (MATCH_TYPENAME(type->name, "bitmap:"))
+                               table = bitmap_errcode_table;
+                       if (MATCH_TYPENAME(type->name, "hash:"))
+                               table = hash_errcode_table;
+               }
+       }
+
+retry:
+       for (i = 0, generic = -1; table[i].errcode; i++) {
+               if (table[i].errcode == errcode
+                   && (table[i].cmd == cmd || table[i].cmd == 0)) {
+                       if (table[i].cmd == 0) {
+                               generic = i;
+                               continue;
+                       }
+                       return ipset_err(session, table[i].message);
+               }
+       }
+       if (generic != -1)
+               return ipset_err(session, table[generic].message);
+       /* Fall back to the core table */
+       if (table != core_errcode_table) {
+               table = core_errcode_table;
+               goto retry;
+       }
+       if (errcode < IPSET_ERR_PRIVATE)
+               return ipset_err(session, "Kernel error received: %s",
+                                strerror(errcode));
+       else
+               return ipset_err(session,
+                                "Undecoded error %u received from kernel", errcode);
+}
diff --git a/src/ui.c b/src/ui.c
new file mode 100644 (file)
index 0000000..bc01e61
--- /dev/null
+++ b/src/ui.c
@@ -0,0 +1,151 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+#include <libipset/linux_ip_set.h>             /* IPSET_CMD_* */
+#include <libipset/types.h>                    /* IPSET_*_ARG */
+#include <libipset/session.h>                  /* ipset_envopt_parse */
+#include <libipset/parse.h>                    /* ipset_parse_family */
+#include <libipset/print.h>                    /* ipset_print_family */
+#include <libipset/ui.h>                       /* prototypes */
+
+/* Commands and environment options */
+
+const struct ipset_commands ipset_commands[] = {
+       [IPSET_CMD_CREATE - 1] = {
+               .name = { "create", "c",  "-N", "--create", NULL },
+               .has_arg = IPSET_MANDATORY_ARG2,
+               .help = "SETNAME TYPENAME [type-specific-options]\n"
+                       "        Create a new set",
+       },
+       [IPSET_CMD_DESTROY - 1] = {
+               .name = { "destroy", "x", "-X", "--destroy", NULL },
+               .has_arg = IPSET_OPTIONAL_ARG,
+               .help = "[SETNAME]\n"
+                       "        Destroy a named set or all sets",
+       },
+       [IPSET_CMD_FLUSH - 1] = {
+               .name = { "flush", "f",   "-F", "--flush", NULL },
+               .has_arg = IPSET_OPTIONAL_ARG,
+               .help = "[SETNAME]\n"
+                       "        Flush a named set or all sets",
+       },
+       [IPSET_CMD_RENAME - 1] = {
+               .name = { "rename", "e",  "-E", "--rename", NULL },
+               .has_arg = IPSET_MANDATORY_ARG2,
+               .help = "FROM-SETNAME TO-SETNAME\n"
+                       "        Rename two sets",
+       },
+       [IPSET_CMD_SWAP - 1] = {
+               .name = { "swap", "w",    "-W", "--swap", NULL },
+               .has_arg = IPSET_MANDATORY_ARG2,
+               .help = "FROM-SETNAME TO-SETNAME\n"
+                       "        Swap the contect of two existing sets",
+       },
+       [IPSET_CMD_LIST - 1] = {
+               .name = { "list", "l",    "-L", "--list", NULL },
+               .has_arg = IPSET_OPTIONAL_ARG,
+               .help = "[SETNAME]\n"
+                       "        List the entries of a named set or all sets",
+       },
+       [IPSET_CMD_SAVE - 1] = {
+               .name = { "save", "s",    "-S", "--save", NULL },
+               .has_arg = IPSET_OPTIONAL_ARG,
+               .help = "[SETNAME]\n"
+                       "        Save the named set or all sets to stdout",
+       },
+       [IPSET_CMD_ADD - 1] = {
+               .name = { "add", "a",     "-A", "--add", NULL },
+               .has_arg = IPSET_MANDATORY_ARG2,
+               .help = "SETNAME ENTRY\n"
+                       "        Add entry to a named set",
+       },
+       [IPSET_CMD_DEL - 1] = {
+               .name = { "del", "d",     "-D", "--del", NULL },
+               .has_arg = IPSET_MANDATORY_ARG2,
+               .help = "SETNAME ENTRY\n"
+                       "        Delete entry from a named set",
+       },
+       [IPSET_CMD_TEST - 1] = {
+               .name = { "test", "t",    "-T", "--test", NULL },
+               .has_arg = IPSET_MANDATORY_ARG2,
+               .help = "SETNAME ENTRY\n"
+                       "        Test if entry exists in the named set",
+       },
+       [IPSET_CMD_HELP - 1] = {
+               .name = { "help", "h",    "-H", "-h", "--help", NULL },
+               .has_arg = IPSET_OPTIONAL_ARG,
+               .help = "[TYPENAME]\n"
+                       "        Print help, and settype specific help",
+       },
+       [IPSET_CMD_RESTORE - 1] = {
+               .name = { "restore", "r", "-R", "--restore", NULL },
+               .has_arg = IPSET_NO_ARG,
+               .help = "\n"
+                       "        Restore a saved state",
+       },
+       [IPSET_CMD_VERSION - 1] = {
+               .name = { "version", "v", "-V", "-v", "--version", NULL },
+               .has_arg = IPSET_NO_ARG,
+               .help = "\n"
+                       "        Print version information",
+       },
+       [IPSET_CMD_MAX - 1] = { },
+};
+
+const struct ipset_envopts ipset_envopts[] = {
+       { .name = { "family", "--family", NULL },
+         .has_arg = IPSET_MANDATORY_ARG,       .flag = IPSET_OPT_FAMILY,
+         .parse = ipset_parse_family,          .print = ipset_print_family,
+         .help = "inet|inet6\n"
+                 "       Specify family when creating a set\n"
+                 "       which supports multiple families.\n"
+                 "       The default family is INET.",
+       },
+       { .name = { "-o", "--output", NULL },
+         .has_arg = IPSET_MANDATORY_ARG,       .flag = IPSET_OPT_MAX,
+         .parse = ipset_parse_output,
+         .help = "plain|save|xml\n"
+                 "       Specify output mode for listing sets.\n"
+                 "       Default value for \"list\" command is mode \"plain\"\n"
+                 "       and for \"save\" command is mode \"save\".",
+       },
+       { .name = { "-s", "--sorted", NULL },
+         .parse = ipset_envopt_parse,
+         .has_arg = IPSET_NO_ARG,      .flag = IPSET_ENV_SORTED,
+         .help = "\n"
+                 "        Print elements sorted (if supported by the set type).",
+       },
+       { .name = { "-q", "--quiet", NULL },
+         .parse = ipset_envopt_parse,
+         .has_arg = IPSET_NO_ARG,      .flag = IPSET_ENV_QUIET,
+         .help = "\n"
+                 "        Suppress any notice or warning message.",
+       },
+       { .name = { "-r", "--resolve", NULL },
+         .parse = ipset_envopt_parse,
+         .has_arg = IPSET_NO_ARG,      .flag = IPSET_ENV_RESOLVE,
+         .help = "\n"
+                 "        Try to resolve IP addresses in the output (slow!)",
+       },
+       { .name = { "-x", "--exist", NULL },
+         .parse = ipset_envopt_parse,
+         .has_arg = IPSET_NO_ARG,      .flag = IPSET_ENV_EXIST,
+         .help = "\n"
+                 "        Ignore errors when creating already created sets,\n"
+                 "        when adding already existing elements\n"
+                 "        or when deleting non-existing elements.",
+       },
+       /* Aliases */
+       { .name = { "-4", NULL },
+         .has_arg = IPSET_NO_ARG,              .flag = IPSET_OPT_FAMILY,
+         .parse = ipset_parse_family,
+       },
+       { .name = { "-6", NULL },
+         .has_arg = IPSET_NO_ARG,              .flag = IPSET_OPT_FAMILY,
+         .parse = ipset_parse_family,
+       },
+       { },
+};