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 --- 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 + 4 files changed, 116 insertions(+), 77 deletions(-) (limited to 'kernel/include') 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__ -- cgit v1.2.3