diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/args.c | 8 | ||||
-rw-r--r-- | lib/data.c | 12 | ||||
-rw-r--r-- | lib/debug.c | 1 | ||||
-rw-r--r-- | lib/errcode.c | 2 | ||||
-rw-r--r-- | lib/ipset.c | 65 | ||||
-rw-r--r-- | lib/ipset_hash_ip.c | 86 | ||||
-rw-r--r-- | lib/ipset_hash_ipport.c | 108 | ||||
-rw-r--r-- | lib/ipset_hash_netnet.c | 101 | ||||
-rw-r--r-- | lib/libipset.map | 4 | ||||
-rw-r--r-- | lib/parse.c | 48 | ||||
-rw-r--r-- | lib/print.c | 22 | ||||
-rw-r--r-- | lib/session.c | 125 |
13 files changed, 526 insertions, 57 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 3a82417..a9edf95 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -46,7 +46,6 @@ EXTRA_libipset_la_SOURCES = \ EXTRA_DIST = $(IPSET_SETTYPE_LIST) libipset.map -pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libipset.pc dist_man_MANS = libipset.3 @@ -300,6 +300,14 @@ static const struct ipset_arg ipset_args[] = { .print = ipset_print_hexnumber, .help = "[initval VALUE]", }, + [IPSET_ARG_BITMASK] = { + .name = { "bitmask", NULL }, + .has_arg = IPSET_MANDATORY_ARG, + .opt = IPSET_OPT_BITMASK, + .parse = ipset_parse_bitmask, + .print = ipset_print_ip, + .help = "[bitmask bitmask]", + }, }; const struct ipset_arg * @@ -53,6 +53,7 @@ struct ipset_data { uint8_t bucketsize; uint8_t resize; uint8_t netmask; + union nf_inet_addr bitmask; uint32_t hashsize; uint32_t maxelem; uint32_t markmask; @@ -110,7 +111,7 @@ ipset_strlcpy(char *dst, const char *src, size_t len) assert(dst); assert(src); - strncpy(dst, src, len); + memcpy(dst, src, len); dst[len - 1] = '\0'; } @@ -301,6 +302,12 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value) case IPSET_OPT_NETMASK: data->create.netmask = *(const uint8_t *) value; break; + case IPSET_OPT_BITMASK: + if (!(data->family == NFPROTO_IPV4 || + data->family == NFPROTO_IPV6)) + return -1; + copy_addr(data->family, &data->create.bitmask, value); + break; case IPSET_OPT_BUCKETSIZE: data->create.bucketsize = *(const uint8_t *) value; break; @@ -508,6 +515,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt) return &data->create.markmask; case IPSET_OPT_NETMASK: return &data->create.netmask; + case IPSET_OPT_BITMASK: + return &data->create.bitmask; case IPSET_OPT_BUCKETSIZE: return &data->create.bucketsize; case IPSET_OPT_RESIZE: @@ -594,6 +603,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family) case IPSET_OPT_IP_TO: case IPSET_OPT_IP2: case IPSET_OPT_IP2_TO: + case IPSET_OPT_BITMASK: return family == NFPROTO_IPV4 ? sizeof(uint32_t) : sizeof(struct in6_addr); case IPSET_OPT_MARK: diff --git a/lib/debug.c b/lib/debug.c index bf57a41..dbc5cfb 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -40,6 +40,7 @@ static const struct ipset_attrname createattr2name[] = { [IPSET_ATTR_MAXELEM] = { .name = "MAXELEM" }, [IPSET_ATTR_MARKMASK] = { .name = "MARKMASK" }, [IPSET_ATTR_NETMASK] = { .name = "NETMASK" }, + [IPSET_ATTR_BITMASK] = { .name = "BITMASK" }, [IPSET_ATTR_BUCKETSIZE] = { .name = "BUCKETSIZE" }, [IPSET_ATTR_RESIZE] = { .name = "RESIZE" }, [IPSET_ATTR_SIZE] = { .name = "SIZE" }, diff --git a/lib/errcode.c b/lib/errcode.c index 76bab74..49c97a1 100644 --- a/lib/errcode.c +++ b/lib/errcode.c @@ -44,6 +44,8 @@ static const struct ipset_errcode_table core_errcode_table[] = { "The value of the markmask parameter is invalid" }, { IPSET_ERR_INVALID_FAMILY, 0, "Protocol family not supported by the set type" }, + { IPSET_ERR_BITMASK_NETMASK_EXCL, 0, + "netmask and bitmask options are mutually exclusive, provide only one" }, /* DESTROY specific error codes */ { IPSET_ERR_BUSY, IPSET_CMD_DESTROY, diff --git a/lib/ipset.c b/lib/ipset.c index 73e67db..c910d88 100644 --- a/lib/ipset.c +++ b/lib/ipset.c @@ -30,6 +30,7 @@ #include <libipset/ipset.h> /* prototypes */ #include <libipset/ip_set_compiler.h> /* compiler attributes */ #include <libipset/list_sort.h> /* lists */ +#include <libipset/xlate.h> /* ipset_xlate_argv */ static char program_name[] = PACKAGE; static char program_version[] = PACKAGE_VERSION; @@ -234,7 +235,7 @@ const struct ipset_envopts ipset_envopts[] = { { .name = { "-o", "-output" }, .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX, .parse = ipset_parse_output, - .help = "plain|save|xml\n" + .help = "plain|save|xml|json\n" " Specify output mode for listing sets.\n" " Default value for \"list\" command is mode \"plain\"\n" " and for \"save\" command is mode \"save\".", @@ -428,6 +429,8 @@ ipset_parse_output(struct ipset *ipset, 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, "json")) + return ipset_session_output(session, IPSET_LIST_JSON); else if (STREQ(str, "save")) return ipset_session_output(session, IPSET_LIST_SAVE); @@ -936,10 +939,10 @@ static const char *cmd_prefix[] = { [IPSET_TEST] = "test SETNAME", }; -static const struct ipset_xlate_set * +static struct ipset_xlate_set * ipset_xlate_set_get(struct ipset *ipset, const char *name) { - const struct ipset_xlate_set *set; + struct ipset_xlate_set *set; list_for_each_entry(set, &ipset->xlate_sets, list) { if (!strcmp(set->name, name)) @@ -949,18 +952,6 @@ ipset_xlate_set_get(struct ipset *ipset, const char *name) 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[]) { @@ -970,7 +961,7 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) char *arg0 = NULL, *arg1 = NULL; const struct ipset_envopts *opt; const struct ipset_commands *command; - const struct ipset_type *type; + const struct ipset_type *type = NULL; struct ipset_session *session = ipset->session; void *p = ipset_session_printf_private(session); int argc = oargc; @@ -1139,6 +1130,7 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) if (arg0) { const struct ipset_arg *arg; int k; + enum ipset_adt c; /* Type-specific help, without kernel checking */ type = type_find(arg0); @@ -1148,11 +1140,11 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) "Unknown settype: `%s'", arg0); printf("\n%s type specific options:\n\n", type->name); for (i = 0; cmd_help_order[i] != IPSET_CADT_MAX; i++) { - cmd = cmd_help_order[i]; + c = cmd_help_order[i]; printf("%s %s %s\n", - cmd_prefix[cmd], type->name, type->cmd[cmd].help); - for (k = 0; type->cmd[cmd].args[k] != IPSET_ARG_NONE; k++) { - arg = ipset_keyword(type->cmd[cmd].args[k]); + cmd_prefix[c], type->name, type->cmd[c].help); + for (k = 0; type->cmd[c].args[k] != IPSET_ARG_NONE; k++) { + arg = ipset_keyword(type->cmd[c].args[k]); if (!arg->help || arg->help[0] == '\0') continue; printf(" %s\n", arg->help); @@ -1282,8 +1274,16 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) 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); + const struct ipset_xlate_set *xlate_set; + + xlate_set = ipset_xlate_set_get(ipset, arg0); + if (xlate_set) { + ipset_session_data_set(session, IPSET_OPT_TYPE, + xlate_set->type); + ipset_session_data_set(session, IPSET_OPT_FAMILY, + &xlate_set->family); + type = xlate_set->type; + } } if (type == NULL) return ipset->standard_error(ipset, p); @@ -1552,7 +1552,7 @@ ipset_fini(struct ipset *ipset) } /* Ignore the set family, use inet. */ -static const char *ipset_xlate_family(uint8_t family) +static const char *ipset_xlate_family(uint8_t family UNUSED) { return "inet"; } @@ -1709,6 +1709,10 @@ ipset_xlate_type_to_nftables(int family, enum ipset_xlate_set_type type, else if (family == AF_INET6) return "ipv6_addr"; break; + case IPSET_XLATE_TYPE_UNKNOWN: + break; + default: + break; } /* This should not ever happen. */ return "unknown"; @@ -1733,7 +1737,6 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, char buf[64]; bool concat; char *term; - int i; session = ipset_session(ipset); data = ipset_session_data(session); @@ -1847,7 +1850,7 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, return -1; case IPSET_CMD_LIST: if (!set) { - printf("list sets %s\n", + printf("list sets %s %s\n", ipset_xlate_family(family), table); } else { printf("list set %s %s %s\n", @@ -1875,9 +1878,6 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, 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) @@ -1906,6 +1906,8 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, } if (ipset_data_test(data, IPSET_OPT_ETHER)) { ipset_print_ether(buf, sizeof(buf), data, IPSET_OPT_ETHER, 0); + size_t i; + for (i = 0; i < strlen(buf); i++) buf[i] = tolower(buf[i]); @@ -1968,7 +1970,6 @@ 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; @@ -1977,7 +1978,7 @@ static int ipset_xlate_restore(struct ipset *ipset) if (ipset->filename) { f = fopen(ipset->filename, "r"); if (!f) { - fprintf(stderr, "cannot open file `%s'\n", filename); + fprintf(stderr, "cannot open file `%s'\n", ipset->filename); return -1; } } @@ -1997,7 +1998,7 @@ static int ipset_xlate_restore(struct ipset *ipset) ret = build_argv(ipset, c); if (ret < 0) - return ret; + break; cmd = ipset_parser(ipset, ipset->newargc, ipset->newargv); if (cmd < 0) @@ -2011,7 +2012,7 @@ static int ipset_xlate_restore(struct ipset *ipset) ipset_data_reset(data); } - if (filename) + if (ipset->filename) fclose(f); return ret; diff --git a/lib/ipset_hash_ip.c b/lib/ipset_hash_ip.c index ea85700..4f96ebb 100644 --- a/lib/ipset_hash_ip.c +++ b/lib/ipset_hash_ip.c @@ -477,6 +477,91 @@ static struct ipset_type ipset_hash_ip5 = { .description = "bucketsize, initval support", }; +/* bitmask support */ +static struct ipset_type ipset_hash_ip6 = { + .name = "hash:ip", + .alias = { "iphash", NULL }, + .revision = 6, + .family = NFPROTO_IPSET_IPV46, + .dimension = IPSET_DIM_ONE, + .elem = { + [IPSET_DIM_ONE - 1] = { + .parse = ipset_parse_ip4_single6, + .print = ipset_print_ip, + .opt = IPSET_OPT_IP + }, + }, + .cmd = { + [IPSET_CREATE] = { + .args = { + IPSET_ARG_FAMILY, + /* Aliases */ + IPSET_ARG_INET, + IPSET_ARG_INET6, + IPSET_ARG_HASHSIZE, + IPSET_ARG_MAXELEM, + IPSET_ARG_NETMASK, + IPSET_ARG_BITMASK, + IPSET_ARG_TIMEOUT, + IPSET_ARG_COUNTERS, + IPSET_ARG_COMMENT, + IPSET_ARG_FORCEADD, + IPSET_ARG_SKBINFO, + IPSET_ARG_BUCKETSIZE, + IPSET_ARG_INITVAL, + /* Ignored options: backward compatibilty */ + IPSET_ARG_PROBES, + IPSET_ARG_RESIZE, + IPSET_ARG_GC, + IPSET_ARG_NONE, + }, + .need = 0, + .full = 0, + .help = "", + }, + [IPSET_ADD] = { + .args = { + IPSET_ARG_TIMEOUT, + IPSET_ARG_PACKETS, + IPSET_ARG_BYTES, + IPSET_ARG_ADT_COMMENT, + IPSET_ARG_SKBMARK, + IPSET_ARG_SKBPRIO, + IPSET_ARG_SKBQUEUE, + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP_TO), + .help = "IP", + }, + [IPSET_DEL] = { + .args = { + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP_TO), + .help = "IP", + }, + [IPSET_TEST] = { + .args = { + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP_TO), + .help = "IP", + }, + }, + .usage = "where depending on the INET family\n" + " IP is a valid IPv4 or IPv6 address (or hostname),\n" + " CIDR is a valid IPv4 or IPv6 CIDR prefix.\n" + " Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n" + " is supported for IPv4.", + .description = "bitmask support", +}; + void _init(void); void _init(void) { @@ -486,4 +571,5 @@ void _init(void) ipset_type_add(&ipset_hash_ip3); ipset_type_add(&ipset_hash_ip4); ipset_type_add(&ipset_hash_ip5); + ipset_type_add(&ipset_hash_ip6); } diff --git a/lib/ipset_hash_ipport.c b/lib/ipset_hash_ipport.c index 288be10..2fa8abd 100644 --- a/lib/ipset_hash_ipport.c +++ b/lib/ipset_hash_ipport.c @@ -604,6 +604,113 @@ static struct ipset_type ipset_hash_ipport6 = { .description = "bucketsize, initval support", }; +/* bitmask support */ +static struct ipset_type ipset_hash_ipport7 = { + .name = "hash:ip,port", + .alias = { "ipporthash", NULL }, + .revision = 7, + .family = NFPROTO_IPSET_IPV46, + .dimension = IPSET_DIM_TWO, + .elem = { + [IPSET_DIM_ONE - 1] = { + .parse = ipset_parse_ip4_single6, + .print = ipset_print_ip, + .opt = IPSET_OPT_IP + }, + [IPSET_DIM_TWO - 1] = { + .parse = ipset_parse_proto_port, + .print = ipset_print_proto_port, + .opt = IPSET_OPT_PORT + }, + }, + .cmd = { + [IPSET_CREATE] = { + .args = { + IPSET_ARG_FAMILY, + /* Aliases */ + IPSET_ARG_INET, + IPSET_ARG_INET6, + IPSET_ARG_HASHSIZE, + IPSET_ARG_MAXELEM, + IPSET_ARG_TIMEOUT, + IPSET_ARG_COUNTERS, + IPSET_ARG_COMMENT, + IPSET_ARG_FORCEADD, + IPSET_ARG_SKBINFO, + IPSET_ARG_BUCKETSIZE, + IPSET_ARG_INITVAL, + IPSET_ARG_NETMASK, + IPSET_ARG_BITMASK, + /* Ignored options: backward compatibilty */ + IPSET_ARG_PROBES, + IPSET_ARG_RESIZE, + IPSET_ARG_IGNORED_FROM, + IPSET_ARG_IGNORED_TO, + IPSET_ARG_IGNORED_NETWORK, + IPSET_ARG_NONE, + }, + .need = 0, + .full = 0, + .help = "", + }, + [IPSET_ADD] = { + .args = { + IPSET_ARG_TIMEOUT, + IPSET_ARG_PACKETS, + IPSET_ARG_BYTES, + IPSET_ARG_ADT_COMMENT, + IPSET_ARG_SKBMARK, + IPSET_ARG_SKBPRIO, + IPSET_ARG_SKBQUEUE, + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_PORT), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PORT_TO), + .help = "IP,[PROTO:]PORT", + }, + [IPSET_DEL] = { + .args = { + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_PORT), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PORT_TO), + .help = "IP,[PROTO:]PORT", + }, + [IPSET_TEST] = { + .args = { + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_PORT), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_PROTO) + | IPSET_FLAG(IPSET_OPT_PORT), + .help = "IP,[PROTO:]PORT", + }, + }, + .usage = "where depending on the INET family\n" + " IP is a valid IPv4 or IPv6 address (or hostname).\n" + " Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n" + " is supported for IPv4.\n" + " Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n" + " port range is supported both for IPv4 and IPv6.", + .usagefn = ipset_port_usage, + .description = "netmask and bitmask support", +}; + void _init(void); void _init(void) { @@ -613,4 +720,5 @@ void _init(void) ipset_type_add(&ipset_hash_ipport4); ipset_type_add(&ipset_hash_ipport5); ipset_type_add(&ipset_hash_ipport6); + ipset_type_add(&ipset_hash_ipport7); } diff --git a/lib/ipset_hash_netnet.c b/lib/ipset_hash_netnet.c index df993b8..0e176e3 100644 --- a/lib/ipset_hash_netnet.c +++ b/lib/ipset_hash_netnet.c @@ -387,6 +387,106 @@ static struct ipset_type ipset_hash_netnet3 = { .description = "bucketsize, initval support", }; +/* bitmask support */ +static struct ipset_type ipset_hash_netnet4 = { + .name = "hash:net,net", + .alias = { "netnethash", NULL }, + .revision = 4, + .family = NFPROTO_IPSET_IPV46, + .dimension = IPSET_DIM_TWO, + .elem = { + [IPSET_DIM_ONE - 1] = { + .parse = ipset_parse_ip4_net6, + .print = ipset_print_ip, + .opt = IPSET_OPT_IP + }, + [IPSET_DIM_TWO - 1] = { + .parse = ipset_parse_ip4_net6, + .print = ipset_print_ip, + .opt = IPSET_OPT_IP2 + }, + }, + .cmd = { + [IPSET_CREATE] = { + .args = { + IPSET_ARG_FAMILY, + /* Aliases */ + IPSET_ARG_INET, + IPSET_ARG_INET6, + IPSET_ARG_HASHSIZE, + IPSET_ARG_MAXELEM, + IPSET_ARG_TIMEOUT, + IPSET_ARG_COUNTERS, + IPSET_ARG_COMMENT, + IPSET_ARG_FORCEADD, + IPSET_ARG_SKBINFO, + IPSET_ARG_BUCKETSIZE, + IPSET_ARG_INITVAL, + IPSET_ARG_BITMASK, + IPSET_ARG_NETMASK, + IPSET_ARG_NONE, + }, + .need = 0, + .full = 0, + .help = "", + }, + [IPSET_ADD] = { + .args = { + IPSET_ARG_TIMEOUT, + IPSET_ARG_NOMATCH, + IPSET_ARG_PACKETS, + IPSET_ARG_BYTES, + IPSET_ARG_ADT_COMMENT, + IPSET_ARG_SKBMARK, + IPSET_ARG_SKBPRIO, + IPSET_ARG_SKBQUEUE, + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP2), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_IP2) + | IPSET_FLAG(IPSET_OPT_CIDR2) + | IPSET_FLAG(IPSET_OPT_IP2_TO), + .help = "IP[/CIDR]|FROM-TO,IP[/CIDR]|FROM-TO", + }, + [IPSET_DEL] = { + .args = { + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP2), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_IP2) + | IPSET_FLAG(IPSET_OPT_CIDR2) + | IPSET_FLAG(IPSET_OPT_IP2_TO), + .help = "IP[/CIDR]|FROM-TO,IP[/CIDR]|FROM-TO", + }, + [IPSET_TEST] = { + .args = { + IPSET_ARG_NOMATCH, + IPSET_ARG_NONE, + }, + .need = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP2), + .full = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_CIDR) + | IPSET_FLAG(IPSET_OPT_IP2) + | IPSET_FLAG(IPSET_OPT_CIDR2), + .help = "IP[/CIDR],IP[/CIDR]", + }, + }, + .usage = "where depending on the INET family\n" + " IP is an IPv4 or IPv6 address (or hostname),\n" + " CIDR is a valid IPv4 or IPv6 CIDR prefix.\n" + " IP range is not supported with IPv6.", + .description = "netmask and bitmask support", +}; + void _init(void); void _init(void) { @@ -394,4 +494,5 @@ void _init(void) ipset_type_add(&ipset_hash_netnet1); ipset_type_add(&ipset_hash_netnet2); ipset_type_add(&ipset_hash_netnet3); + ipset_type_add(&ipset_hash_netnet4); } diff --git a/lib/libipset.map b/lib/libipset.map index c380f9c..c69b738 100644 --- a/lib/libipset.map +++ b/lib/libipset.map @@ -213,3 +213,7 @@ global: ipset_xlate_argv; } LIBIPSET_4.10; +LIBIPSET_4.12 { +global: + ipset_parse_bitmask; +} LIBIPSET_4.10; diff --git a/lib/parse.c b/lib/parse.c index 974eaf8..4d2d8b3 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -280,7 +280,8 @@ static int parse_portname(struct ipset_session *session, const char *str, uint16_t *port, const char *proto) { - char *saved, *tmp, *protoname; + char *saved, *tmp; + const char *protoname; const struct protoent *protoent; struct servent *service; uint8_t protonum = 0; @@ -292,7 +293,7 @@ parse_portname(struct ipset_session *session, const char *str, if (tmp == NULL) goto error; - protoname = (char *)proto; + protoname = proto; if (string_to_u8(session, proto, &protonum, IPSET_WARNING) == 0) { protoent = getprotobynumber(protonum); if (protoent == NULL) @@ -1703,6 +1704,9 @@ ipset_parse_netmask(struct ipset_session *session, assert(str); data = ipset_session_data(session); + if (ipset_data_test(data, IPSET_OPT_BITMASK)) + return syntax_err("bitmask and netmask are mutually exclusive, provide only one"); + family = ipset_data_family(data); if (family == NFPROTO_UNSPEC) { family = NFPROTO_IPV4; @@ -1722,6 +1726,46 @@ ipset_parse_netmask(struct ipset_session *session, } /** + * ipset_parse_bitmask - parse string as a bitmask + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a bitmask 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_bitmask(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + uint8_t family; + struct ipset_data *data; + + assert(session); + assert(opt == IPSET_OPT_BITMASK); + assert(str); + + data = ipset_session_data(session); + if (ipset_data_test(data, IPSET_OPT_NETMASK)) + return syntax_err("bitmask and netmask are mutually exclusive, provide only one"); + + family = ipset_data_family(data); + if (family == NFPROTO_UNSPEC) { + family = NFPROTO_IPV4; + ipset_data_set(data, IPSET_OPT_FAMILY, &family); + } + + if (parse_ipaddr(session, opt, str, family)) + return syntax_err("bitmask is not valid for family = %s", + family == NFPROTO_IPV4 ? "inet" : "inet6"); + + return 0; +} + +/** * ipset_parse_flag - "parse" option flags * @session: session structure * @opt: option kind of the data diff --git a/lib/print.c b/lib/print.c index a7ffd81..6ea79cb 100644 --- a/lib/print.c +++ b/lib/print.c @@ -265,7 +265,7 @@ ipset_print_ip(char *buf, unsigned int len, assert(buf); assert(len > 0); assert(data); - assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2 || opt == IPSET_OPT_BITMASK); D("len: %u", len); family = ipset_data_family(data); @@ -411,10 +411,11 @@ ipset_print_number(char *buf, unsigned int len, int ipset_print_hexnumber(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, - uint8_t env UNUSED) + uint8_t env) { size_t maxsize; const void *number; + const char *quoted = env & IPSET_ENV_QUOTED ? "\"" : ""; assert(buf); assert(len > 0); @@ -424,17 +425,17 @@ ipset_print_hexnumber(char *buf, unsigned int len, maxsize = ipset_data_sizeof(opt, AF_INET); D("opt: %u, maxsize %zu", opt, maxsize); if (maxsize == sizeof(uint8_t)) - return snprintf(buf, len, "0x%02"PRIx8, - *(const uint8_t *) number); + return snprintf(buf, len, "%s0x%02"PRIx8"%s", + quoted, *(const uint8_t *) number, quoted); else if (maxsize == sizeof(uint16_t)) - return snprintf(buf, len, "0x%04"PRIx16, - *(const uint16_t *) number); + return snprintf(buf, len, "%s0x%04"PRIx16"%s", + quoted, *(const uint16_t *) number, quoted); else if (maxsize == sizeof(uint32_t)) - return snprintf(buf, len, "0x%08"PRIx32, - *(const uint32_t *) number); + return snprintf(buf, len, "%s0x%08"PRIx32"%s", + quoted, *(const uint32_t *) number, quoted); else if (maxsize == sizeof(uint64_t)) - return snprintf(buf, len, "0x%016"PRIx64, - *(const uint64_t *) number); + return snprintf(buf, len, "%s0x%016"PRIx64"%s", + quoted, *(const uint64_t *) number, quoted); else assert(0); return 0; @@ -976,6 +977,7 @@ ipset_print_data(char *buf, unsigned int len, size = ipset_print_elem(buf, len, data, opt, env); break; case IPSET_OPT_IP: + case IPSET_OPT_BITMASK: size = ipset_print_ip(buf, len, data, opt, env); break; case IPSET_OPT_PORT: diff --git a/lib/session.c b/lib/session.c index 1ca26ff..f822288 100644 --- a/lib/session.c +++ b/lib/session.c @@ -462,6 +462,10 @@ static const struct ipset_attr_policy create_attrs[] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_MEMSIZE, }, + [IPSET_ATTR_BITMASK] = { + .type = MNL_TYPE_NESTED, + .opt = IPSET_OPT_BITMASK, + }, }; static const struct ipset_attr_policy adt_attrs[] = { @@ -856,6 +860,7 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) const struct ipset_arg *arg; size_t offset = 0; int i, found = 0; + static char last_setname[IPSET_MAXNAMELEN] = ""; D("enter"); /* Check and load type, family */ @@ -890,6 +895,13 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) case IPSET_LIST_XML: safe_snprintf(session, "<member><elem>"); break; + case IPSET_LIST_JSON: + /* print separator if a member for this set was printed before */ + if (STREQ(ipset_data_setname(data), last_setname)) + safe_snprintf(session, ","); + strcpy(last_setname, ipset_data_setname(data)); + safe_snprintf(session, "\n {\n \"elem\" : \""); + break; case IPSET_LIST_PLAIN: default: break; @@ -898,6 +910,8 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM); if (session->mode == IPSET_LIST_XML) safe_snprintf(session, "</elem>"); + if (session->mode == IPSET_LIST_JSON) + safe_snprintf(session, "\""); for (i = 0; type->cmd[IPSET_ADD].args[i] != IPSET_ARG_NONE; i++) { arg = ipset_keyword(type->cmd[IPSET_ADD].args[i]); @@ -925,6 +939,15 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) safe_dprintf(session, arg->print, arg->opt); safe_snprintf(session, "</%s>", arg->name[0]); break; + case IPSET_LIST_JSON: + if (arg->has_arg == IPSET_NO_ARG) { + safe_snprintf(session, + ",\n \"%s\" : true", arg->name[0]); + break; + } + safe_snprintf(session, ",\n \"%s\" : ", arg->name[0]); + safe_dprintf(session, arg->print, arg->opt); + break; default: break; } @@ -932,6 +955,8 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) if (session->mode == IPSET_LIST_XML) safe_snprintf(session, "</member>\n"); + else if (session->mode == IPSET_LIST_JSON) + safe_snprintf(session, "\n }"); else safe_snprintf(session, "\n"); @@ -968,6 +993,7 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) const struct ipset_arg *arg; uint8_t family; int i; + static bool firstipset = true; for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++) if (nla[i]) { @@ -1003,6 +1029,19 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) ipset_data_setname(data), type->name, type->revision); break; + case IPSET_LIST_JSON: + if (!firstipset) + safe_snprintf(session, ",\n"); + firstipset = false; + safe_snprintf(session, + " \{\n" + " \"name\" : \"%s\",\n" + " \"type\" : \"%s\",\n" + " \"revision\" : %u,\n" + " \"header\" : \{\n", + ipset_data_setname(data), + type->name, type->revision); + break; default: break; } @@ -1038,6 +1077,22 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) safe_dprintf(session, arg->print, arg->opt); safe_snprintf(session, "</%s>", arg->name[0]); break; + case IPSET_LIST_JSON: + if (arg->has_arg == IPSET_NO_ARG) { + safe_snprintf(session, + " \"%s\" : true,\n", arg->name[0]); + break; + } + if (arg->opt == IPSET_OPT_FAMILY) { + safe_snprintf(session, " \"%s\" : \"", arg->name[0]); + safe_dprintf(session, arg->print, arg->opt); + safe_snprintf(session, "\",\n"); + break; + } + safe_snprintf(session, " \"%s\" : ", arg->name[0]); + safe_dprintf(session, arg->print, arg->opt); + safe_snprintf(session, ",\n"); + break; default: break; } @@ -1075,6 +1130,21 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) "</header>\n" : "</header>\n<members>\n"); break; + case IPSET_LIST_JSON: + safe_snprintf(session, " \"memsize\" : "); + safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE); + safe_snprintf(session, ",\n \"references\" : "); + safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES); + if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) { + safe_snprintf(session, ",\n \"numentries\" : "); + safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS); + } + safe_snprintf(session, "\n"); + safe_snprintf(session, + session->envopts & IPSET_ENV_LIST_HEADER ? + " },\n" : + " },\n \"members\" : ["); + break; default: break; } @@ -1210,11 +1280,24 @@ print_set_done(struct ipset_session *session, bool callback_done) if (session->saved_setname[0] != '\0') safe_snprintf(session, "</members>\n</ipset>\n"); break; + case IPSET_LIST_JSON: + if (session->envopts & IPSET_ENV_LIST_SETNAME) + break; + if (session->envopts & IPSET_ENV_LIST_HEADER) { + if (session->saved_setname[0] != '\0') + safe_snprintf(session, " }"); + break; + } + if (session->saved_setname[0] != '\0') + safe_snprintf(session, "\n ]\n }"); + break; default: break; } if (callback_done && session->mode == IPSET_LIST_XML) safe_snprintf(session, "</ipsets>\n"); + if (callback_done && session->mode == IPSET_LIST_JSON) + safe_snprintf(session, "\n]\n"); return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP; } @@ -1223,6 +1306,7 @@ callback_list(struct ipset_session *session, struct nlattr *nla[], enum ipset_cmd cmd) { struct ipset_data *data = session->data; + static bool firstipset = true; if (setjmp(printf_failure)) { session->saved_setname[0] = '\0'; @@ -1241,7 +1325,13 @@ callback_list(struct ipset_session *session, struct nlattr *nla[], if (session->mode == IPSET_LIST_XML) safe_snprintf(session, "<ipset name=\"%s\"/>\n", ipset_data_setname(data)); - else + else if (session->mode == IPSET_LIST_JSON) { + if (!firstipset) + safe_snprintf(session, ",\n"); + firstipset = false; + safe_snprintf(session, " { \"name\" : \"%s\" }", + ipset_data_setname(data)); + } else safe_snprintf(session, "%s\n", ipset_data_setname(data)); return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK; @@ -1721,6 +1811,10 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh, if (attr->type == MNL_TYPE_NESTED) { /* IP addresses */ struct nlattr *nested; + + if (type == IPSET_ATTR_BITMASK) + family = ipset_data_family(session->data); + int atype = family == NFPROTO_IPV4 ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6; @@ -2187,18 +2281,27 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno) 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 (cmd == IPSET_CMD_LIST || cmd == IPSET_CMD_SAVE) { + /* Set default output mode */ if (session->mode == IPSET_LIST_NONE) - session->mode = IPSET_LIST_SAVE; + session->mode = cmd == IPSET_CMD_LIST ? + IPSET_LIST_PLAIN : IPSET_LIST_SAVE; + /* Reset just in case there are multiple modes in a session */ + ipset_envopt_unset(session, IPSET_ENV_QUOTED); + switch (session->mode) { + case IPSET_LIST_XML: + /* Start the root element in XML mode */ + safe_snprintf(session, "<ipsets>\n"); + break; + case IPSET_LIST_JSON: + /* Start the root element in json mode */ + ipset_envopt_set(session, IPSET_ENV_QUOTED); + safe_snprintf(session, "[\n"); + break; + default: + break; + } } - /* Start the root element in XML mode */ - if ((cmd == IPSET_CMD_LIST || cmd == IPSET_CMD_SAVE) && - session->mode == IPSET_LIST_XML) - safe_snprintf(session, "<ipsets>\n"); D("next: build_msg"); /* Build new message or append buffered commands */ |