From 020936c8c3375e1efe44a3087c891a4b2cbfe044 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 22 Jun 2010 10:49:41 +0200 Subject: ipset 5: last new feature added - the hash types can now store protocol together port, not only port - lots of fixes everywhere: parser, error reporting, manpage The last bits on the todo list before announcing ipset 5: - recheck all the error messages - add possibly more tests - polish manpage --- Make_global.am | 5 +- include/libipset/data.h | 2 + include/libipset/linux_ip_set.h | 74 ++++++------- include/libipset/linux_ip_set_hash.h | 2 + include/libipset/parse.h | 5 + include/libipset/print.h | 6 ++ include/libipset/session.h | 1 + kernel/include/linux/netfilter/ip_set.h | 80 ++++++++------- kernel/include/linux/netfilter/ip_set_chash.h | 12 ++- kernel/include/linux/netfilter/ip_set_getport.h | 99 ++++++++++-------- kernel/include/linux/netfilter/ip_set_hash.h | 2 + kernel/ip_set.c | 1 - kernel/ip_set_bitmap_ip.c | 23 +++-- kernel/ip_set_bitmap_ipmac.c | 11 +- kernel/ip_set_bitmap_port.c | 31 +++--- kernel/ip_set_hash_ip.c | 25 +++-- kernel/ip_set_hash_ipport.c | 131 +++++++++++++++++++----- kernel/ip_set_hash_ipportip.c | 128 ++++++++++++++++++----- kernel/ip_set_hash_ipportnet.c | 128 +++++++++++++++++------ kernel/ip_set_hash_net.c | 43 +++++--- kernel/ip_set_list_set.c | 16 ++- lib/data.c | 7 ++ lib/parse.c | 83 +++++++++++++++ lib/print.c | 73 +++++++++++++ lib/session.c | 25 +++++ src/errcode.c | 6 +- src/ipset.8 | 96 +++++++++++------ src/ipset.c | 31 ++++-- src/ipset_hash_ipport.c | 24 +++-- src/ipset_hash_ipportip.c | 20 ++-- src/ipset_hash_ipportnet.c | 20 ++-- tests/hash:ip,port,ip.t | 50 ++++----- tests/hash:ip,port.t | 64 ++++++++---- tests/hash:ip,port.t.list2 | 10 ++ tests/hash:ip6,port,ip6.t | 50 ++++----- tests/hash:ip6,port.t | 42 ++++---- tests/hash:net.t | 36 +++---- tests/hash:net6.t | 36 +++---- tests/init.t | 2 + tests/ipmap.t | 18 ++++ tests/ipporthash.t | 70 ++++++------- tests/ipportiphash.t | 80 +++++++-------- tests/nethash.t | 36 +++---- tests/setlist.t | 54 +++++----- 44 files changed, 1181 insertions(+), 577 deletions(-) create mode 100644 tests/hash:ip,port.t.list2 diff --git a/Make_global.am b/Make_global.am index 857d0de..5b2ec6f 100644 --- a/Make_global.am +++ b/Make_global.am @@ -38,12 +38,11 @@ AM_CFLAGS = -std=gnu99 \ -Wundef \ -Wwrite-strings \ -Wno-missing-field-initializers \ - -Werror \ - -g -ggdb -gdwarf-2 -g3 + -Werror endif if ENABLE_DEBUG -AM_CFLAGS += -DIPSET_DEBUG +AM_CFLAGS += -g -ggdb -gdwarf-2 -g3 -DIPSET_DEBUG endif if ! ENABLE_VERBOSE diff --git a/include/libipset/data.h b/include/libipset/data.h index 936b807..cb35393 100644 --- a/include/libipset/data.h +++ b/include/libipset/data.h @@ -44,6 +44,7 @@ enum ipset_opt { IPSET_OPT_NAMEREF, IPSET_OPT_IP2, IPSET_OPT_CIDR2, + IPSET_OPT_PROTO, /* Swap/rename to */ IPSET_OPT_SETNAME2, /* Flags */ @@ -93,6 +94,7 @@ enum ipset_opt { | IPSET_FLAG(IPSET_OPT_NAMEREF) \ | IPSET_FLAG(IPSET_OPT_IP2) \ | IPSET_FLAG(IPSET_OPT_CIDR2) \ + | IPSET_FLAG(IPSET_OPT_PROTO) \ | IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\ | IPSET_FLAG(IPSET_OPT_BEFORE)) diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h index 4af75ba..0ad2b14 100644 --- a/include/libipset/linux_ip_set.h +++ b/include/libipset/linux_ip_set.h @@ -20,47 +20,47 @@ /* 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_CMD_CREATE, /* 1: Create a new (empty) set */ + IPSET_CMD_DESTROY, /* 2: Remove a (empty) set */ + IPSET_CMD_FLUSH, /* 3: Remove all elements from a set */ + IPSET_CMD_RENAME, /* 4: Rename a set */ + IPSET_CMD_SWAP, /* 5: Swap two sets */ + IPSET_CMD_LIST, /* 6: List sets */ + IPSET_CMD_SAVE, /* 7: Save sets */ + IPSET_CMD_ADD, /* 8: Add an element to a set */ + IPSET_CMD_DEL, /* 9: Delete an element from a set */ + IPSET_CMD_TEST, /* 10: Test an element in a set */ + IPSET_CMD_HEADER, /* 11: Get set header data only */ + IPSET_CMD_TYPE, /* 12: Get set type */ + IPSET_CMD_PROTOCOL, /* 13: 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_QUIT, /* Quit from interactive mode */ + IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */ + IPSET_CMD_HELP, /* 15: Get help */ + IPSET_CMD_VERSION, /* 16: Get program version */ + IPSET_CMD_QUIT, /* 17: Quit from interactive mode */ IPSET_CMD_MAX, - IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* Commit buffered commands */ + IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: 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_PROTOCOL, /* 1: Protocol version */ + IPSET_ATTR_SETNAME, /* 2: Name of the set */ + IPSET_ATTR_TYPENAME, /* 3: Typename */ IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */ - IPSET_ATTR_REVISION, /* Settype revision */ - IPSET_ATTR_FAMILY, /* Settype family */ - IPSET_ATTR_FLAGS, /* Flags at command level */ - 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_REVISION, /* 4: Settype revision */ + IPSET_ATTR_FAMILY, /* 5: Settype family */ + IPSET_ATTR_FLAGS, /* 6: Flags at command level */ + IPSET_ATTR_DATA, /* 7: Nested attributes */ + IPSET_ATTR_ADT, /* 8: Multiple data containers */ + IPSET_ATTR_LINENO, /* 9: Restore lineno */ + IPSET_ATTR_PROTOCOL_MIN, /* 10: 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) @@ -69,13 +69,14 @@ enum { 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_IP_TO, /* 2 */ + IPSET_ATTR_CIDR, /* 3 */ + IPSET_ATTR_PORT, /* 4 */ IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, - IPSET_ATTR_PORT_TO, - IPSET_ATTR_TIMEOUT, - IPSET_ATTR_CADT_FLAGS, + IPSET_ATTR_PORT_TO, /* 5 */ + IPSET_ATTR_TIMEOUT, /* 6 */ + IPSET_ATTR_PROTO, /* 7 */ + IPSET_ATTR_CADT_FLAGS, /* 8 */ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16, @@ -147,4 +148,7 @@ enum ipset_adt { IPSET_CADT_MAX, }; +#define IPSET_IPPROTO_ANY 255 +#define IPSET_IPPROTO_TCPUDP 254 + #endif /* __IP_SET_H */ diff --git a/include/libipset/linux_ip_set_hash.h b/include/libipset/linux_ip_set_hash.h index 76d2489..db6977b 100644 --- a/include/libipset/linux_ip_set_hash.h +++ b/include/libipset/linux_ip_set_hash.h @@ -5,6 +5,8 @@ enum { IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC, IPSET_ERR_HASH_ELEM, + IPSET_ERR_INVALID_PROTO, + IPSET_ERR_MISSING_PROTO, }; #endif /* __IP_SET_HASH_H */ diff --git a/include/libipset/parse.h b/include/libipset/parse.h index 143e2b3..1e055ee 100644 --- a/include/libipset/parse.h +++ b/include/libipset/parse.h @@ -14,6 +14,7 @@ #define IPSET_RANGE_SEPARATOR "-" #define IPSET_ELEM_SEPARATOR "," #define IPSET_NAME_SEPARATOR "," +#define IPSET_PROTO_SEPARATOR ":" struct ipset_session; @@ -26,6 +27,10 @@ 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_proto(struct ipset_session *session, + enum ipset_opt opt, const char *str); +extern int ipset_parse_proto_port(struct ipset_session *session, + enum ipset_opt opt, const char *str); extern int ipset_parse_family(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_ip(struct ipset_session *session, diff --git a/include/libipset/print.h b/include/libipset/print.h index dbb70f3..1fc5abc 100644 --- a/include/libipset/print.h +++ b/include/libipset/print.h @@ -37,6 +37,12 @@ extern int ipset_print_name(char *buf, unsigned int len, 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_proto(char *buf, unsigned int len, + const struct ipset_data *data, enum ipset_opt opt, + uint8_t env); +extern int ipset_print_proto_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); diff --git a/include/libipset/session.h b/include/libipset/session.h index cc0940e..02e8b36 100644 --- a/include/libipset/session.h +++ b/include/libipset/session.h @@ -23,6 +23,7 @@ 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); +extern const struct ipset_type * ipset_saved_type(const struct ipset_session *session); enum ipset_err_type { IPSET_ERROR, diff --git a/kernel/include/linux/netfilter/ip_set.h b/kernel/include/linux/netfilter/ip_set.h index e700503..f306859 100644 --- a/kernel/include/linux/netfilter/ip_set.h +++ b/kernel/include/linux/netfilter/ip_set.h @@ -20,47 +20,47 @@ /* 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_CMD_CREATE, /* 1: Create a new (empty) set */ + IPSET_CMD_DESTROY, /* 2: Remove a (empty) set */ + IPSET_CMD_FLUSH, /* 3: Remove all elements from a set */ + IPSET_CMD_RENAME, /* 4: Rename a set */ + IPSET_CMD_SWAP, /* 5: Swap two sets */ + IPSET_CMD_LIST, /* 6: List sets */ + IPSET_CMD_SAVE, /* 7: Save sets */ + IPSET_CMD_ADD, /* 8: Add an element to a set */ + IPSET_CMD_DEL, /* 9: Delete an element from a set */ + IPSET_CMD_TEST, /* 10: Test an element in a set */ + IPSET_CMD_HEADER, /* 11: Get set header data only */ + IPSET_CMD_TYPE, /* 12: Get set type */ + IPSET_CMD_PROTOCOL, /* 13: 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_QUIT, /* Quit from interactive mode */ + IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */ + IPSET_CMD_HELP, /* 15: Get help */ + IPSET_CMD_VERSION, /* 16: Get program version */ + IPSET_CMD_QUIT, /* 17: Quit from interactive mode */ IPSET_CMD_MAX, - IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* Commit buffered commands */ + IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: 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_PROTOCOL, /* 1: Protocol version */ + IPSET_ATTR_SETNAME, /* 2: Name of the set */ + IPSET_ATTR_TYPENAME, /* 3: Typename */ IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */ - IPSET_ATTR_REVISION, /* Settype revision */ - IPSET_ATTR_FAMILY, /* Settype family */ - IPSET_ATTR_FLAGS, /* Flags at command level */ - 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_REVISION, /* 4: Settype revision */ + IPSET_ATTR_FAMILY, /* 5: Settype family */ + IPSET_ATTR_FLAGS, /* 6: Flags at command level */ + IPSET_ATTR_DATA, /* 7: Nested attributes */ + IPSET_ATTR_ADT, /* 8: Multiple data containers */ + IPSET_ATTR_LINENO, /* 9: Restore lineno */ + IPSET_ATTR_PROTOCOL_MIN, /* 10: 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) @@ -69,13 +69,14 @@ enum { 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_IP_TO, /* 2 */ + IPSET_ATTR_CIDR, /* 3 */ + IPSET_ATTR_PORT, /* 4 */ IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, - IPSET_ATTR_PORT_TO, - IPSET_ATTR_TIMEOUT, - IPSET_ATTR_CADT_FLAGS, + IPSET_ATTR_PORT_TO, /* 5 */ + IPSET_ATTR_TIMEOUT, /* 6 */ + IPSET_ATTR_PROTO, /* 7 */ + IPSET_ATTR_CADT_FLAGS, /* 8 */ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16, @@ -147,6 +148,9 @@ enum ipset_adt { IPSET_CADT_MAX, }; +#define IPSET_IPPROTO_ANY 255 +#define IPSET_IPPROTO_TCPUDP 254 + #ifdef __KERNEL__ #include #include @@ -343,6 +347,12 @@ ip_set_free(void *members, u8 flags) vfree(members); } +static inline bool +ip_set_eexist(int ret, u32 flags) +{ + return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST); +} + /* Useful converters */ static inline u32 ip_set_get_h32(const struct nlattr *attr) diff --git a/kernel/include/linux/netfilter/ip_set_chash.h b/kernel/include/linux/netfilter/ip_set_chash.h index 0d77a5d..e0e16bd 100644 --- a/kernel/include/linux/netfilter/ip_set_chash.h +++ b/kernel/include/linux/netfilter/ip_set_chash.h @@ -37,6 +37,9 @@ struct chash { #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; /* netmask value for subnets to store */ #endif +#ifdef IP_SET_HASH_WITH_PROTO + u8 proto; /* default protocol for SET target */ +#endif #ifdef IP_SET_HASH_WITH_NETS struct chash_nets nets[0]; /* book keeping of networks */ #endif @@ -205,8 +208,9 @@ jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ /* Flavour without timeout */ -#define chash_data(n, i) \ -(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_elem)) +#define chash_data(n, i) \ +(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) \ + + (i)*sizeof(struct type_pf_elem)) static int type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits, @@ -506,6 +510,10 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb) #ifdef IP_SET_HASH_WITH_NETMASK if (h->netmask != HOST_MASK) NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); +#endif +#ifdef IP_SET_HASH_WITH_PROTO + if (h->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, h->proto); #endif NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); diff --git a/kernel/include/linux/netfilter/ip_set_getport.h b/kernel/include/linux/netfilter/ip_set_getport.h index ffa89f1..680a89a 100644 --- a/kernel/include/linux/netfilter/ip_set_getport.h +++ b/kernel/include/linux/netfilter/ip_set_getport.h @@ -8,72 +8,91 @@ #define IPSET_INVALID_PORT 65536 /* We must handle non-linear skbs */ -static bool -get_port(u8 pf, const struct sk_buff *skb, bool src, u16 *port) -{ - unsigned short protocol; - unsigned int protoff; - int fragoff; - - switch (pf) { - case AF_INET: { - const struct iphdr *iph = ip_hdr(skb); - - protocol = iph->protocol; - fragoff = ntohs(iph->frag_off) & IP_OFFSET; - protoff = ip_hdrlen(skb); - break; - } - case AF_INET6: { - int protohdr; - unsigned short frag_off; - - protohdr = ipv6_find_hdr(skb, &protoff, -1, &frag_off); - if (protohdr < 0) - return false; - - protocol = protohdr; - fragoff = frag_off; - break; - } - default: - return false; - } - - /* See comments at tcp_match in ip_tables.c */ - if (fragoff) - return false; +static inline bool +get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, + bool src, u16 *port, u8 *proto) +{ switch (protocol) { case IPPROTO_TCP: { struct tcphdr _tcph; const struct tcphdr *th; - th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph); if (th == NULL) /* No choice either */ return false; *port = src ? th->source : th->dest; break; - } + } case IPPROTO_UDP: { struct udphdr _udph; const struct udphdr *uh; - uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph); + uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph); if (uh == NULL) /* No choice either */ return false; *port = src ? uh->source : uh->dest; break; - } + } default: - return false; + if (*proto == IPSET_IPPROTO_TCPUDP) + return false; + break; } + if (*proto != IPSET_IPPROTO_TCPUDP) + *proto = protocol; + return true; } + +static inline bool +get_ip4_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto) +{ + const struct iphdr *iph = ip_hdr(skb); + unsigned int protooff = ip_hdrlen(skb); + int protocol = iph->protocol; + + if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol)) + return false; + + /* See comments at tcp_match in ip_tables.c */ + if (ntohs(iph->frag_off) & IP_OFFSET) + return false; + + return get_port(skb, protocol, protooff, src, port, proto); +} + +static inline bool +get_ip6_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto) +{ + unsigned int *protooff = 0; + int protocol; + unsigned short fragoff; + + protocol = ipv6_find_hdr(skb, protooff, -1, &fragoff); + if (protocol < 0 || fragoff) + return false; + + if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol)) + return false; + + return get_port(skb, protocol, *protooff, src, port, proto); +} + +static inline bool +get_ip_port(const struct sk_buff *skb, u8 pf, bool src, u16 *port) +{ + u8 proto = IPSET_IPPROTO_TCPUDP; + + if (pf == AF_INET) + return get_ip4_port(skb, src, port, &proto); + else + return get_ip6_port(skb, src, port, &proto); +} #endif /* __KERNEL__ */ #endif /*_IP_SET_GETPORT_H*/ diff --git a/kernel/include/linux/netfilter/ip_set_hash.h b/kernel/include/linux/netfilter/ip_set_hash.h index c1a6964..4003af0 100644 --- a/kernel/include/linux/netfilter/ip_set_hash.h +++ b/kernel/include/linux/netfilter/ip_set_hash.h @@ -5,6 +5,8 @@ enum { IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC, IPSET_ERR_HASH_ELEM, + IPSET_ERR_INVALID_PROTO, + IPSET_ERR_MISSING_PROTO, }; #ifdef __KERNEL__ diff --git a/kernel/ip_set.c b/kernel/ip_set.c index 5bf331e..9d7093c 100644 --- a/kernel/ip_set.c +++ b/kernel/ip_set.c @@ -474,7 +474,6 @@ ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = { .len = IPSET_MAXNAMELEN - 1}, [IPSET_ATTR_REVISION] = { .type = NLA_U8 }, [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, - [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, }; diff --git a/kernel/ip_set_bitmap_ip.c b/kernel/ip_set_bitmap_ip.c index 66d3979..27b9665 100644 --- a/kernel/ip_set_bitmap_ip.c +++ b/kernel/ip_set_bitmap_ip.c @@ -107,6 +107,7 @@ bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -115,7 +116,6 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, { struct bitmap_ip *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u32 ip, ip_to, id; int ret = 0; @@ -123,6 +123,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ip_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); else @@ -164,11 +167,10 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adt == IPSET_ADD ? bitmap_ip_add(map, id) : bitmap_ip_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } @@ -356,7 +358,6 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, { struct bitmap_ip_timeout *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u32 ip, ip_to, id, timeout = map->timeout; int ret = 0; @@ -364,6 +365,9 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ip_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); else @@ -405,11 +409,10 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, ? bitmap_ip_timeout_add(map, id, timeout) : bitmap_ip_timeout_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } diff --git a/kernel/ip_set_bitmap_ipmac.c b/kernel/ip_set_bitmap_ipmac.c index d036862..3c94975 100644 --- a/kernel/ip_set_bitmap_ipmac.c +++ b/kernel/ip_set_bitmap_ipmac.c @@ -368,6 +368,7 @@ bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_U32 }, [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -377,7 +378,6 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, struct bitmap_ipmac *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - bool eexist = flags & IPSET_FLAG_EXIST; struct ipmac data; u32 timeout = map->timeout; int ret = 0; @@ -386,6 +386,9 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ipmac_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.id = ip_set_get_h32(tb[IPSET_ATTR_IP]); else @@ -409,11 +412,7 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static void diff --git a/kernel/ip_set_bitmap_port.c b/kernel/ip_set_bitmap_port.c index f3e498a..6517252 100644 --- a/kernel/ip_set_bitmap_port.c +++ b/kernel/ip_set_bitmap_port.c @@ -72,9 +72,9 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_port *map = set->data; - u16 port; + u16 port = 0; - if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port)) + if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port)) return -EINVAL; port = ntohs(port); @@ -101,6 +101,7 @@ bitmap_port_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -109,7 +110,6 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, { struct bitmap_port *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u32 port; /* wraparound */ u16 id, port_to; int ret = 0; @@ -118,6 +118,9 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_port_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_PORT]) port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); else @@ -150,11 +153,10 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adt == IPSET_ADD ? bitmap_port_add(map, id) : bitmap_port_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } @@ -313,9 +315,9 @@ bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_port_timeout *map = set->data; - u16 port; + u16 port = 0; - if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port)) + if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port)) return -EINVAL; port = ntohs(port); @@ -343,7 +345,6 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, { const struct bitmap_port_timeout *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u16 id, port_to; u32 port, timeout = map->timeout; /* wraparound */ int ret = 0; @@ -352,6 +353,9 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_port_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_PORT]) port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); else @@ -385,11 +389,10 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, ? bitmap_port_timeout_add(map, id, timeout) : bitmap_port_timeout_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } diff --git a/kernel/ip_set_hash_ip.c b/kernel/ip_set_hash_ip.c index e5ce6a4..d73a5da 100644 --- a/kernel/ip_set_hash_ip.c +++ b/kernel/ip_set_hash_ip.c @@ -140,6 +140,7 @@ hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -148,7 +149,6 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; u32 ip, nip, ip_to, hosts, timeout = h->timeout; int ret = 0; @@ -157,6 +157,9 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ip4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -196,11 +199,10 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, nip = htonl(ip); ret = adtfn(set, &nip, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } @@ -213,10 +215,10 @@ hash_ip_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->netmask == y->netmask && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size - && x->chain_limit == y->chain_limit - && x->netmask == y->netmask; + && x->chain_limit == y->chain_limit; } /* The type variant functions: IPv6 */ @@ -327,6 +329,7 @@ hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_BINARY, .len = sizeof(struct in6_addr) }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -338,11 +341,15 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, ipset_adtfn adtfn = set->variant->adt[adt]; union nf_inet_addr *ip; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ip6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = nla_data(tb[IPSET_ATTR_IP]); else @@ -358,7 +365,9 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, ip, GFP_KERNEL, timeout); + ret = adtfn(set, ip, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ diff --git a/kernel/ip_set_hash_ipport.c b/kernel/ip_set_hash_ipport.c index 8210f67..cb319d2 100644 --- a/kernel/ip_set_hash_ipport.c +++ b/kernel/ip_set_hash_ipport.c @@ -48,14 +48,16 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b); struct hash_ipport4_elem { u32 ip; u16 port; - u16 match; + u8 proto; + u8 padding; }; /* Member elements with timeout support */ struct hash_ipport4_telem { u32 ip; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -63,13 +65,15 @@ static inline bool hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1, const struct hash_ipport4_elem *ip2) { - return ip1->ip == ip2->ip && ip1->port == ip2->port; + return ip1->ip == ip2->ip + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -78,7 +82,7 @@ hash_ipport4_data_copy(struct hash_ipport4_elem *dst, { dst->ip = src->ip; dst->port = src->port; - dst->match = 1; + dst->proto = src->proto; } static inline void @@ -87,12 +91,13 @@ hash_ipport4_data_swap(struct hash_ipport4_elem *dst, { swap(dst->ip, src->ip); swap(dst->port, src->port); + swap(dst->proto, src->proto); } static inline void hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -101,6 +106,8 @@ hash_ipport4_data_list(struct sk_buff *skb, { NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -116,6 +123,8 @@ hash_ipport4_data_tlist(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -125,6 +134,8 @@ nla_put_failure: return 1; } +#define IP_SET_HASH_WITH_PROTO + #define PF 4 #define HOST_MASK 32 #include @@ -135,12 +146,14 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport4_elem data = {}; + struct hash_ipport4_elem data = { .proto = h->proto }; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip4_port(skb, flags & IPSET_DIM_ONE_SRC, + &data.port, &data.proto)) return -EINVAL; + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + return adtfn(set, &data, GFP_ATOMIC, h->timeout); } @@ -148,7 +161,9 @@ static const struct nla_policy hash_ipport4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_U32 }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -157,9 +172,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport4_elem data = {}; + struct hash_ipport4_elem data = { .proto = h->proto }; u32 timeout = h->timeout; int ret; @@ -167,6 +181,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipport4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -177,6 +194,24 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; @@ -185,11 +220,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -200,6 +231,7 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->proto == y->proto && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size && x->chain_limit == y->chain_limit; @@ -210,13 +242,15 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b) struct hash_ipport6_elem { union nf_inet_addr ip; u16 port; - u16 match; + u8 proto; + u8 padding; }; struct hash_ipport6_telem { union nf_inet_addr ip; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -225,13 +259,14 @@ hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, const struct hash_ipport6_elem *ip2) { return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -239,7 +274,6 @@ hash_ipport6_data_copy(struct hash_ipport6_elem *dst, const struct hash_ipport6_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -256,7 +290,7 @@ hash_ipport6_data_swap(struct hash_ipport6_elem *dst, static inline void hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -265,6 +299,8 @@ hash_ipport6_data_list(struct sk_buff *skb, { NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -280,6 +316,8 @@ hash_ipport6_data_tlist(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -301,12 +339,14 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport6_elem data = {}; + struct hash_ipport6_elem data = { .proto = h->proto }; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip6_port(skb, flags & IPSET_DIM_ONE_SRC, + &data.port, &data.proto)) return -EINVAL; + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + return adtfn(set, &data, GFP_ATOMIC, h->timeout); } @@ -315,7 +355,9 @@ hash_ipport6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_BINARY, .len = sizeof(struct in6_addr) }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -325,13 +367,17 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport6_elem data = {}; + struct hash_ipport6_elem data = { .proto = h->proto }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ipport6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -343,13 +389,33 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ @@ -360,6 +426,7 @@ hash_ipport_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -367,8 +434,9 @@ static int hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; struct chash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -386,6 +454,12 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + if (tb[IPSET_ATTR_PROTO]) { + proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + if (!proto) + return -IPSET_ERR_INVALID_PROTO; + } + h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) return -ENOMEM; @@ -395,6 +469,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); + h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), diff --git a/kernel/ip_set_hash_ipportip.c b/kernel/ip_set_hash_ipportip.c index fbf2780..2c3cf9b 100644 --- a/kernel/ip_set_hash_ipportip.c +++ b/kernel/ip_set_hash_ipportip.c @@ -49,7 +49,8 @@ struct hash_ipportip4_elem { u32 ip; u32 ip2; u16 port; - u16 match; + u8 proto; + u8 padding; }; /* Member elements with timeout support */ @@ -57,7 +58,8 @@ struct hash_ipportip4_telem { u32 ip; u32 ip2; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -67,13 +69,14 @@ hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, { return ip1->ip == ip2->ip && ip1->ip2 == ip2->ip2 - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -81,7 +84,6 @@ hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst, const struct hash_ipportip4_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -98,7 +100,7 @@ hash_ipportip4_data_swap(struct hash_ipportip4_elem *dst, static inline void hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -108,6 +110,8 @@ hash_ipportip4_data_list(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -124,6 +128,8 @@ hash_ipportip4_data_tlist(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -133,6 +139,8 @@ nla_put_failure: return 1; } +#define IP_SET_HASH_WITH_PROTO + #define PF 4 #define HOST_MASK 32 #include @@ -143,11 +151,13 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip4_elem data = {}; + struct hash_ipportip4_elem data = { .proto = h->proto }; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); return adtfn(set, &data, GFP_ATOMIC, h->timeout); @@ -158,7 +168,9 @@ hash_ipportip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_U32 }, [IPSET_ATTR_IP2] = { .type = NLA_U32 }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -167,9 +179,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip4_elem data = {}; + struct hash_ipportip4_elem data = { .proto = h->proto }; u32 timeout = h->timeout; int ret; @@ -177,6 +188,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportip4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -192,6 +206,24 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; @@ -200,11 +232,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -215,6 +243,7 @@ hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->proto == y->proto && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size && x->chain_limit == y->chain_limit; @@ -226,14 +255,16 @@ struct hash_ipportip6_elem { union nf_inet_addr ip; union nf_inet_addr ip2; u16 port; - u16 match; + u8 proto; + u8 padding; }; struct hash_ipportip6_telem { union nf_inet_addr ip; union nf_inet_addr ip2; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -243,13 +274,14 @@ hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, { return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -257,7 +289,6 @@ hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst, const struct hash_ipportip6_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -274,7 +305,7 @@ hash_ipportip6_data_swap(struct hash_ipportip6_elem *dst, static inline void hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -284,6 +315,8 @@ hash_ipportip6_data_list(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -300,6 +333,8 @@ hash_ipportip6_data_tlist(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -321,11 +356,13 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip6_elem data = {}; + struct hash_ipportip6_elem data = { .proto = h->proto }; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); return adtfn(set, &data, GFP_ATOMIC, h->timeout); @@ -338,7 +375,9 @@ hash_ipportip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP2] = { .type = NLA_BINARY, .len = sizeof(struct in6_addr) }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -348,13 +387,17 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip6_elem data = {}; + struct hash_ipportip6_elem data = { .proto = h->proto }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ipportip6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -372,13 +415,33 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ @@ -389,6 +452,7 @@ hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -397,8 +461,9 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; struct chash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -416,6 +481,12 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + if (tb[IPSET_ATTR_PROTO]) { + proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + if (!proto) + return -IPSET_ERR_INVALID_PROTO; + } + h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) return -ENOMEM; @@ -425,6 +496,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); + h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), diff --git a/kernel/ip_set_hash_ipportnet.c b/kernel/ip_set_hash_ipportnet.c index dfe9348..4bc44f5 100644 --- a/kernel/ip_set_hash_ipportnet.c +++ b/kernel/ip_set_hash_ipportnet.c @@ -50,7 +50,7 @@ struct hash_ipportnet4_elem { u32 ip2; u16 port; u8 cidr; - u8 match; + u8 proto; }; /* Member elements with timeout support */ @@ -59,7 +59,7 @@ struct hash_ipportnet4_telem { u32 ip2; u16 port; u8 cidr; - u8 match; + u8 proto; unsigned long timeout; }; @@ -70,13 +70,14 @@ hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, return ip1->ip == ip2->ip && ip1->ip2 == ip2->ip2 && ip1->cidr == ip2->cidr - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -84,7 +85,6 @@ hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst, const struct hash_ipportnet4_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -108,7 +108,7 @@ hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) static inline void hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -119,6 +119,8 @@ hash_ipportnet4_data_list(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -136,6 +138,8 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -145,6 +149,7 @@ nla_put_failure: return 1; } +#define IP_SET_HASH_WITH_PROTO #define IP_SET_HASH_WITH_NETS #define PF 4 @@ -158,16 +163,19 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK }; - + { .cidr = h->nets[0].cidr || HOST_MASK, + .proto = h->proto }; + if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); data.ip2 &= NETMASK(data.cidr); @@ -180,7 +188,9 @@ hash_ipportnet4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP2] = { .type = NLA_U32 }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -189,9 +199,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; + struct hash_ipportnet4_elem data = { .cidr = HOST_MASK, + .proto = h->proto }; u32 timeout = h->timeout; int ret; @@ -199,6 +209,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportnet4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -222,6 +235,24 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; @@ -230,11 +261,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -245,6 +272,7 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->proto == y->proto && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size && x->chain_limit == y->chain_limit; @@ -257,7 +285,7 @@ struct hash_ipportnet6_elem { union nf_inet_addr ip2; u16 port; u8 cidr; - u8 match; + u8 proto; }; struct hash_ipportnet6_telem { @@ -265,7 +293,7 @@ struct hash_ipportnet6_telem { union nf_inet_addr ip2; u16 port; u8 cidr; - u8 match; + u8 proto; unsigned long timeout; }; @@ -276,13 +304,14 @@ hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && ip1->cidr == ip2->cidr - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -290,7 +319,6 @@ hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst, const struct hash_ipportnet6_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -307,7 +335,7 @@ hash_ipportnet6_data_swap(struct hash_ipportnet6_elem *dst, static inline void hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline void @@ -334,6 +362,8 @@ hash_ipportnet6_data_list(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -351,6 +381,8 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -373,16 +405,19 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK }; + { .cidr = h->nets[0].cidr || HOST_MASK, + .proto = h->proto }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); ip6_netmask(&data.ip2, data.cidr); @@ -397,7 +432,9 @@ hash_ipportnet6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { .len = sizeof(struct in6_addr) }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -407,13 +444,18 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; + struct hash_ipportnet6_elem data = { .cidr = HOST_MASK, + .proto = h->proto }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ipportnet6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -439,13 +481,33 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ @@ -456,6 +518,7 @@ hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -464,8 +527,9 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; struct chash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -483,6 +547,11 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + if (tb[IPSET_ATTR_PROTO]) { + proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + if (!proto) + return -IPSET_ERR_INVALID_PROTO; + } h = kzalloc(sizeof(*h) + sizeof(struct chash_nets) * (set->family == AF_INET ? 31 : 127), GFP_KERNEL); @@ -494,6 +563,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); + h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), diff --git a/kernel/ip_set_hash_net.c b/kernel/ip_set_hash_net.c index a8611c2..9be9e2c 100644 --- a/kernel/ip_set_hash_net.c +++ b/kernel/ip_set_hash_net.c @@ -46,13 +46,17 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b); /* Member elements without timeout */ struct hash_net4_elem { u32 ip; - u8 cidr; /* Not hashed, zero for null value */ + u16 padding0; + u8 padding1; + u8 cidr; }; /* Member elements with timeout support */ struct hash_net4_telem { u32 ip; - u8 cidr; /* Not hashed, zero for null value */ + u16 padding0; + u8 padding1; + u8 cidr; unsigned long timeout; }; @@ -60,10 +64,7 @@ static inline bool hash_net4_data_equal(const struct hash_net4_elem *ip1, const struct hash_net4_elem *ip2) { - /* We don't have to check the cidr equality - * because overlapping nets cannot be added to the set - */ - return ip1->ip == ip2->ip; + return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr; } static inline bool @@ -168,7 +169,6 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem data = { .cidr = HOST_MASK }; u32 timeout = h->timeout; @@ -178,6 +178,9 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_net4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -199,11 +202,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -223,12 +222,16 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b) struct hash_net6_elem { union nf_inet_addr ip; - u8 cidr; /* Not hashed */ + u16 padding0; + u8 padding1; + u8 cidr; }; struct hash_net6_telem { union nf_inet_addr ip; - u8 cidr; /* Not hashed */ + u16 padding0; + u8 padding1; + u8 cidr; unsigned long timeout; }; @@ -236,7 +239,8 @@ static inline bool hash_net6_data_equal(const struct hash_net6_elem *ip1, const struct hash_net6_elem *ip2) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 + && ip1->cidr == ip2->cidr; } static inline bool @@ -344,6 +348,7 @@ hash_net6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { .len = sizeof(struct in6_addr) }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -355,11 +360,15 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem data = { .cidr = HOST_MASK }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_net6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -380,7 +389,9 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ diff --git a/kernel/ip_set_list_set.c b/kernel/ip_set_list_set.c index ce6c4d1..94c5702 100644 --- a/kernel/ip_set_list_set.c +++ b/kernel/ip_set_list_set.c @@ -119,6 +119,7 @@ list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING, .len = IPSET_MAXNAMELEN }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, }; @@ -210,8 +211,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len, { struct list_set *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST, - with_timeout = with_timeout(map->timeout); + bool with_timeout = with_timeout(map->timeout); int before = 0; u32 timeout = map->timeout; ip_set_id_t id, refid = IPSET_INVALID_ID; @@ -224,6 +224,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len, list_set_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_NAME]) { id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); if (id == IPSET_INVALID_ID) @@ -334,12 +337,7 @@ finish: if (adt != IPSET_ADD || ret) ip_set_put_byindex(id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - return ret; - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static void @@ -565,7 +563,7 @@ list_set_create(struct ip_set *set, struct nlattr *head, int len, static struct ip_set_type list_set_type = { .name = "list:set", .protocol = IPSET_PROTOCOL, - .features = IPSET_TYPE_NAME, + .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, .revision = 0, diff --git a/lib/data.c b/lib/data.c index f8ff4a9..65ba209 100644 --- a/lib/data.c +++ b/lib/data.c @@ -66,6 +66,7 @@ struct ipset_data { struct { union nf_inet_addr ip2; uint8_t cidr2; + uint8_t proto; char ether[ETH_ALEN]; char name[IPSET_MAXNAMELEN]; char nameref[IPSET_MAXNAMELEN]; @@ -288,6 +289,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value) case IPSET_OPT_CIDR2: data->u.adt.cidr2 = *(const uint8_t *) value; break; + case IPSET_OPT_PROTO: + data->u.adt.proto = *(const uint8_t *) value; + break; /* Swap/rename */ case IPSET_OPT_SETNAME2: ipset_strncpy(data->u.setname2, value, IPSET_MAXNAMELEN); @@ -396,6 +400,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt) return &data->u.adt.ip2; case IPSET_OPT_CIDR2: return &data->u.adt.cidr2; + case IPSET_OPT_PROTO: + return &data->u.adt.proto; /* Swap/rename */ case IPSET_OPT_SETNAME2: return data->u.setname2; @@ -450,6 +456,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family) case IPSET_OPT_NETMASK: case IPSET_OPT_PROBES: case IPSET_OPT_RESIZE: + case IPSET_OPT_PROTO: return sizeof(uint8_t); case IPSET_OPT_ETHER: return ETH_ALEN; diff --git a/lib/parse.c b/lib/parse.c index a07168d..b1fecc7 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -31,6 +31,7 @@ #define range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR) #define elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR) #define name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR) +#define proto_separator(str) ipset_strchr(str, IPSET_PROTO_SEPARATOR) #define syntax_err(fmt, args...) \ ipset_err(session, "Syntax error: " fmt , ## args) @@ -280,6 +281,88 @@ error: return err; } +/** + * ipset_parse_proto - parse protocol name + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a protocol name. "any" is supported + * as a special protocol name for ipset itself. + * The parsed protocol are stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_proto(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + uint8_t proto = 0; + + assert(session); + assert(opt == IPSET_OPT_PROTO); + assert(str); + + if (STREQ(str, "any")) + proto = IPSET_IPPROTO_ANY; + else { + struct protoent *protoent = getprotobyname(str); + if (protoent == NULL) + return syntax_err("cannot parse '%s' as a protocol name", str); + proto = protoent->p_proto; + } + if (!proto || proto == IPSET_IPPROTO_TCPUDP) + return syntax_err("invalid protocol '%s'", str); + + return ipset_session_data_set(session, opt, &proto); +} + +/** + * ipset_parse_proto_port - parse (optional) protocol and a single port + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a protocol and port, separated by a colon. + * The protocol part is optional. + * The parsed protocol and port numbers are stored in the data + * blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_proto_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 = proto_separator(tmp); + if (a != NULL) { + /* proto:port */ + *a++ = '\0'; + err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp); + if (err) + goto error; + tmp = a; + } + err = ipset_parse_single_port(session, opt, tmp); + +error: + free(saved); + return err; +} + /** * ipset_parse_family - parse INET|INET6 family names * @session: session structure diff --git a/lib/print.c b/lib/print.c index d96e643..68f658a 100644 --- a/lib/print.c +++ b/lib/print.c @@ -435,6 +435,79 @@ ipset_print_port(char *buf, unsigned int len, return offset; } +/** + * ipset_print_proto - print protocol name + * @buf: printing buffer + * @len: length of available buffer space + * @data: data blob + * @opt: the option kind + * @env: environment flags + * + * Print protocol name to output buffer. + * + * Return lenght of printed string or error size. + */ +int +ipset_print_proto(char *buf, unsigned int len, + const struct ipset_data *data, enum ipset_opt opt, + uint8_t env UNUSED) +{ + struct protoent *protoent; + uint8_t proto; + + assert(buf); + assert(len > 0); + assert(data); + assert(opt == IPSET_OPT_PROTO); + + proto = *(uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO); + assert(proto); + + if (proto == IPSET_IPPROTO_ANY) + return snprintf(buf, len, "any"); + protoent = getprotobynumber(proto); + if (protoent) + return snprintf(buf, len, "%s", protoent->p_name); + + /* Should not happen */ + return snprintf(buf, len, "%u", proto); +} + +/** + * ipset_print_proto_port - print proto:port + * @buf: printing buffer + * @len: length of available buffer space + * @data: data blob + * @opt: the option kind + * @env: environment flags + * + * Print protocol and port to output buffer. + * + * Return lenght of printed string or error size. + */ +int +ipset_print_proto_port(char *buf, unsigned int len, + const struct ipset_data *data, enum ipset_opt opt, + uint8_t env UNUSED) +{ + int size, offset = 0; + + assert(buf); + assert(len > 0); + assert(data); + assert(opt == IPSET_OPT_PORT); + + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) { + size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env); + SNPRINTF_FAILURE(size, len, offset); + if (len < 2) + return -ENOSPC; + strcat(buf, ":"); + SNPRINTF_FAILURE(1, len, offset); + } + return ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env); +} + #define print_second(data) \ ipset_data_flags_test(data, \ IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER)) diff --git a/lib/session.c b/lib/session.c index 36716f0..fe1e178 100644 --- a/lib/session.c +++ b/lib/session.c @@ -35,6 +35,7 @@ struct ipset_session { enum ipset_cmd cmd; /* Current command */ uint32_t lineno; /* Current lineno in restore mode */ char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */ + const struct ipset_type *saved_type; /* Saved type */ struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */ uint8_t nestid; /* Current nest level */ bool version_checked; /* Version checked */ @@ -82,6 +83,20 @@ ipset_session_handle(const struct ipset_session *session) return session->handle; } +/** + * ipset_saved_type - return pointer to the saved type + * @session: session structure + * + * Returns the pointer to the saved type from the last ipset_cmd + * It is required to decode type-specific error codes in restore mode. + */ +const struct ipset_type * +ipset_saved_type(const struct ipset_session *session) +{ + assert(session); + return session->saved_type; +} + /* * Environment options */ @@ -327,6 +342,10 @@ const struct ipset_attr_policy create_attrs[] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_TIMEOUT, }, + [IPSET_ATTR_PROTO] = { + .type = MNL_TYPE_U8, + .opt = IPSET_OPT_PROTO, + }, [IPSET_ATTR_CADT_FLAGS] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_CADT_FLAGS, @@ -394,6 +413,10 @@ const struct ipset_attr_policy adt_attrs[] = { .type = MNL_TYPE_U16, .opt = IPSET_OPT_PORT_TO, }, + [IPSET_ATTR_PROTO] = { + .type = MNL_TYPE_U8, + .opt = IPSET_OPT_PROTO, + }, [IPSET_ATTR_TIMEOUT] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_TIMEOUT, @@ -1714,6 +1737,8 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno) goto cleanup; D("past: build_msg"); + /* We have to save the type for error handling */ + session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE); /* Save setname for the next possible aggregated restore line */ if (session->lineno != 0 && (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) { diff --git a/src/errcode.c b/src/errcode.c index c30ecb6..ae0d8c8 100644 --- a/src/errcode.c +++ b/src/errcode.c @@ -102,6 +102,10 @@ static const struct ipset_errcode_table hash_errcode_table[] = { "Hash is full, cannot add more elements" }, { IPSET_ERR_HASH_ELEM, 0, "Null-valued element, cannot be stored in a hash type of set" }, + { IPSET_ERR_INVALID_PROTO, 0, + "Invalid protocol specified" }, + { IPSET_ERR_MISSING_PROTO, 0, + "Protocol missing, but must be specified" }, { }, }; @@ -144,7 +148,7 @@ ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode) if (errcode >= IPSET_ERR_TYPE_SPECIFIC) { const struct ipset_type *type; - type = ipset_session_data_get(session, IPSET_OPT_TYPE); + type = ipset_saved_type(session); if (type) { if (MATCH_TYPENAME(type->name, "bitmap:")) table = bitmap_errcode_table; diff --git a/src/ipset.8 b/src/ipset.8 index 661d1b4..5b9e4ad 100644 --- a/src/ipset.8 +++ b/src/ipset.8 @@ -112,12 +112,12 @@ If the set has got reference(s), nothing is done and no set destroyed. \fBlist\fP [ \fISETNAME\fP ] List the header data and the entries for the specified set, or for all sets if none is given. The -\fB\-\-resolve\fP +\fB\-resolve\fP option can be used to force name lookups (which may be slow). When the -\fB\-\-sorted\fP +\fB\-sorted\fP option is given, the entries are listed sorted (if the given set type supports the operation). The option -\fB\-\-output\fR +\fB\-output\fR can be used to control the format of the listing: \fBplain\fR, \fBsave\fR or \fBxml\fR. The default is @@ -231,7 +231,7 @@ to 65536 entries. .PP \fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR } .PP -\fITEST\-ENTRY\fR := { \fIipaddr\fR } +\fITEST\-ENTRY\fR := \fIipaddr\fR .PP Mandatory \fBcreate\fR options: .TP @@ -262,13 +262,13 @@ The \fBbitmap:ip,mac\fR set type uses a memory range to store IPv4 and a MAC add .PP \fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] } +\fIADD\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR] .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] } +\fIDEL\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR] .PP -\fITEST\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] } +\fITEST\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR] .PP Mandatory options to use when creating a \fBbitmap:ip,mac\fR type of set: .TP @@ -307,7 +307,7 @@ and such a set can store up to 65536 ports. .PP \fIDEL\-ENTRY\fR := {\fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR } .PP -\fITEST\-ENTRY\fR := { \fIport\fR } +\fITEST\-ENTRY\fR := \fIport\fR .PP Mandatory options to use when creating a \fBbitmap:port\fR type of set: .TP @@ -328,13 +328,13 @@ if that is exhausted, the doubling of the hash is performed. .PP \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := { \fIipaddr\fR } +\fIADD\-ENTRY\fR := \fIipaddr\fR .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := { \fIipaddr\fR } +\fIDEL\-ENTRY\fR := \fIipaddr\fR .PP -\fITEST\-ENTRY\fR := { \fIipaddr\fR } +\fITEST\-ENTRY\fR := \fIipaddr\fR .PP For the \fBinet\fR family one can add or delete multiple entries by specifying a range or a network: @@ -378,13 +378,13 @@ if that is exhausted, the doubling of the hash is performed. .PP \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] } +\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR] .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] } +\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR] .PP -\fITEST\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] } +\fITEST\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR] .PP Optional \fBcreate\fR options: .TP @@ -427,15 +427,15 @@ The \fBhash:ip,port\fR set type uses a hash to store IP address and port pairs. In order to avoid clashes in the hash a limited number of chaining, and then if that is exhausted, the doubling of the hash is performed. .PP -\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBproto\fR \fIvalue\fR ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR } +\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR } +\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR .PP -\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR } +\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR .PP Optional \fBcreate\fR options: .TP @@ -443,6 +443,12 @@ Optional \fBcreate\fR options: The protocol family of the IP addresses to be stored in the set. The default is \fBinet\fR, i.e IPv4. .TP +\fBproto\fR \fIvalue\fR +The default protocol for the port to be stored in the set. If no protocol is specified, +then TCP/UDP ports are assumed as backward compatibility. The default protocol +also defines which kind of ports are to be added to the set when the \fBSET\fR +target is used. +.TP \fBhashsize\fR \fIvalue\fR The initial hash size for the set, default is 1024. The hash size must be a power of two, the kernel automatically rounds up non power of two hash sizes to the first @@ -451,30 +457,37 @@ correct value. \fBmaxelem\fR \fIvalue\fR The maximal number of elements which can be stored in the set, default 65536. .PP -The \fBhash:ip,port\fR type of sets require two \fBsrc\fR/\fBdst\fR parameters of -the \fBset\fR match and \fBSET\fR target kernel modules. +When adding, deleting, testing entries the port value is interpreted +for TCP and UDP only, for other protocols the port value currently is ignored and +zeroed out, but must be specified. The \fBhash:ip,port\fR type of sets require +two \fBsrc\fR/\fBdst\fR parameters of the \fBset\fR match and \fBSET\fR +target kernel modules. .PP Examples: .IP -ipset create foo hash:ip,port +ipset create foo hash:ip,port proto tcp .IP ipset add foo 192.168.1.1,80 .IP +ipset add foo 192.168.1.1,udp:53 +.IP +ipset add foo 192.168.1.1,ospf:0 +.IP ipset test foo 192.168.1.1,80 .SS hash:ip,port,ip The \fBhash:ip,port,ip\fR set type uses a hash to store IP address, port and IP address triples. In order to avoid clashes in the hash a limited number of chaining, and then if that is exhausted, the doubling of the hash is performed. .PP -\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBproto\fR \fIvalue\fR ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR } +\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR } +\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR .PP -\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR } +\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR .PP Optional \fBcreate\fR options: .TP @@ -482,6 +495,12 @@ Optional \fBcreate\fR options: The protocol family of the IP addresses to be stored in the set. The default is \fBinet\fR, i.e IPv4. .TP +\fBproto\fR \fIvalue\fR +The default protocol for the port to be stored in the set. If no protocol is specified, +then TCP/UDP ports are assumed as backward compatibility. The default protocol +also defines which kind of ports are to be added to the set when the \fBSET\fR +target is used. +.TP \fBhashsize\fR \fIvalue\fR The initial hash size for the set, default is 1024. The hash size must be a power of two, the kernel automatically rounds up non power of two hash sizes to the first @@ -490,8 +509,11 @@ correct value. \fBmaxelem\fR \fIvalue\fR The maximal number of elements which can be stored in the set, default 65536. .PP -The \fBhash:ip,port,ip\fR type of sets require three \fBsrc\fR/\fBdst\fR parameters of -the \fBset\fR match and \fBSET\fR target kernel modules. +When adding, deleting, testing entries the port value is interpreted +for TCP and UDP only, for other protocols the port value currently is ignored and +zeroed out, but must be specified. The \fBhash:ip,port,ip\fR type of sets require +three \fBsrc\fR/\fBdst\fR parameters of the \fBset\fR match and \fBSET\fR +target kernel modules. .PP Examples: .IP @@ -499,22 +521,22 @@ ipset create foo hash:ip,port,ip .IP ipset add foo 192.168.1.1,80,10.0.0.1 .IP -ipset test foo 192.168.1.1,80,10.0.0.1 +ipset test foo 192.168.1.1,udp:53,10.0.0.1 .SS hash:ip,port,net The \fBhash:ip,port,net\fR set type uses a hash to store IP address, port and IP network triples. In order to avoid clashes in the hash a limited number of chaining, and then if that is exhausted, the doubling of the hash is performed. .PP -\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBproto\fR \fIvalue\fR ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] } +\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] .PP \fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] .PP -\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] } +\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] .PP -\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] } +\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] .PP Optional \fBcreate\fR options: .TP @@ -522,6 +544,12 @@ Optional \fBcreate\fR options: The protocol family of the IP addresses to be stored in the set. The default is \fBinet\fR, i.e IPv4. .TP +\fBproto\fR \fIvalue\fR +The default protocol for the port to be stored in the set. If no protocol is specified, +then TCP/UDP ports are assumed as backward compatibility. The default protocol +also defines which kind of ports are to be added to the set when the \fBSET\fR +target is used. +.TP \fBhashsize\fR \fIvalue\fR The initial hash size for the set, default is 1024. The hash size must be a power of two, the kernel automatically rounds up non power of two hash sizes to the first @@ -531,7 +559,9 @@ correct value. The maximal number of elements which can be stored in the set, default 65536. .PP When adding/deleting/testing entries, if the cidr parameter is not specified, -then the host cidr value is assumed. +then the host cidr value is assumed. The port value is interpreted +for TCP and UDP only, for other protocols the port value currently is ignored and +zeroed out, but must be specified. .PP From the \fBset\fR netfilter match point of view a triple will be in a \fBhash:ip,port,net\fR type of set (when the first IP and the port match) if the second IP belongs to any of the netblocks added to the set. diff --git a/src/ipset.c b/src/ipset.c index 69fcd09..c613b24 100644 --- a/src/ipset.c +++ b/src/ipset.c @@ -202,7 +202,7 @@ restore(char *argv0) } static int -call_parser(int argc, char *argv[], const struct ipset_arg *args) +call_parser(int *argc, char *argv[], const struct ipset_arg *args) { int i = 1, ret = 0; const struct ipset_arg *arg; @@ -212,8 +212,8 @@ call_parser(int argc, char *argv[], const struct ipset_arg *args) if (!args) goto done; for (arg = args; arg->opt; arg++) { - for (i = 1; i < argc; ) { - D("argc: %u, i: %u: %s vs %s", argc, i, argv[i], arg->name[0]); + for (i = 1; i < *argc; ) { + D("argc: %u, i: %u: %s vs %s", *argc, i, argv[i], arg->name[0]); if (!(ipset_match_option(argv[i], arg->name))) { i++; continue; @@ -221,24 +221,24 @@ call_parser(int argc, char *argv[], const struct ipset_arg *args) optstr = argv[i]; /* Shift off matched option */ D("match %s", arg->name[0]); - ipset_shift_argv(&argc, argv, i); - D("argc: %u, i: %u", argc, i); + ipset_shift_argv(argc, argv, i); + D("argc: %u, i: %u", *argc, i); switch (arg->has_arg) { case IPSET_MANDATORY_ARG: - if (i + 1 > argc) + if (i + 1 > *argc) return exit_error(PARAMETER_PROBLEM, "Missing mandatory argument of option `%s'", arg->name[0]); /* Fall through */ case IPSET_OPTIONAL_ARG: - if (i + 1 <= argc) { + if (i + 1 <= *argc) { ret = ipset_call_parser(session, arg->parse, optstr, arg->opt, argv[i]); if (ret < 0) return ret; - ipset_shift_argv(&argc, argv, i); + ipset_shift_argv(argc, argv, i); break; } /* Fall through */ @@ -253,7 +253,7 @@ call_parser(int argc, char *argv[], const struct ipset_arg *args) } } done: - if (i < argc) + if (i < *argc) return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", argv[i]); @@ -458,6 +458,9 @@ parse_commandline(int argc, char *argv[]) } return exit_error(NO_PROBLEM, NULL); } + if (argc > 1) + return exit_error(PARAMETER_PROBLEM, + "No command specified: unknown argument %s", argv[1]); return exit_error(PARAMETER_PROBLEM, "No command specified."); case IPSET_CMD_VERSION: printf("%s v%s.\n", program_name, program_version); @@ -522,7 +525,7 @@ parse_commandline(int argc, char *argv[]) return handle_error(); /* Parse create options */ - ret = call_parser(argc, argv, type->args[IPSET_CREATE]); + ret = call_parser(&argc, argv, type->args[IPSET_CREATE]); if (ret < 0) return handle_error(); else if (ret) @@ -557,6 +560,9 @@ parse_commandline(int argc, char *argv[]) case IPSET_CMD_RESTORE: /* Restore mode */ + if (argc > 1) + return exit_error(PARAMETER_PROBLEM, + "Unknown argument %s", argv[1]); return restore(argv[0]); case IPSET_CMD_ADD: case IPSET_CMD_DEL: @@ -576,7 +582,7 @@ parse_commandline(int argc, char *argv[]) return handle_error(); /* Parse additional ADT options */ - ret = call_parser(argc, argv, type->args[cmd2cmd(cmd)]); + ret = call_parser(&argc, argv, type->args[cmd2cmd(cmd)]); if (ret < 0) return handle_error(); else if (ret) @@ -590,6 +596,9 @@ parse_commandline(int argc, char *argv[]) break; } + if (argc > 1) + return exit_error(PARAMETER_PROBLEM, + "Unknown argument %s", argv[1]); ret = ipset_cmd(session, cmd, restore_line); D("ret %d", ret); /* Special case for TEST and non-quiet mode */ diff --git a/src/ipset_hash_ipport.c b/src/ipset_hash_ipport.c index 4a9b8cf..94a8cc6 100644 --- a/src/ipset_hash_ipport.c +++ b/src/ipset_hash_ipport.c @@ -37,6 +37,10 @@ static const struct ipset_arg hash_ipport_create_args[] = { .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, + { .name = { "proto", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO, + .parse = ipset_parse_proto, .print = ipset_print_proto, + }, /* Backward compatibility */ { .name = { "probes", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, @@ -71,12 +75,12 @@ static const struct ipset_arg hash_ipport_add_args[] = { static const char hash_ipport_usage[] = "create SETNAME hash:ip,port\n" -" [family inet|inet6]\n" +" [family inet|inet6] [proto PROTO]\n" " [hashsize VALUE] [maxelem VALUE]\n" " [timeout VALUE]\n" -"add SETNAME IP,PORT [timeout VALUE]\n" -"del SETNAME IP,PORT\n" -"test SETNAME IP,PORT\n"; +"add SETNAME IP,[PROTO:]PORT [timeout VALUE]\n" +"del SETNAME IP,[PROTO:]PORT\n" +"test SETNAME IP,[PROTO:]PORT\n"; struct ipset_type ipset_hash_ipport0 = { .name = "hash:ip,port", @@ -91,8 +95,8 @@ struct ipset_type ipset_hash_ipport0 = { .opt = IPSET_OPT_IP }, [IPSET_DIM_TWO] = { - .parse = ipset_parse_single_port, - .print = ipset_print_port, + .parse = ipset_parse_proto_port, + .print = ipset_print_proto_port, .opt = IPSET_OPT_PORT }, }, @@ -112,14 +116,18 @@ struct ipset_type ipset_hash_ipport0 = { .full = { [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_PORT), + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO), [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_PORT), + | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO), }, .usage = hash_ipport_usage, diff --git a/src/ipset_hash_ipportip.c b/src/ipset_hash_ipportip.c index 299e362..dc121e4 100644 --- a/src/ipset_hash_ipportip.c +++ b/src/ipset_hash_ipportip.c @@ -37,6 +37,10 @@ static const struct ipset_arg hash_ipportip_create_args[] = { .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, + { .name = { "proto", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO, + .parse = ipset_parse_proto, .print = ipset_print_proto, + }, /* Backward compatibility */ { .name = { "probes", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, @@ -71,12 +75,12 @@ static const struct ipset_arg hash_ipportip_add_args[] = { static const char hash_ipportip_usage[] = "create SETNAME hash:ip,port,ip\n" -" [family inet|inet6]\n" +" [family inet|inet6] [proto PROTO]\n" " [hashsize VALUE] [maxelem VALUE]\n" " [timeout VALUE]\n" -"add SETNAME IP,PORT,IP [timeout VALUE]\n" -"del SETNAME IP,PORT,IP\n" -"test SETNAME IP,PORT,IP\n"; +"add SETNAME IP,[PROTO:]PORT,IP [timeout VALUE]\n" +"del SETNAME IP,[PROTO:]PORT,IP\n" +"test SETNAME IP,[PROTO:]PORT,IP\n"; struct ipset_type ipset_hash_ipportip0 = { .name = "hash:ip,port,ip", @@ -91,8 +95,8 @@ struct ipset_type ipset_hash_ipportip0 = { .opt = IPSET_OPT_IP }, [IPSET_DIM_TWO] = { - .parse = ipset_parse_single_port, - .print = ipset_print_port, + .parse = ipset_parse_proto_port, + .print = ipset_print_proto_port, .opt = IPSET_OPT_PORT }, [IPSET_DIM_THREE] = { @@ -120,16 +124,20 @@ struct ipset_type ipset_hash_ipportip0 = { .full = { [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IP2) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IP2), [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IP2), }, diff --git a/src/ipset_hash_ipportnet.c b/src/ipset_hash_ipportnet.c index 13a0487..a668c5e 100644 --- a/src/ipset_hash_ipportnet.c +++ b/src/ipset_hash_ipportnet.c @@ -37,6 +37,10 @@ static const struct ipset_arg hash_ipportnet_create_args[] = { .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, + { .name = { "proto", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO, + .parse = ipset_parse_proto, .print = ipset_print_proto, + }, /* Backward compatibility */ { .name = { "probes", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, @@ -71,12 +75,12 @@ static const struct ipset_arg hash_ipportnet_add_args[] = { static const char hash_ipportnet_usage[] = "create SETNAME hash:ip,port,net\n" -" [family inet|inet6]\n" +" [family inet|inet6] [proto PROTO]\n" " [hashsize VALUE] [maxelem VALUE]\n" " [timeout VALUE]\n" -"add SETNAME IP,PORT,IP[/CIDR] [timeout VALUE]\n" -"del SETNAME IP,PORT,IP[/CIDR]\n" -"test SETNAME IP,PORT,IP[/CIDR]\n"; +"add SETNAME IP,[PROTO:]PORT,IP[/CIDR] [timeout VALUE]\n" +"del SETNAME IP,[PROTO:]PORT,IP[/CIDR]\n" +"test SETNAME IP,[PROTO:]PORT,IP[/CIDR]\n"; struct ipset_type ipset_hash_ipportnet0 = { .name = "hash:ip,port,net", @@ -91,8 +95,8 @@ struct ipset_type ipset_hash_ipportnet0 = { .opt = IPSET_OPT_IP }, [IPSET_DIM_TWO] = { - .parse = ipset_parse_single_port, - .print = ipset_print_port, + .parse = ipset_parse_proto_port, + .print = ipset_print_proto_port, .opt = IPSET_OPT_PORT }, [IPSET_DIM_THREE] = { @@ -120,18 +124,22 @@ struct ipset_type ipset_hash_ipportnet0 = { .full = { [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IP2) | IPSET_FLAG(IPSET_OPT_CIDR2) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IP2) | IPSET_FLAG(IPSET_OPT_CIDR2), [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) + | IPSET_FLAG(IPSET_OPT_PROTO) | IPSET_FLAG(IPSET_OPT_IP2) | IPSET_FLAG(IPSET_OPT_CIDR2), }, diff --git a/tests/hash:ip,port,ip.t b/tests/hash:ip,port,ip.t index 3c74cd9..cc6783b 100644 --- a/tests/hash:ip,port,ip.t +++ b/tests/hash:ip,port,ip.t @@ -1,51 +1,51 @@ -# Range: Create a set with timeout +# Create a set with timeout 0 ipset create test hash:ip,port,ip timeout 5 -# Range: Add partly zero valued element +# Add partly zero valued element 0 ipset add test 2.0.0.1,0,0.0.0.0 -# Range: Test partly zero valued element +# Test partly zero valued element 0 ipset test test 2.0.0.1,0,0.0.0.0 -# Range: Delete party zero valued element +# Delete party zero valued element 0 ipset del test 2.0.0.1,0,0.0.0.0 -# Range: Add almost zero valued element +# Add almost zero valued element 0 ipset add test 2.0.0.1,0,0.0.0.1 -# Range: Test almost zero valued element +# Test almost zero valued element 0 ipset test test 2.0.0.1,0,0.0.0.1 -# Range: Delete almost zero valued element +# Delete almost zero valued element 0 ipset del test 2.0.0.1,0,0.0.0.1 -# Range: Add lower boundary +# Add lower boundary 0 ipset add test 2.0.0.1,5,1.1.1.1 -# Range: Add upper boundary +# Add upper boundary 0 ipset add test 2.1.0.0,128,2.2.2.2 -# Range: Test lower boundary +# Test lower boundary 0 ipset test test 2.0.0.1,5,1.1.1.1 -# Range: Test upper boundary +# Test upper boundary 0 ipset test test 2.1.0.0,128,2.2.2.2 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2.0.0.1,5,1.1.1.2 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2.0.0.1,6,1.1.1.1 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2.0.0.2,6,1.1.1.1 -# Range: Test value before lower boundary +# Test value before lower boundary 1 ipset test test 2.0.0.0,5,1.1.1.1 -# Range: Test value after upper boundary +# Test value after upper boundary 1 ipset test test 2.1.0.1,128,2.2.2.2 -# Range: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset add test 2.0.0.0,5,1.1.1.1 -# Range: Try to add value after upper boundary +# Try to add value after upper boundary 0 ipset add test 2.1.0.1,128,2.2.2.2 -# Range: List set +# List set 0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list0 && rm .foo -# Range: Sleep 6s so that elements can time out +# Sleep 6s so that elements can time out 0 sleep 6 -# Range: List set +# List set 0 ipset list test > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list1 && rm .foo -# Range: Flush test set +# Flush test set 0 ipset flush test -# Range: Delete test set +# Delete test set 0 ipset destroy test # eof diff --git a/tests/hash:ip,port.t b/tests/hash:ip,port.t index cb88f96..e3b292b 100644 --- a/tests/hash:ip,port.t +++ b/tests/hash:ip,port.t @@ -1,43 +1,65 @@ -# Range: Create a set with timeout +# Create a set with timeout 0 ipset create test hash:ip,port timeout 5 -# Range: Add partly zero valued element +# Add partly zero valued element 0 ipset add test 2.0.0.1,0 -# Range: Test partly zero valued element +# Test partly zero valued element 0 ipset test test 2.0.0.1,0 -# Range: Delete partly zero valued element +# Delete partly zero valued element 0 ipset del test 2.0.0.1,0 -# Range: Add lower boundary +# Add lower boundary 0 ipset add test 2.0.0.1,5 -# Range: Add upper boundary +# Add upper boundary 0 ipset add test 2.1.0.0,128 -# Range: Test lower boundary +# Test lower boundary 0 ipset test test 2.0.0.1,5 -# Range: Test upper boundary +# Test upper boundary 0 ipset test test 2.1.0.0,128 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2.0.0.1,4 -# Range: Delete value not added to the set +# Delete value not added to the set 1 ipset del test 2.0.0.1,6 -# Range: Test value before lower boundary +# Test value before lower boundary 1 ipset test test 2.0.0.0,5 -# Range: Test value after upper boundary +# Test value after upper boundary 1 ipset test test 2.1.0.1,128 -# Range: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset add test 2.0.0.0,5 -# Range: Try to add value after upper boundary +# Try to add value after upper boundary 0 ipset add test 2.1.0.1,128 -# Range: List set +# List set 0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list0 && rm .foo -# Range: Sleep 6s so that elements can time out +# Sleep 6s so that elements can time out 0 sleep 6 -# Range: List set +# List set 0 ipset list test > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list1 && rm .foo -# Range: Flush test set +# Flush test set 0 ipset flush test -# Range: Delete test set +# Delete test set +0 ipset destroy test +# Create a set with default TCP protocol +0 ipset create test hash:ip,port proto tcp +# Add element without specifying protocol +0 ipset add test 2.0.0.1,80 +# Add "same" element but with UDP protocol +0 ipset add test 2.0.0.1,udp:80 +# Test element without specifying protocol +0 ipset test test 2.0.0.1,80 +# Test element with TCP protocol +0 ipset test test 2.0.0.1,tcp:80 +# Test element with UDP protocol +0 ipset test test 2.0.0.1,udp:80 +# Add element with ospf +0 ipset add test 2.0.0.1,ospf:0 +# Test element with ospf +0 ipset test test 2.0.0.1,ospf:0 +# List set +0 ipset list test > .foo0 && ./sort.sh .foo0 +# Check listing +0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list2 && rm .foo +# Delete set 0 ipset destroy test # eof diff --git a/tests/hash:ip,port.t.list2 b/tests/hash:ip,port.t.list2 new file mode 100644 index 0000000..e8a9db0 --- /dev/null +++ b/tests/hash:ip,port.t.list2 @@ -0,0 +1,10 @@ +Name: test +Type: hash:ip,port +Header: family inet hashsize 1024 maxelem 65536 proto tcp +Size in memory: 8424 +References: 0 +Members: +2.0.0.1,ospf:0 +2.0.0.1,tcp:80 +2.0.0.1,udp:80 + diff --git a/tests/hash:ip6,port,ip6.t b/tests/hash:ip6,port,ip6.t index 84afdf9..712db33 100644 --- a/tests/hash:ip6,port,ip6.t +++ b/tests/hash:ip6,port,ip6.t @@ -1,51 +1,51 @@ -# Range: Create a set with timeout +# Create a set with timeout 0 ipset create test hash:ip,port,ip family inet6 timeout 5 -# Range: Add partly zero valued element +# Add partly zero valued element 0 ipset add test 2:0:0::1,0,0:0:0::0 -# Range: Test partly zero valued element +# Test partly zero valued element 0 ipset test test 2:0:0::1,0,0:0:0::0 -# Range: Delete party zero valued element +# Delete party zero valued element 0 ipset del test 2:0:0::1,0,0:0:0::0 -# Range: Add almost zero valued element +# Add almost zero valued element 0 ipset add test 2:0:0::1,0,0:0:0::1 -# Range: Test almost zero valued element +# Test almost zero valued element 0 ipset test test 2:0:0::1,0,0:0:0::1 -# Range: Delete almost zero valued element +# Delete almost zero valued element 0 ipset del test 2:0:0::1,0,0:0:0::1 -# Range: Add lower boundary +# Add lower boundary 0 ipset add test 2:0:0::1,5,1:1:1::1 -# Range: Add upper boundary +# Add upper boundary 0 ipset add test 2:1:0::0,128,2:2:2::2 -# Range: Test lower boundary +# Test lower boundary 0 ipset test test 2:0:0::1,5,1:1:1::1 -# Range: Test upper boundary +# Test upper boundary 0 ipset test test 2:1:0::0,128,2:2:2::2 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2:0:0::1,5,1:1:1::2 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2:0:0::1,6,1:1:1::1 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2:0:0::2,6,1:1:1::1 -# Range: Test value before lower boundary +# Test value before lower boundary 1 ipset test test 2:0:0::0,5,1:1:1::1 -# Range: Test value after upper boundary +# Test value after upper boundary 1 ipset test test 2:1:0::1,128,2:2:2::2 -# Range: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset add test 2:0:0::0,5,1:1:1::1 -# Range: Try to add value after upper boundary +# Try to add value after upper boundary 0 ipset add test 2:1:0::1,128,2:2:2::2 -# Range: List set +# List set 0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip6,port,ip6.t.list0 && rm .foo -# Range: Sleep 6s so that elements can time out +# Sleep 6s so that elements can time out 0 sleep 6 -# Range: List set +# List set 0 ipset list test > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip6,port,ip6.t.list1 && rm .foo -# Range: Flush test set +# Flush test set 0 ipset flush test -# Range: Delete test set +# Delete test set 0 ipset destroy test # eof diff --git a/tests/hash:ip6,port.t b/tests/hash:ip6,port.t index feb525c..5b00942 100644 --- a/tests/hash:ip6,port.t +++ b/tests/hash:ip6,port.t @@ -1,43 +1,43 @@ -# Range: Create a set with timeout +# Create a set with timeout 0 ipset create test hash:ip,port family inet6 timeout 5 -# Range: Add partly zero valued element +# Add partly zero valued element 0 ipset add test 2:0:0::1,0 -# Range: Test partly zero valued element +# Test partly zero valued element 0 ipset test test 2:0:0::1,0 -# Range: Delete partly zero valued element +# Delete partly zero valued element 0 ipset del test 2:0:0::1,0 -# Range: Add lower boundary +# Add lower boundary 0 ipset add test 2:0:0::1,5 -# Range: Add upper boundary +# Add upper boundary 0 ipset add test 2:1:0::0,128 -# Range: Test lower boundary +# Test lower boundary 0 ipset test test 2:0:0::1,5 -# Range: Test upper boundary +# Test upper boundary 0 ipset test test 2:1:0::0,128 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2:0:0::1,4 -# Range: Delete value not added to the set +# Delete value not added to the set 1 ipset del test 2:0:0::1,6 -# Range: Test value before lower boundary +# Test value before lower boundary 1 ipset test test 2:0:0::0,5 -# Range: Test value after upper boundary +# Test value after upper boundary 1 ipset test test 2:1:0::1,128 -# Range: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset add test 2:0:0::0,5 -# Range: Try to add value after upper boundary +# Try to add value after upper boundary 0 ipset add test 2:1:0::1,128 -# Range: List set +# List set 0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip6,port.t.list0 && rm .foo -# Range: Sleep 6s so that elements can time out +# Sleep 6s so that elements can time out 0 sleep 6 -# Range: List set +# List set 0 ipset list test > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:ip6,port.t.list1 && rm .foo -# Range: Flush test set +# Flush test set 0 ipset flush test -# Range: Delete test set +# Delete test set 0 ipset destroy test # eof diff --git a/tests/hash:net.t b/tests/hash:net.t index b46ac86..c27bb65 100644 --- a/tests/hash:net.t +++ b/tests/hash:net.t @@ -1,40 +1,40 @@ # Create a set with timeout 0 ipset create test nethash hashsize 128 timeout 6 -# Range: Add zero valued element +# Add zero valued element 1 ipset add test 0.0.0.0/0 -# Range: Test zero valued element +# Test zero valued element 1 ipset test test 0.0.0.0/0 -# Range: Delete zero valued element +# Delete zero valued element 1 ipset del test 0.0.0.0/0 -# Range: Try to add /0 +# Try to add /0 1 ipset add test 1.1.1.1/0 -# Range: Try to add /32 +# Try to add /32 0 ipset add test 1.1.1.1/32 -# Range: Add almost zero valued element +# Add almost zero valued element 0 ipset add test 0.0.0.0/1 -# Range: Test almost zero valued element +# Test almost zero valued element 0 ipset test test 0.0.0.0/1 -# Range: Delete almost zero valued element +# Delete almost zero valued element 0 ipset del test 0.0.0.0/1 -# Range: Test deleted element +# Test deleted element 1 ipset test test 0.0.0.0/1 -# Range: Delete element not added to the set +# Delete element not added to the set 1 ipset del test 0.0.0.0/1 -# Range: Add first random network +# Add first random network 0 ipset add test 2.0.0.1/24 -# Range: Add second random network +# Add second random network 0 ipset add test 192.168.68.69/27 -# Range: Test first random value +# Test first random value 0 ipset test test 2.0.0.255 -# Range: Test second random value +# Test second random value 0 ipset test test 192.168.68.95 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 2.0.1.0 -# Range: Try to add IP address +# Try to add IP address 0 ipset add test 2.0.0.1 -# Range: List set +# List set 0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:net.t.list0 && rm .foo # Sleep 6s so that element can time out 0 sleep 6 diff --git a/tests/hash:net6.t b/tests/hash:net6.t index e4246d9..5a575d1 100644 --- a/tests/hash:net6.t +++ b/tests/hash:net6.t @@ -1,40 +1,40 @@ # Create a set with timeout 0 ipset create test nethash family inet6 hashsize 128 timeout 6 -# Range: Add zero valued element +# Add zero valued element 1 ipset add test ::/0 -# Range: Test zero valued element +# Test zero valued element 1 ipset test test ::/0 -# Range: Delete zero valued element +# Delete zero valued element 1 ipset del test ::/0 -# Range: Try to add /0 +# Try to add /0 1 ipset add test 1:1:1::1/0 -# Range: Try to add /32 +# Try to add /32 0 ipset add test 1:1:1::1/128 -# Range: Add almost zero valued element +# Add almost zero valued element 0 ipset add test 0:0:0::0/1 -# Range: Test almost zero valued element +# Test almost zero valued element 0 ipset test test 0:0:0::0/1 -# Range: Delete almost zero valued element +# Delete almost zero valued element 0 ipset del test 0:0:0::0/1 -# Range: Test deleted element +# Test deleted element 1 ipset test test 0:0:0::0/1 -# Range: Delete element not added to the set +# Delete element not added to the set 1 ipset del test 0:0:0::0/1 -# Range: Add first random network +# Add first random network 0 ipset add test 2:0:0::1/24 -# Range: Add second random network +# Add second random network 0 ipset add test 192:168:68::69/27 -# Range: Test first random value +# Test first random value 0 ipset test test 2:0:0::255 -# Range: Test second random value +# Test second random value 0 ipset test test 192:168:68::95 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset test test 3:0:0::1 -# Range: Try to add IP address +# Try to add IP address 0 ipset add test 3:0:0::1 -# Range: List set +# List set 0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo hash:net6.t.list0 && rm .foo # Sleep 6s so that element can time out 0 sleep 6 diff --git a/tests/init.t b/tests/init.t index ac15311..3311856 100644 --- a/tests/init.t +++ b/tests/init.t @@ -4,4 +4,6 @@ 1 ipset -L test >/dev/null # Delete our test set: the testsuite fails if it exists 1 ipset -X test +# Check mandatory create arguments +2 ipset -N test # eof diff --git a/tests/ipmap.t b/tests/ipmap.t index 00c35e8..2373176 100644 --- a/tests/ipmap.t +++ b/tests/ipmap.t @@ -1,3 +1,11 @@ +# Range: Check syntax error: missing range/from-to +2 ipset -N test ipmap +# Range: Check syntax error: missing --from +2 ipset -N test ipmap --to 2.1.0.1 +# Range: Check syntax error: missing --to +2 ipset -N test ipmap --from 2.1.0.1 +# Range: Catch invalid IPv4 address +1 ipset -N test ipmap --from 2.0.0.256 --to 2.1.0.1 # Range: Try to create from an invalid range 1 ipset -N test ipmap --from 2.0.0.1 --to 2.1.0.1 # Range: Create a set from a valid range @@ -28,6 +36,16 @@ 0 ipset -D test 2.0.0.128 # Range: Add a range of elements 0 ipset -A test 2.0.0.128-2.0.0.131 +# Range: Save set +0 ipset -S test > ipmap.t.restore +# Range: Destroy set +0 ipset -X test +# Range: Restore set and catch error +1 sed 's/2.0.0.131/222.0.0.131/' < ipmap.t.restore | ipset -R +# Range: Destroy set +0 ipset -X test +# Range: Restore set +0 ipset -R < ipmap.t.restore && rm ipmap.t.restore # Range: List set 0 ipset -L test > .foo # Range: Check listing diff --git a/tests/ipporthash.t b/tests/ipporthash.t index dc35bcd..9847830 100644 --- a/tests/ipporthash.t +++ b/tests/ipporthash.t @@ -1,71 +1,71 @@ -# Range: Create a set from a range (range ignored) +# Create a set from a range (range ignored) 0 ipset -N test ipporthash --from 2.0.0.1 --to 2.1.0.0 -# Range: Destroy set +# Destroy set 0 ipset -X test -# Range: Create a set +# Create a set 0 ipset -N test ipporthash -# Range: Add partly zero valued element +# Add partly zero valued element 0 ipset -A test 2.0.0.1,0 -# Range: Test partly zero valued element +# Test partly zero valued element 0 ipset -T test 2.0.0.1,0 -# Range: Delete partly zero valued element +# Delete partly zero valued element 0 ipset -D test 2.0.0.1,0 -# Range: Add lower boundary +# Add lower boundary 0 ipset -A test 2.0.0.1,5 -# Range: Add upper boundary +# Add upper boundary 0 ipset -A test 2.1.0.0,128 -# Range: Test lower boundary +# Test lower boundary 0 ipset -T test 2.0.0.1,5 -# Range: Test upper boundary +# Test upper boundary 0 ipset -T test 2.1.0.0,128 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.0.1,4 -# Range: Delete value not added to the set +# Delete value not added to the set 1 ipset -D test 2.0.0.1,6 -# Range: Test value before lower boundary +# Test value before lower boundary 1 ipset -T test 2.0.0.0,5 -# Range: Test value after upper boundary +# Test value after upper boundary 1 ipset -T test 2.1.0.1,128 -# Range: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset -A test 2.0.0.0,5 -# Range: Try to add value after upper boundary +# Try to add value after upper boundary 0 ipset -A test 2.1.0.1,128 -# Range: List set +# List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo ipporthash.t.list0 && rm .foo -# Range: Flush test set +# Flush test set 0 ipset -F test -# Range: Delete test set +# Delete test set 0 ipset -X test -# Network: Create a set from a network (network ignored) +# Create a set from a network (network ignored) 0 ipset -N test ipporthash --network 2.0.0.0/16 -# Network: Add lower boundary +# Add lower boundary 0 ipset -A test 2.0.0.0,5 -# Network: Add upper boundary +# Add upper boundary 0 ipset -A test 2.0.255.255,128 -# Network: Test lower boundary +# Test lower boundary 0 ipset -T test 2.0.0.0,5 -# Network: Test upper boundary +# Test upper boundary 0 ipset -T test 2.0.255.255,128 -# Network: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.0.0,4 -# Network: Delete value not added to the set +# Delete value not added to the set 1 ipset -D test 2.0.0.0,6 -# Network: Test value before lower boundary +# Test value before lower boundary 1 ipset -T test 1.255.255.255,5 -# Network: Test value after upper boundary +# Test value after upper boundary 1 ipset -T test 2.1.0.0,128 -# Network: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset -A test 1.255.255.255,5 -# Network: Try to add value after upper boundary +# Try to add value after upper boundary 0 ipset -A test 2.1.0.0,128 -# Network: List set +# List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 -# Network: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo ipporthash.t.list1 && rm .foo -# Network: Flush test set +# Flush test set 0 ipset -F test -# Network: Delete test set +# Delete test set 0 ipset -X test # eof diff --git a/tests/ipportiphash.t b/tests/ipportiphash.t index 3f61947..f6e79cc 100644 --- a/tests/ipportiphash.t +++ b/tests/ipportiphash.t @@ -1,81 +1,81 @@ -# Range: Create a set from a range (range ignored) +# Create a set from a range (range ignored) 0 ipset -N test ipportiphash --from 2.0.0.1 --to 2.1.0.0 -# Range: Destroy set +# Destroy set 0 ipset -X test -# Range: Create a set +# Create a set 0 ipset -N test ipportiphash -# Range: Add partly zero valued element +# Add partly zero valued element 0 ipset -A test 2.0.0.1,0,0.0.0.0 -# Range: Test partly zero valued element +# Test partly zero valued element 0 ipset -T test 2.0.0.1,0,0.0.0.0 -# Range: Delete party zero valued element +# Delete party zero valued element 0 ipset -D test 2.0.0.1,0,0.0.0.0 -# Range: Add almost zero valued element +# Add almost zero valued element 0 ipset -A test 2.0.0.1,0,0.0.0.1 -# Range: Test almost zero valued element +# Test almost zero valued element 0 ipset -T test 2.0.0.1,0,0.0.0.1 -# Range: Delete almost zero valued element +# Delete almost zero valued element 0 ipset -D test 2.0.0.1,0,0.0.0.1 -# Range: Add lower boundary +# Add lower boundary 0 ipset -A test 2.0.0.1,5,1.1.1.1 -# Range: Add upper boundary +# Add upper boundary 0 ipset -A test 2.1.0.0,128,2.2.2.2 -# Range: Test lower boundary +# Test lower boundary 0 ipset -T test 2.0.0.1,5,1.1.1.1 -# Range: Test upper boundary +# Test upper boundary 0 ipset -T test 2.1.0.0,128,2.2.2.2 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.0.1,5,1.1.1.2 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.0.1,6,1.1.1.1 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.0.2,6,1.1.1.1 -# Range: Test value before lower boundary +# Test value before lower boundary 1 ipset -T test 2.0.0.0,5,1.1.1.1 -# Range: Test value after upper boundary +# Test value after upper boundary 1 ipset -T test 2.1.0.1,128,2.2.2.2 -# Range: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset -A test 2.0.0.0,5,1.1.1.1 -# Range: Try to add value after upper boundary +# Try to add value after upper boundary 0 ipset -A test 2.1.0.1,128,2.2.2.2 -# Range: List set +# List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo ipportiphash.t.list0 && rm .foo -# Range: Flush test set +# Flush test set 0 ipset -F test -# Range: Delete test set +# Delete test set 0 ipset -X test -# Network: Create a set from a valid network (network ignored) +# Create a set from a valid network (network ignored) 0 ipset -N test ipportiphash --network 2.0.0.0/16 -# Network: Add lower boundary +# Add lower boundary 0 ipset -A test 2.0.0.0,5,1.1.1.1 -# Network: Add upper boundary +# Add upper boundary 0 ipset -A test 2.0.255.255,128,2.2.2.2 -# Network: Test lower boundary +# Test lower boundary 0 ipset -T test 2.0.0.0,5,1.1.1.1 -# Network: Test upper boundary +# Test upper boundary 0 ipset -T test 2.0.255.255,128,2.2.2.2 -# Network: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.0.0,5,1.1.1.2 -# Network: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.0.0,6,1.1.1.1 -# Network: Test value before lower boundary +# Test value before lower boundary 1 ipset -T test 1.255.255.255,5,1.1.1.1 -# Network: Test value after upper boundary +# Test value after upper boundary 1 ipset -T test 2.1.0.0,128,2.2.2.2 -# Network: Try to add value before lower boundary +# Try to add value before lower boundary 0 ipset -A test 1.255.255.255,5,1.1.1.1 -# Network: Try to test value before lower boundary +# Try to test value before lower boundary 0 ipset -T test 1.255.255.255,5,1.1.1.1 -# Network: Try to del value before lower boundary +# Try to del value before lower boundary 0 ipset -D test 1.255.255.255,5,1.1.1.1 -# Network: List set +# List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 -# Network: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo ipportiphash.t.list1 && rm .foo -# Network: Flush test set +# Flush test set 0 ipset -F test -# Network: Delete test set +# Delete test set 0 ipset -X test # eof diff --git a/tests/nethash.t b/tests/nethash.t index 400e110..537c0db 100644 --- a/tests/nethash.t +++ b/tests/nethash.t @@ -1,40 +1,40 @@ # Create a set 0 ipset -N test nethash --hashsize 128 -# Range: Add zero valued element +# Add zero valued element 1 ipset -A test 0.0.0.0/0 -# Range: Test zero valued element +# Test zero valued element 1 ipset -T test 0.0.0.0/0 -# Range: Delete zero valued element +# Delete zero valued element 1 ipset -D test 0.0.0.0/0 -# Range: Try to add /0 +# Try to add /0 1 ipset -A test 1.1.1.1/0 -# Range: Try to add /32 +# Try to add /32 0 ipset -A test 1.1.1.1/32 -# Range: Add almost zero valued element +# Add almost zero valued element 0 ipset -A test 0.0.0.0/1 -# Range: Test almost zero valued element +# Test almost zero valued element 0 ipset -T test 0.0.0.0/1 -# Range: Delete almost zero valued element +# Delete almost zero valued element 0 ipset -D test 0.0.0.0/1 -# Range: Test deleted element +# Test deleted element 1 ipset -T test 0.0.0.0/1 -# Range: Delete element not added to the set +# Delete element not added to the set 1 ipset -D test 0.0.0.0/1 -# Range: Add first random network +# Add first random network 0 ipset -A test 2.0.0.1/24 -# Range: Add second random network +# Add second random network 0 ipset -A test 192.168.68.69/27 -# Range: Test first random value +# Test first random value 0 ipset -T test 2.0.0.255 -# Range: Test second random value +# Test second random value 0 ipset -T test 192.168.68.95 -# Range: Test value not added to the set +# Test value not added to the set 1 ipset -T test 2.0.1.0 -# Range: Try to add IP address +# Try to add IP address 0 ipset -A test 2.0.0.1 -# Range: List set +# List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 -# Range: Check listing +# Check listing 0 diff -I 'Size in memory.*' .foo nethash.t.list0 && rm .foo # Flush test set 0 ipset -F test diff --git a/tests/setlist.t b/tests/setlist.t index f80f5ae..d673e8e 100644 --- a/tests/setlist.t +++ b/tests/setlist.t @@ -1,55 +1,55 @@ -# Setlist: Create dummy set +# Create dummy set 0 ipset -N dummy list:set -# Setlist: Create base set foo +# Create base set foo 0 ipset -N foo ipmap --from 2.0.0.1 --to 2.1.0.0 -# Setlist: Create base set bar +# Create base set bar 0 ipset -N bar iphash -# Setlist: Create setlist kind of set +# Create setlist kind of set 0 ipset -N test setlist -# Setlist: Swap test and dumy sets +# Swap test and dumy sets 0 ipset -W test dummy -# Setlist: Destroy dummy set +# Destroy dummy set 0 ipset -X dummy -# Setlist: Add foo set to setlist +# Add foo set to setlist 0 ipset -A test foo -# Setlist: Test foo set in setlist +# Test foo set in setlist 0 ipset -T test foo -# Setlist: Test nonexistent set in setlist +# Test nonexistent set in setlist 1 ipset -T test nonexistent -# Setlist: Try to delete foo set +# Try to delete foo set 1 ipset -X foo -# Setlist: Add bar set to setlist, after foo +# Add bar set to setlist, after foo 0 ipset -A test bar -# Setlist: Test bar,after,foo +# Test bar,after,foo 0 ipset -T test bar,after,foo -# Setlist: Test foo,before,bar +# Test foo,before,bar 0 ipset -T test foo,before,bar -# Setlist: Test bar,before,foo +# Test bar,before,foo 1 ipset -T test bar,before,foo -# Setlist: Test foo,after,bar +# Test foo,after,bar 1 ipset -T test foo,after,bar -# Setlist: Save sets +# Save sets 0 ipset -S > setlist.t.restore -# Setlist: Delete bar,before,foo +# Delete bar,before,foo 1 ipset -D test bar,before,foo -# Setlist: Delete foo,after,bar +# Delete foo,after,bar 1 ipset -D test foo,after,bar -# Setlist: Delete bar,after,foo +# Delete bar,after,foo 0 ipset -D test bar,after,foo -# Setlist: Flush test set +# Flush test set 0 ipset -F test -# Setlist: Delete test set +# Delete test set 0 ipset -X test -# Setlist: Delete all sets +# Delete all sets 0 ipset -X -# Setlist: Restore saved sets +# Restore saved sets 0 ipset -R < setlist.t.restore -# Setlist: List set +# List set 0 ipset -L test > .foo -# Setlist: Check listing +# Check listing 0 diff .foo setlist.t.list0 && rm .foo -# Setlist: Flush all sets +# Flush all sets 0 ipset -F -# Setlist: Delete all sets +# Delete all sets 0 ipset -X && rm setlist.t.restore # eof -- cgit v1.2.3