summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/errcode.c2
-rw-r--r--lib/ipset.c588
-rw-r--r--lib/libipset.map5
-rw-r--r--lib/parse.c77
4 files changed, 629 insertions, 43 deletions
diff --git a/lib/errcode.c b/lib/errcode.c
index b38f95e..76bab74 100644
--- a/lib/errcode.c
+++ b/lib/errcode.c
@@ -25,6 +25,8 @@ static const struct ipset_errcode_table core_errcode_table[] = {
"The set with the given name does not exist" },
{ EMSGSIZE, 0,
"Kernel error received: message could not be created" },
+ { ERANGE, 0,
+ "The specified range is too large, split it up into smaller ranges" },
{ IPSET_ERR_PROTOCOL, 0,
"Kernel error received: ipset protocol error" },
diff --git a/lib/ipset.c b/lib/ipset.c
index 6729919..73e67db 100644
--- a/lib/ipset.c
+++ b/lib/ipset.c
@@ -13,6 +13,7 @@
#include <stdio.h> /* printf */
#include <stdlib.h> /* exit */
#include <string.h> /* str* */
+#include <inttypes.h> /* PRIu64 */
#include <config.h>
@@ -28,6 +29,7 @@
#include <libipset/utils.h> /* STREQ */
#include <libipset/ipset.h> /* prototypes */
#include <libipset/ip_set_compiler.h> /* compiler attributes */
+#include <libipset/list_sort.h> /* lists */
static char program_name[] = PACKAGE;
static char program_version[] = PACKAGE_VERSION;
@@ -50,6 +52,17 @@ struct ipset {
char *newargv[MAX_ARGS];
int newargc;
const char *filename; /* Input/output filename */
+ bool xlate;
+ struct list_head xlate_sets;
+};
+
+struct ipset_xlate_set {
+ struct list_head list;
+ char name[IPSET_MAXNAMELEN];
+ uint8_t netmask;
+ uint8_t family;
+ bool interval;
+ const struct ipset_type *type;
};
/* Commands and environment options */
@@ -923,20 +936,33 @@ static const char *cmd_prefix[] = {
[IPSET_TEST] = "test SETNAME",
};
-/* Workhorses */
+static const struct ipset_xlate_set *
+ipset_xlate_set_get(struct ipset *ipset, const char *name)
+{
+ const struct ipset_xlate_set *set;
-/**
- * ipset_parse_argv - parse and argv array and execute the command
- * @ipset: ipset structure
- * @argc: length of the array
- * @argv: array of strings
- *
- * Parse an array of strings and execute the ipset command.
- *
- * Returns 0 on success or a negative error code.
- */
-int
-ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
+ list_for_each_entry(set, &ipset->xlate_sets, list) {
+ if (!strcmp(set->name, name))
+ return set;
+ }
+
+ return NULL;
+}
+
+static const struct ipset_type *ipset_xlate_type_get(struct ipset *ipset,
+ const char *name)
+{
+ const struct ipset_xlate_set *set;
+
+ set = ipset_xlate_set_get(ipset, name);
+ if (!set)
+ return NULL;
+
+ return set->type;
+}
+
+static int
+ipset_parser(struct ipset *ipset, int oargc, char *oargv[])
{
int ret = 0;
enum ipset_cmd cmd = IPSET_CMD_NONE;
@@ -1243,7 +1269,7 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
return ipset->custom_error(ipset,
p, IPSET_PARAMETER_PROBLEM,
"Unknown argument %s", argv[1]);
- return restore(ipset);
+ return IPSET_CMD_RESTORE;
case IPSET_CMD_ADD:
case IPSET_CMD_DEL:
case IPSET_CMD_TEST:
@@ -1253,7 +1279,12 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
if (ret < 0)
return ipset->standard_error(ipset, p);
- type = ipset_type_get(session, cmd);
+ if (!ipset->xlate) {
+ type = ipset_type_get(session, cmd);
+ } else {
+ type = ipset_xlate_type_get(ipset, arg0);
+ ipset_session_data_set(session, IPSET_OPT_TYPE, type);
+ }
if (type == NULL)
return ipset->standard_error(ipset, p);
@@ -1280,6 +1311,37 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
if (argc > 1)
return ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM,
"Unknown argument %s", argv[1]);
+
+ return cmd;
+}
+
+/* Workhorses */
+
+/**
+ * ipset_parse_argv - parse and argv array and execute the command
+ * @ipset: ipset structure
+ * @argc: length of the array
+ * @argv: array of strings
+ *
+ * Parse an array of strings and execute the ipset command.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
+{
+ struct ipset_session *session = ipset->session;
+ void *p = ipset_session_printf_private(session);
+ enum ipset_cmd cmd;
+ int ret;
+
+ cmd = ipset_parser(ipset, oargc, oargv);
+ if (cmd < 0)
+ return cmd;
+
+ if (cmd == IPSET_CMD_RESTORE)
+ return restore(ipset);
+
ret = ipset_cmd(session, cmd, ipset->restore_line);
D("ret %d", ret);
/* In the case of warning, the return code is success */
@@ -1455,6 +1517,9 @@ ipset_init(void)
return NULL;
}
ipset_custom_printf(ipset, NULL, NULL, NULL, NULL);
+
+ INIT_LIST_HEAD(&ipset->xlate_sets);
+
return ipset;
}
@@ -1469,6 +1534,8 @@ ipset_init(void)
int
ipset_fini(struct ipset *ipset)
{
+ struct ipset_xlate_set *xlate_set, *next;
+
assert(ipset);
if (ipset->session)
@@ -1477,6 +1544,497 @@ ipset_fini(struct ipset *ipset)
if (ipset->newargv[0])
free(ipset->newargv[0]);
+ list_for_each_entry_safe(xlate_set, next, &ipset->xlate_sets, list)
+ free(xlate_set);
+
free(ipset);
return 0;
}
+
+/* Ignore the set family, use inet. */
+static const char *ipset_xlate_family(uint8_t family)
+{
+ return "inet";
+}
+
+enum ipset_xlate_set_type {
+ IPSET_XLATE_TYPE_UNKNOWN = 0,
+ IPSET_XLATE_TYPE_HASH_MAC,
+ IPSET_XLATE_TYPE_HASH_IP,
+ IPSET_XLATE_TYPE_HASH_IP_MAC,
+ IPSET_XLATE_TYPE_HASH_NET_IFACE,
+ IPSET_XLATE_TYPE_HASH_NET_PORT,
+ IPSET_XLATE_TYPE_HASH_NET_PORT_NET,
+ IPSET_XLATE_TYPE_HASH_NET_NET,
+ IPSET_XLATE_TYPE_HASH_NET,
+ IPSET_XLATE_TYPE_HASH_IP_PORT_NET,
+ IPSET_XLATE_TYPE_HASH_IP_PORT_IP,
+ IPSET_XLATE_TYPE_HASH_IP_MARK,
+ IPSET_XLATE_TYPE_HASH_IP_PORT,
+ IPSET_XLATE_TYPE_BITMAP_PORT,
+ IPSET_XLATE_TYPE_BITMAP_IP_MAC,
+ IPSET_XLATE_TYPE_BITMAP_IP,
+};
+
+static enum ipset_xlate_set_type ipset_xlate_set_type(const char *typename)
+{
+ if (!strcmp(typename, "hash:mac"))
+ return IPSET_XLATE_TYPE_HASH_MAC;
+ else if (!strcmp(typename, "hash:ip"))
+ return IPSET_XLATE_TYPE_HASH_IP;
+ else if (!strcmp(typename, "hash:ip,mac"))
+ return IPSET_XLATE_TYPE_HASH_IP_MAC;
+ else if (!strcmp(typename, "hash:net,iface"))
+ return IPSET_XLATE_TYPE_HASH_NET_IFACE;
+ else if (!strcmp(typename, "hash:net,port"))
+ return IPSET_XLATE_TYPE_HASH_NET_PORT;
+ else if (!strcmp(typename, "hash:net,port,net"))
+ return IPSET_XLATE_TYPE_HASH_NET_PORT_NET;
+ else if (!strcmp(typename, "hash:net,net"))
+ return IPSET_XLATE_TYPE_HASH_NET_NET;
+ else if (!strcmp(typename, "hash:net"))
+ return IPSET_XLATE_TYPE_HASH_NET;
+ else if (!strcmp(typename, "hash:ip,port,net"))
+ return IPSET_XLATE_TYPE_HASH_IP_PORT_NET;
+ else if (!strcmp(typename, "hash:ip,port,ip"))
+ return IPSET_XLATE_TYPE_HASH_IP_PORT_IP;
+ else if (!strcmp(typename, "hash:ip,mark"))
+ return IPSET_XLATE_TYPE_HASH_IP_MARK;
+ else if (!strcmp(typename, "hash:ip,port"))
+ return IPSET_XLATE_TYPE_HASH_IP_PORT;
+ else if (!strcmp(typename, "hash:ip"))
+ return IPSET_XLATE_TYPE_HASH_IP;
+ else if (!strcmp(typename, "bitmap:port"))
+ return IPSET_XLATE_TYPE_BITMAP_PORT;
+ else if (!strcmp(typename, "bitmap:ip,mac"))
+ return IPSET_XLATE_TYPE_BITMAP_IP_MAC;
+ else if (!strcmp(typename, "bitmap:ip"))
+ return IPSET_XLATE_TYPE_BITMAP_IP;
+
+ return IPSET_XLATE_TYPE_UNKNOWN;
+}
+
+#define NFT_SET_INTERVAL (1 << 0)
+
+static const char *
+ipset_xlate_type_to_nftables(int family, enum ipset_xlate_set_type type,
+ uint32_t *flags)
+{
+ switch (type) {
+ case IPSET_XLATE_TYPE_HASH_MAC:
+ return "ether_addr";
+ case IPSET_XLATE_TYPE_HASH_IP:
+ if (family == AF_INET)
+ return "ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_MAC:
+ if (family == AF_INET)
+ return "ipv4_addr . ether_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ether_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_IFACE:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . ifname";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ifname";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_PORT:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_PORT_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_PORT_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_PORT_IP:
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_MARK:
+ if (family == AF_INET)
+ return "ipv4_addr . mark";
+ else if (family == AF_INET6)
+ return "ipv6_addr . mark";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_PORT:
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service";
+ break;
+ case IPSET_XLATE_TYPE_BITMAP_PORT:
+ return "inet_service";
+ case IPSET_XLATE_TYPE_BITMAP_IP_MAC:
+ if (family == AF_INET)
+ return "ipv4_addr . ether_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ether_addr";
+ break;
+ case IPSET_XLATE_TYPE_BITMAP_IP:
+ if (family == AF_INET)
+ return "ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr";
+ break;
+ }
+ /* This should not ever happen. */
+ return "unknown";
+}
+
+static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd,
+ const char *table)
+{
+ const char *set, *typename, *nft_type;
+ const struct ipset_type *ipset_type;
+ struct ipset_xlate_set *xlate_set;
+ enum ipset_xlate_set_type type;
+ struct ipset_session *session;
+ const uint32_t *cadt_flags;
+ const uint32_t *timeout;
+ const uint32_t *maxelem;
+ struct ipset_data *data;
+ const uint8_t *netmask;
+ const char *comment;
+ uint32_t flags = 0;
+ uint8_t family;
+ char buf[64];
+ bool concat;
+ char *term;
+ int i;
+
+ session = ipset_session(ipset);
+ data = ipset_session_data(session);
+
+ set = ipset_data_get(data, IPSET_SETNAME);
+ family = ipset_data_family(data);
+
+ switch (cmd) {
+ case IPSET_CMD_CREATE:
+ /* Not supported. */
+ if (ipset_data_test(data, IPSET_OPT_MARKMASK)) {
+ printf("# %s", ipset->cmdline);
+ break;
+ }
+ cadt_flags = ipset_data_get(data, IPSET_OPT_CADT_FLAGS);
+
+ /* Ignore:
+ * - IPSET_FLAG_WITH_COMMENT
+ * - IPSET_FLAG_WITH_FORCEADD
+ */
+ if (cadt_flags &&
+ (*cadt_flags & (IPSET_FLAG_BEFORE |
+ IPSET_FLAG_PHYSDEV |
+ IPSET_FLAG_NOMATCH |
+ IPSET_FLAG_WITH_SKBINFO |
+ IPSET_FLAG_IFACE_WILDCARD))) {
+ printf("# %s", ipset->cmdline);
+ break;
+ }
+
+ typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
+ type = ipset_xlate_set_type(typename);
+ nft_type = ipset_xlate_type_to_nftables(family, type, &flags);
+
+ printf("add set %s %s %s { type %s; ",
+ ipset_xlate_family(family), table, set, nft_type);
+ if (cadt_flags) {
+ if (*cadt_flags & IPSET_FLAG_WITH_COUNTERS)
+ printf("counter; ");
+ }
+ timeout = ipset_data_get(data, IPSET_OPT_TIMEOUT);
+ if (timeout)
+ printf("timeout %us; ", *timeout);
+ maxelem = ipset_data_get(data, IPSET_OPT_MAXELEM);
+ if (maxelem)
+ printf("size %u; ", *maxelem);
+
+ netmask = ipset_data_get(data, IPSET_OPT_NETMASK);
+ if (netmask &&
+ ((family == AF_INET && *netmask < 32) ||
+ (family == AF_INET6 && *netmask < 128)))
+ flags |= NFT_SET_INTERVAL;
+
+ if (flags & NFT_SET_INTERVAL)
+ printf("flags interval; ");
+
+ /* These create-specific options are safe to be ignored:
+ * - IPSET_OPT_GC
+ * - IPSET_OPT_HASHSIZE
+ * - IPSET_OPT_PROBES
+ * - IPSET_OPT_RESIZE
+ * - IPSET_OPT_SIZE
+ * - IPSET_OPT_FORCEADD
+ *
+ * Ranges and CIDR are safe to be ignored too:
+ * - IPSET_OPT_IP_FROM
+ * - IPSET_OPT_IP_TO
+ * - IPSET_OPT_PORT_FROM
+ * - IPSET_OPT_PORT_TO
+ */
+
+ printf("}\n");
+
+ xlate_set = calloc(1, sizeof(*xlate_set));
+ if (!xlate_set)
+ return -1;
+
+ snprintf(xlate_set->name, sizeof(xlate_set->name), "%s", set);
+ ipset_type = ipset_types();
+ while (ipset_type) {
+ if (!strcmp(ipset_type->name, typename))
+ break;
+ ipset_type = ipset_type->next;
+ }
+
+ xlate_set->family = family;
+ xlate_set->type = ipset_type;
+ if (netmask) {
+ xlate_set->netmask = *netmask;
+ xlate_set->interval = true;
+ }
+ list_add_tail(&xlate_set->list, &ipset->xlate_sets);
+ break;
+ case IPSET_CMD_DESTROY:
+ printf("del set %s %s %s\n",
+ ipset_xlate_family(family), table, set);
+ break;
+ case IPSET_CMD_FLUSH:
+ if (!set) {
+ printf("# %s", ipset->cmdline);
+ } else {
+ printf("flush set %s %s %s\n",
+ ipset_xlate_family(family), table, set);
+ }
+ break;
+ case IPSET_CMD_RENAME:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_SWAP:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_LIST:
+ if (!set) {
+ printf("list sets %s\n",
+ ipset_xlate_family(family), table);
+ } else {
+ printf("list set %s %s %s\n",
+ ipset_xlate_family(family), table, set);
+ }
+ break;
+ case IPSET_CMD_SAVE:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_ADD:
+ case IPSET_CMD_DEL:
+ case IPSET_CMD_TEST:
+ /* Not supported. */
+ if (ipset_data_test(data, IPSET_OPT_NOMATCH) ||
+ ipset_data_test(data, IPSET_OPT_SKBINFO) ||
+ ipset_data_test(data, IPSET_OPT_SKBMARK) ||
+ ipset_data_test(data, IPSET_OPT_SKBPRIO) ||
+ ipset_data_test(data, IPSET_OPT_SKBQUEUE) ||
+ ipset_data_test(data, IPSET_OPT_IFACE_WILDCARD)) {
+ printf("# %s", ipset->cmdline);
+ break;
+ }
+ printf("%s element %s %s %s { ",
+ cmd == IPSET_CMD_ADD ? "add" :
+ cmd == IPSET_CMD_DEL ? "delete" : "get",
+ ipset_xlate_family(family), table, set);
+
+ typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
+ type = ipset_xlate_set_type(typename);
+
+ xlate_set = (struct ipset_xlate_set *)
+ ipset_xlate_set_get(ipset, set);
+ if (xlate_set && xlate_set->interval)
+ netmask = &xlate_set->netmask;
+ else
+ netmask = NULL;
+
+ concat = false;
+ if (ipset_data_test(data, IPSET_OPT_IP)) {
+ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_IP, 0);
+ printf("%s", buf);
+ if (netmask)
+ printf("/%u ", *netmask);
+ else
+ printf(" ");
+
+ concat = true;
+ }
+ if (ipset_data_test(data, IPSET_OPT_MARK)) {
+ ipset_print_mark(buf, sizeof(buf), data, IPSET_OPT_MARK, 0);
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ if (ipset_data_test(data, IPSET_OPT_IFACE)) {
+ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_IFACE, 0);
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ if (ipset_data_test(data, IPSET_OPT_ETHER)) {
+ ipset_print_ether(buf, sizeof(buf), data, IPSET_OPT_ETHER, 0);
+ for (i = 0; i < strlen(buf); i++)
+ buf[i] = tolower(buf[i]);
+
+ printf("%s%s ", concat ? ". " : "", buf);
+ concat = true;
+ }
+ if (ipset_data_test(data, IPSET_OPT_PORT)) {
+ ipset_print_proto_port(buf, sizeof(buf), data, IPSET_OPT_PORT, 0);
+ term = strchr(buf, ':');
+ if (term) {
+ *term = '\0';
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_PORT, 0);
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ if (ipset_data_test(data, IPSET_OPT_IP2)) {
+ ipset_print_ip(buf, sizeof(buf), data, IPSET_OPT_IP2, 0);
+ printf("%s%s", concat ? ". " : "", buf);
+ if (netmask)
+ printf("/%u ", *netmask);
+ else
+ printf(" ");
+ }
+ if (ipset_data_test(data, IPSET_OPT_PACKETS) &&
+ ipset_data_test(data, IPSET_OPT_BYTES)) {
+ const uint64_t *pkts, *bytes;
+
+ pkts = ipset_data_get(data, IPSET_OPT_PACKETS);
+ bytes = ipset_data_get(data, IPSET_OPT_BYTES);
+
+ printf("counter packets %" PRIu64 " bytes %" PRIu64 " ",
+ *pkts, *bytes);
+ }
+ timeout = ipset_data_get(data, IPSET_OPT_TIMEOUT);
+ if (timeout)
+ printf("timeout %us ", *timeout);
+
+ comment = ipset_data_get(data, IPSET_OPT_ADT_COMMENT);
+ if (comment)
+ printf("comment \"%s\" ", comment);
+
+ printf("}\n");
+ break;
+ case IPSET_CMD_GET_BYNAME:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_GET_BYINDEX:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ipset_xlate_restore(struct ipset *ipset)
+{
+ struct ipset_session *session = ipset_session(ipset);
+ struct ipset_data *data = ipset_session_data(session);
+ void *p = ipset_session_printf_private(session);
+ const char *filename;
+ enum ipset_cmd cmd;
+ FILE *f = stdin;
+ int ret = 0;
+ char *c;
+
+ if (ipset->filename) {
+ f = fopen(ipset->filename, "r");
+ if (!f) {
+ fprintf(stderr, "cannot open file `%s'\n", filename);
+ return -1;
+ }
+ }
+
+ /* TODO: Allow to specify the table name other than 'global'. */
+ printf("add table inet global\n");
+
+ while (fgets(ipset->cmdline, sizeof(ipset->cmdline), f)) {
+ ipset->restore_line++;
+ c = ipset->cmdline;
+ while (isspace(c[0]))
+ c++;
+ if (c[0] == '\0' || c[0] == '#')
+ continue;
+ else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n"))
+ continue;
+
+ ret = build_argv(ipset, c);
+ if (ret < 0)
+ return ret;
+
+ cmd = ipset_parser(ipset, ipset->newargc, ipset->newargv);
+ if (cmd < 0)
+ ipset->standard_error(ipset, p);
+
+ /* TODO: Allow to specify the table name other than 'global'. */
+ ret = ipset_xlate(ipset, cmd, "global");
+ if (ret < 0)
+ break;
+
+ ipset_data_reset(data);
+ }
+
+ if (filename)
+ fclose(f);
+
+ return ret;
+}
+
+int ipset_xlate_argv(struct ipset *ipset, int argc, char *argv[])
+{
+ enum ipset_cmd cmd;
+ int ret;
+
+ ipset->xlate = true;
+
+ cmd = ipset_parser(ipset, argc, argv);
+ if (cmd < 0)
+ return cmd;
+
+ if (cmd == IPSET_CMD_RESTORE) {
+ ret = ipset_xlate_restore(ipset);
+ } else {
+ fprintf(stderr, "This command is not supported, "
+ "use `ipset-translate restore < file'\n");
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/lib/libipset.map b/lib/libipset.map
index 12d16a4..c380f9c 100644
--- a/lib/libipset.map
+++ b/lib/libipset.map
@@ -208,3 +208,8 @@ global:
ipset_print_hexnumber;
} LIBIPSET_4.9;
+LIBIPSET_4.11 {
+global:
+ ipset_xlate_argv;
+} LIBIPSET_4.10;
+
diff --git a/lib/parse.c b/lib/parse.c
index f3f2d11..974eaf8 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -41,6 +41,9 @@
#define syntax_err(fmt, args...) \
ipset_err(session, "Syntax error: " fmt , ## args)
+#define syntax_err_ll(errtype, fmt, args...) \
+ ipset_session_report(session, errtype, "Syntax error: " fmt , ## args)
+
static char *
ipset_strchr(const char *str, const char *sep)
{
@@ -87,7 +90,8 @@ 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 *ret,
+ enum ipset_err_type errtype)
{
unsigned long long number = 0;
char *end;
@@ -104,23 +108,24 @@ string_to_number_ll(struct ipset_session *session,
errno = ERANGE;
}
if (errno == ERANGE && max)
- return syntax_err("'%s' is out of range %llu-%llu",
- str, min, max);
+ return syntax_err_ll(errtype, "'%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);
+ return syntax_err_ll(errtype, "'%s' is out of range %llu-%llu",
+ str, min, ULLONG_MAX);
else
- return syntax_err("'%s' is invalid as number", str);
+ return syntax_err_ll(errtype, "'%s' is invalid as number", str);
}
static int
string_to_u8(struct ipset_session *session,
- const char *str, uint8_t *ret)
+ const char *str, uint8_t *ret,
+ enum ipset_err_type errtype)
{
int err;
unsigned long long num = 0;
- err = string_to_number_ll(session, str, 0, 255, &num);
+ err = string_to_number_ll(session, str, 0, 255, &num, errtype);
*ret = num;
return err;
@@ -130,7 +135,7 @@ static int
string_to_cidr(struct ipset_session *session,
const char *str, uint8_t min, uint8_t max, uint8_t *ret)
{
- int err = string_to_u8(session, str, ret);
+ int err = string_to_u8(session, str, ret, IPSET_ERROR);
if (!err && (*ret < min || *ret > max))
return syntax_err("'%s' is out of range %u-%u",
@@ -141,12 +146,13 @@ string_to_cidr(struct ipset_session *session,
static int
string_to_u16(struct ipset_session *session,
- const char *str, uint16_t *ret)
+ const char *str, uint16_t *ret,
+ enum ipset_err_type errtype)
{
int err;
unsigned long long num = 0;
- err = string_to_number_ll(session, str, 0, USHRT_MAX, &num);
+ err = string_to_number_ll(session, str, 0, USHRT_MAX, &num, errtype);
*ret = num;
return err;
@@ -159,7 +165,8 @@ string_to_u32(struct ipset_session *session,
int err;
unsigned long long num = 0;
- err = string_to_number_ll(session, str, 0, UINT_MAX, &num);
+ err = string_to_number_ll(session, str, 0, UINT_MAX, &num,
+ IPSET_ERROR);
*ret = num;
return err;
@@ -273,8 +280,10 @@ static int
parse_portname(struct ipset_session *session, const char *str,
uint16_t *port, const char *proto)
{
- char *saved, *tmp;
+ char *saved, *tmp, *protoname;
+ const struct protoent *protoent;
struct servent *service;
+ uint8_t protonum = 0;
saved = tmp = ipset_strdup(session, str);
if (tmp == NULL)
@@ -283,7 +292,15 @@ parse_portname(struct ipset_session *session, const char *str,
if (tmp == NULL)
goto error;
- service = getservbyname(tmp, proto);
+ protoname = (char *)proto;
+ if (string_to_u8(session, proto, &protonum, IPSET_WARNING) == 0) {
+ protoent = getprotobynumber(protonum);
+ if (protoent == NULL)
+ goto error;
+ protoname = protoent->p_name;
+ }
+
+ service = getservbyname(tmp, protoname);
if (service != NULL) {
*port = ntohs((uint16_t) service->s_port);
free(saved);
@@ -319,7 +336,7 @@ ipset_parse_port(struct ipset_session *session,
assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
assert(str);
- if (string_to_u16(session, str, &port) == 0) {
+ if (string_to_u16(session, str, &port, IPSET_WARNING) == 0) {
return ipset_session_data_set(session, opt, &port);
}
/* Error is stored as warning in session report */
@@ -469,21 +486,23 @@ ipset_parse_proto(struct ipset_session *session,
{
const struct protoent *protoent;
uint8_t proto = 0;
+ uint8_t protonum = 0;
assert(session);
assert(opt == IPSET_OPT_PROTO);
assert(str);
+ if (string_to_u8(session, str, &protonum, IPSET_WARNING) == 0)
+ return ipset_session_data_set(session, opt, &protonum);
+
+ /* No error, so reset false error messages */
+ ipset_session_report_reset(session);
protoent = getprotobyname(strcasecmp(str, "icmpv6") == 0
? "ipv6-icmp" : str);
- if (protoent == NULL) {
- uint8_t protonum = 0;
- if (string_to_u8(session, str, &protonum) ||
- (protoent = getprotobynumber(protonum)) == NULL)
- return syntax_err("cannot parse '%s' "
- "as a protocol", str);
- }
+ if (protoent == NULL)
+ return syntax_err("cannot parse '%s' "
+ "as a protocol", str);
proto = protoent->p_proto;
if (!proto)
return syntax_err("Unsupported protocol '%s'", str);
@@ -513,8 +532,8 @@ parse_icmp_typecode(struct ipset_session *session,
str, family);
}
*a++ = '\0';
- if ((err = string_to_u8(session, tmp, &type)) != 0 ||
- (err = string_to_u8(session, a, &code)) != 0)
+ if ((err = string_to_u8(session, tmp, &type, IPSET_ERROR)) != 0 ||
+ (err = string_to_u8(session, a, &code, IPSET_ERROR)) != 0)
goto error;
typecode = (type << 8) | code;
@@ -1335,7 +1354,8 @@ ipset_parse_timeout(struct ipset_session *session,
assert(opt == IPSET_OPT_TIMEOUT);
assert(str);
- err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum);
+ err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum,
+ IPSET_ERROR);
if (err == 0) {
/* Timeout is expected to be 32bits wide, so we have
to convert it here */
@@ -1579,7 +1599,8 @@ ipset_parse_uint64(struct ipset_session *session,
assert(session);
assert(str);
- err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value);
+ err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value,
+ IPSET_ERROR);
if (err)
return err;
@@ -1623,7 +1644,7 @@ ipset_parse_uint16(struct ipset_session *session,
assert(session);
assert(str);
- err = string_to_u16(session, str, &value);
+ err = string_to_u16(session, str, &value, IPSET_ERROR);
if (err == 0)
return ipset_session_data_set(session, opt, &value);
@@ -1651,7 +1672,7 @@ ipset_parse_uint8(struct ipset_session *session,
assert(session);
assert(str);
- if ((err = string_to_u8(session, str, &value)) == 0)
+ if ((err = string_to_u8(session, str, &value, IPSET_ERROR)) == 0)
return ipset_session_data_set(session, opt, &value);
return err;