From 14ea38fca9e40df4f172a573c222591b5f3cc241 Mon Sep 17 00:00:00 2001 From: Vytas Dauksa Date: Tue, 17 Dec 2013 14:01:43 +0000 Subject: add hash:ip,mark data type to ipset Introduce packet mark support with new ip,mark hash set. This includes userspace and kernelspace code, hash:ip,mark set tests and man page updates. The intended use of ip,mark set is similar to the ip:port type, but for protocols which don't use a predictable port number. Instead of port number it matches a firewall mark determined by a layer 7 filtering program like opendpi. As well as allowing or blocking traffic it will also be used for accounting packets and bytes sent for each protocol. Signed-off-by: Jozsef Kadlecsik --- lib/Makefile.am | 1 + lib/data.c | 8 +++ lib/debug.c | 1 + lib/ipset_hash_ipmark.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/libipset.map | 6 ++ lib/parse.c | 30 +++++++++ lib/print.c | 35 ++++++++++ lib/session.c | 4 ++ 8 files changed, 253 insertions(+) create mode 100644 lib/ipset_hash_ipmark.c (limited to 'lib') diff --git a/lib/Makefile.am b/lib/Makefile.am index 2234670..6398be4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -6,6 +6,7 @@ IPSET_SETTYPE_LIST = \ ipset_bitmap_port.c \ ipset_hash_ip.c \ ipset_hash_ipport.c \ + ipset_hash_ipmark.c \ ipset_hash_ipportip.c \ ipset_hash_ipportnet.c \ ipset_hash_net.c \ diff --git a/lib/data.c b/lib/data.c index ba4ed57..1f74cd5 100644 --- a/lib/data.c +++ b/lib/data.c @@ -41,6 +41,7 @@ struct ipset_data { uint32_t timeout; union nf_inet_addr ip; union nf_inet_addr ip_to; + uint32_t mark; uint16_t port; uint16_t port_to; union { @@ -264,6 +265,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value) case IPSET_OPT_CIDR: data->cidr = *(const uint8_t *) value; break; + case IPSET_OPT_MARK: + data->mark = *(const uint32_t *) value; + break; case IPSET_OPT_PORT: data->port = *(const uint16_t *) value; break; @@ -448,6 +452,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt) return &data->ip_to; case IPSET_OPT_CIDR: return &data->cidr; + case IPSET_OPT_MARK: + return &data->mark; case IPSET_OPT_PORT: return &data->port; case IPSET_OPT_PORT_TO: @@ -542,6 +548,8 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family) case IPSET_OPT_IP2_TO: return family == NFPROTO_IPV4 ? sizeof(uint32_t) : sizeof(struct in6_addr); + case IPSET_OPT_MARK: + return sizeof(uint32_t); case IPSET_OPT_PORT: case IPSET_OPT_PORT_TO: return sizeof(uint16_t); diff --git a/lib/debug.c b/lib/debug.c index a204940..9a3c6b8 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -51,6 +51,7 @@ static const struct ipset_attrname adtattr2name[] = { [IPSET_ATTR_IP] = { .name = "IP" }, [IPSET_ATTR_IP_TO] = { .name = "IP_TO" }, [IPSET_ATTR_CIDR] = { .name = "CIDR" }, + [IPSET_ATTR_MARK] = { .name = "MARK" }, [IPSET_ATTR_PORT] = { .name = "PORT" }, [IPSET_ATTR_PORT_TO] = { .name = "PORT_TO" }, [IPSET_ATTR_TIMEOUT] = { .name = "TIMEOUT" }, diff --git a/lib/ipset_hash_ipmark.c b/lib/ipset_hash_ipmark.c new file mode 100644 index 0000000..6976371 --- /dev/null +++ b/lib/ipset_hash_ipmark.c @@ -0,0 +1,168 @@ +/* Copyright 2007-2013 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * Copyright 2013 Smoothwall Ltd. (vytas.dauksa@smoothwall.net) + * + * 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 /* IPSET_OPT_* */ +#include /* parser functions */ +#include /* printing functions */ +#include /* prototypes */ + +/* Parse commandline arguments */ +static const struct ipset_arg hash_ipmark_create_args0[] = { + { .name = { "family", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, .print = ipset_print_family, + }, + /* Alias: family inet */ + { .name = { "-4", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + /* Alias: family inet6 */ + { .name = { "-6", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + { .name = { "hashsize", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE, + .parse = ipset_parse_uint32, .print = ipset_print_number, + }, + { .name = { "maxelem", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM, + .parse = ipset_parse_uint32, .print = ipset_print_number, + }, + { .name = { "timeout", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, + .parse = ipset_parse_timeout, .print = ipset_print_number, + }, + { .name = { "counters", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_COUNTERS, + .parse = ipset_parse_flag, .print = ipset_print_flag, + }, + { .name = { "comment", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_CREATE_COMMENT, + .parse = ipset_parse_flag, .print = ipset_print_flag, + }, + /* Backward compatibility */ + { .name = { "probes", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "resize", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "from", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, + .parse = ipset_parse_ignored, + }, + { .name = { "to", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO, + .parse = ipset_parse_ignored, + }, + { .name = { "network", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, + .parse = ipset_parse_ignored, + }, + { }, +}; + +static const struct ipset_arg hash_ipmark_add_args0[] = { + { .name = { "timeout", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, + .parse = ipset_parse_timeout, .print = ipset_print_number, + }, + { .name = { "packets", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PACKETS, + .parse = ipset_parse_uint64, .print = ipset_print_number, + }, + { .name = { "bytes", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_BYTES, + .parse = ipset_parse_uint64, .print = ipset_print_number, + }, + { .name = { "comment", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_ADT_COMMENT, + .parse = ipset_parse_comment, .print = ipset_print_comment, + }, + { }, +}; + +static const char hash_ipmark_usage0[] = +"create SETNAME hash:ip,mark\n" +" [family inet|inet6]\n" +" [hashsize VALUE] [maxelem VALUE]\n" +" [timeout VALUE] [counters] [comment]\n" +"add SETNAME IP,MARK [timeout VALUE]\n" +" [packets VALUE] [bytes VALUE] [comment \"string\"]\n" +"del SETNAME IP,MARK\n" +"test SETNAME IP,MARK\n\n" +"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 single mark element\n" +" is supported both for IPv4 and IPv6.\n"; + +static struct ipset_type ipset_hash_ipmark0 = { + .name = "hash:ip,mark", + .alias = { "ipmarkhash", NULL }, + .revision = 0, + .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_mark, + .print = ipset_print_mark, + .opt = IPSET_OPT_MARK + }, + }, + .args = { + [IPSET_CREATE] = hash_ipmark_create_args0, + [IPSET_ADD] = hash_ipmark_add_args0, + }, + .mandatory = { + [IPSET_CREATE] = 0, + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_MARK), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_MARK), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_MARK), + }, + .full = { + [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) + | IPSET_FLAG(IPSET_OPT_MAXELEM) + | IPSET_FLAG(IPSET_OPT_TIMEOUT) + | IPSET_FLAG(IPSET_OPT_COUNTERS) + | IPSET_FLAG(IPSET_OPT_CREATE_COMMENT), + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_MARK) + | IPSET_FLAG(IPSET_OPT_TIMEOUT) + | IPSET_FLAG(IPSET_OPT_PACKETS) + | IPSET_FLAG(IPSET_OPT_BYTES) + | IPSET_FLAG(IPSET_OPT_ADT_COMMENT), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IP_TO) + | IPSET_FLAG(IPSET_OPT_MARK), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_MARK), + }, + + .usage = hash_ipmark_usage0, + .description = "initial revision", +}; + +void _init(void); +void _init(void) +{ + ipset_type_add(&ipset_hash_ipmark0); +} diff --git a/lib/libipset.map b/lib/libipset.map index 1080f0d..a035115 100644 --- a/lib/libipset.map +++ b/lib/libipset.map @@ -138,3 +138,9 @@ global: ipset_print_comment; ipset_strlcat; } LIBIPSET_4.0; + +LIBIPSET_4.2 { +global: + ipset_parse_mark; + ipset_print_mark; +} LIBIPSET_4.1; diff --git a/lib/parse.c b/lib/parse.c index 440ef8f..f1c1f0e 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -327,6 +327,36 @@ ipset_parse_port(struct ipset_session *session, return err; } +/** + * ipset_parse_mark - parse a mark + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a mark. The parsed mark number is + * stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_mark(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + uint32_t mark; + int err; + + assert(session); + assert(str); + + if ((err = string_to_u32(session, str, &mark)) == 0) + err = ipset_session_data_set(session, opt, &mark); + + if (!err) + /* No error, so reset false error messages! */ + ipset_session_report_reset(session); + return err; +} + /** * ipset_parse_tcpudp_port - parse TCP/UDP port name, number, or range of them * @session: session structure diff --git a/lib/print.c b/lib/print.c index 6988fdf..3c43443 100644 --- a/lib/print.c +++ b/lib/print.c @@ -490,6 +490,41 @@ ipset_print_port(char *buf, unsigned int len, return offset; } +/** + * ipset_print_mark - print mark to string + * @buf: printing buffer + * @len: length of available buffer space + * @data: data blob + * @opt: the option kind + * @env: environment flags + * + * Print mark to output buffer. + * + * Return lenght of printed string or error size. + */ +int +ipset_print_mark(char *buf, unsigned int len, + const struct ipset_data *data, + enum ipset_opt opt ASSERT_UNUSED, + uint8_t env UNUSED) +{ + const uint32_t *mark; + int size, offset = 0; + + assert(buf); + assert(len > 0); + assert(data); + assert(opt == IPSET_OPT_MARK); + + mark = ipset_data_get(data, IPSET_OPT_MARK); + assert(mark); + + size = snprintf(buf, len, "%u", *mark); + SNPRINTF_FAILURE(size, len, offset); + + return offset; +} + /** * ipset_print_iface - print interface element string * @buf: printing buffer diff --git a/lib/session.c b/lib/session.c index 6f89281..cf65960 100644 --- a/lib/session.c +++ b/lib/session.c @@ -424,6 +424,10 @@ static const struct ipset_attr_policy adt_attrs[] = { .type = MNL_TYPE_U8, .opt = IPSET_OPT_CIDR, }, + [IPSET_ATTR_MARK] = { + .type = MNL_TYPE_U32, + .opt = IPSET_OPT_MARK, + }, [IPSET_ATTR_PORT] = { .type = MNL_TYPE_U16, .opt = IPSET_OPT_PORT, -- cgit v1.2.3