Re: [PATCH v2 2/5] ipset: add "inner" flag implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 17 Jun 2013, Dash Four wrote:

> This patch implements "inner" flag option in set iptables match,
> allowing matching based on the properties (source/destination IP address,
> protocol, port and so on) of the original (inner) connection in the event
> of the following ICMP[v4,v6] messages:
> 
> ICMPv4 destination-unreachable (code 3);
> ICMPv4 source-quench (code 4);
> ICMPv4 time-exceeded (code 11);
> ICMPv6 destination-unreachable (code 1);
> ICMPv6 packet-too-big (code 2);
> ICMPv6 time-exceeded (code 3);
> 
> Revision history:

The patchset is a good step forward.

I have no objections against the other patches, however I have to comment 
this one.
 
> v1 * initial revision
> v2 * redundant code removed;
>    * added a new header file (ip_set_icmp.h) with 2 inline functions,
>      allowing access to the internal icmp header properties;
>    * removed ip[46]inneraddr[ptr]functions as they are no longer needed
>    * added new ipv[46]addr[ptr] and ip_set_get*port functions, the old
>      functions are still preserved for backwards compatibility

Get rid of the compatibility functions. Or keep the original function 
names with the extended arguments: there's no stable API.
 
> Signed-off-by: Dash Four <mr.dash.four@xxxxxxxxxxxxxx>
> ---
>  kernel/include/linux/netfilter/ipset/ip_set.h      |   66 ++++++++++++++
>  .../include/linux/netfilter/ipset/ip_set_getport.h |   16 ++++
>  kernel/include/linux/netfilter/ipset/ip_set_icmp.h |   91
> ++++++++++++++++++++
>  kernel/include/uapi/linux/netfilter/ipset/ip_set.h |    2 +
>  kernel/net/netfilter/ipset/ip_set_getport.c        |   78 +++++++++++++----
>  5 files changed, 238 insertions(+), 15 deletions(-)
>  create mode 100644 kernel/include/linux/netfilter/ipset/ip_set_icmp.h
> 
> diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h
> b/kernel/include/linux/netfilter/ipset/ip_set.h
> index 8499e25..5b6ef72 100644
> --- a/kernel/include/linux/netfilter/ipset/ip_set.h
> +++ b/kernel/include/linux/netfilter/ipset/ip_set.h
> @@ -17,9 +17,13 @@
>  #include <linux/netfilter/x_tables.h>
>  #include <linux/stringify.h>
>  #include <linux/vmalloc.h>
> +#include <net/ip.h>
> +#include <net/ipv6.h>
> +#include <net/icmp.h>
>  #include <net/netlink.h>
>  #include <uapi/linux/netfilter/ipset/ip_set.h>
>  #include <linux/netfilter/ipset/ip_set_compat.h>
> +#include <linux/netfilter/ipset/ip_set_icmp.h>

I don't really like the new header file. ip[v]4|6addrptr are generic 
functions (from ipset point of view) and getting the inner header is too, 
so those deserve to be in the ipset core.

>  #define _IP_SET_MODULE_DESC(a, b, c)		\
>  	MODULE_DESCRIPTION(a " type of IP sets, revisions " b "-" c)
> @@ -361,6 +365,25 @@ static inline int nla_put_ipaddr6(struct sk_buff *skb,
> int type,
>  }
> 
>  /* Get address from skbuff */
> +static inline bool
> +ipv4addrptr(const struct sk_buff *skb, bool inner, bool src, __be32 *addr)
> +{
> +	struct iphdr *ih = ip_hdr(skb);
> +	unsigned int protooff = ip_hdrlen(skb);
> +
> +	if (ih == NULL ||

Why do you have to check the IP header?

> +	    (inner &&
> +	     (ih->protocol != IPPROTO_ICMP ||
> +	      !ip_set_get_icmpv4_inner_hdr(skb, &protooff, &ih))))
> +			goto err;

Move the protocol checking into ip_set_get_icmpv4_inner_hdr. Also, I 
suggested the name ip_set_get[_ip4]_inner_hdr because I want to extend it 
to support IPIP and GRE too. By moving the protocol checking into the 
function and using a not so specific name, those make possible the 
extension straightforward. Also, there's no need for the goto here: 
there's no other error path.

> +	*addr = src ? ih->saddr : ih->daddr;
> +	return true;
> +
> +err:
> +	return false;
> +}
> +
>  static inline __be32
>  ip4addr(const struct sk_buff *skb, bool src)
>  {
> @@ -373,12 +396,55 @@ ip4addrptr(const struct sk_buff *skb, bool src, __be32
> *addr)
>  	*addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
>  }
> 
> +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
> +static inline bool
> +ipv6addrptr(const struct sk_buff *skb, bool inner, bool src,
> +	    struct in6_addr *addr)
> +{
> +	struct ipv6hdr *ih = ipv6_hdr(skb);
> +
> +	if (ih == NULL)
> +		goto err;

The same comment as above.

> +	if (inner) {
> +		unsigned int protooff;
> +		u8 nexthdr = ih->nexthdr;
> +		__be16 frag_off;
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
> +		protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
> +					    &nexthdr, &frag_off);
> +#else
> +		protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
> +					    &nexthdr);
> +#endif
> +		if (protooff < 0 || nexthdr != IPPROTO_ICMPV6 ||
> +		    !ip_set_get_icmpv6_inner_hdr(skb, &protooff, &ih))
> +			goto err;
> +	}

Move the whole code in the braces into ip_set_get_ipv6_inner_hdr, for the 
same reason as above.

> +	memcpy(addr, src ? &ih->saddr : &ih->daddr, sizeof(*addr));
> +	return true;
> +
> +err:
> +	return false;
> +}
> +
>  static inline void
>  ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr)
>  {
>  	memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr,
>  	       sizeof(*addr));
>  }
> +#else
> +static inline bool
> +ipv6addrptr(const struct sk_buff *skb, bool inner, bool src,
> +	    struct in6_addr *addr)
> +{
> +	return false;
> +}
> +
> +static inline void
> +ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr) { ; }
> +#endif
> 
>  /* Calculate the bytes required to store the inclusive range of a-b */
>  static inline int
> diff --git a/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> b/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> index 90d0930..8cdc177 100644
> --- a/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> +++ b/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> @@ -1,13 +1,26 @@
>  #ifndef _IP_SET_GETPORT_H
>  #define _IP_SET_GETPORT_H
> 
> +extern bool ip_set_get_ipv4_port(const struct sk_buff *skb, bool inner,
> +				 bool src, __be16 *port, u8 *proto);
> +
>  extern bool ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
>  				__be16 *port, u8 *proto);
> 
>  #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
> +extern bool ip_set_get_ipv6_port(const struct sk_buff *skb, bool inner,
> +				 bool src, __be16 *port, u8 *proto);
> +
>  extern bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
>  				__be16 *port, u8 *proto);
>  #else
> +static inline bool ip_set_get_ipv6_port(const struct sk_buff *skb,
> +					bool inner, bool src,
> +					__be16 *port, u8 *proto)
> +{
> +	return false;
> +}
> +
>  static inline bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
>  				       __be16 *port, u8 *proto)
>  {
> @@ -15,6 +28,9 @@ static inline bool ip_set_get_ip6_port(const struct sk_buff
> *skb, bool src,
>  }
>  #endif
> 
> +extern bool ip_set_get_ipv_port(const struct sk_buff *skb, u8 pf, bool inner,
> +				bool src, __be16 *port);
> +
>  extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src,
>  				__be16 *port);
> 
> diff --git a/kernel/include/linux/netfilter/ipset/ip_set_icmp.h
> b/kernel/include/linux/netfilter/ipset/ip_set_icmp.h
> new file mode 100644
> index 0000000..e116d28
> --- /dev/null
> +++ b/kernel/include/linux/netfilter/ipset/ip_set_icmp.h
> @@ -0,0 +1,91 @@
> +#ifndef _IP_SET_ICMP_H
> +#define _IP_SET_ICMP_H
> +
> +#include <linux/ip.h>
> +#include <linux/ipv6.h>
> +#include <linux/icmp.h>
> +
> +static inline
> +bool ip_set_get_icmpv4_inner_hdr(const struct sk_buff *skb,
> +				 unsigned int *offset,
> +				 struct iphdr **ih)
> +{
> +	u8 type;
> +	struct iphdr _iph;
> +	struct icmphdr _icmph;
> +	struct iphdr *iph;
> +	const struct icmphdr *ich;
> +	/* RFC 1122: 3.2.2: req'd len: IP header + 8 bytes of inner header */
> +	static const size_t req_len = sizeof(struct iphdr) + 8;
> +
> +	if (offset == NULL || ih == NULL)
> +		goto err;
> +
> +	ich = skb_header_pointer(skb, *offset, sizeof(_icmph), &_icmph);
> +	if (ich == NULL ||
> +	    (ich->type <= NR_ICMP_TYPES && skb->len - *offset < req_len))
> +		goto err;
> +
> +	type = ich->type;
> +	if (type == ICMP_DEST_UNREACH ||
> +	    type == ICMP_SOURCE_QUENCH ||
> +	    type == ICMP_TIME_EXCEEDED) {
> +		*offset += sizeof(_icmph);
> +		iph = skb_header_pointer(skb, *offset, sizeof(_iph), &_iph);
> +		if (iph == NULL || ntohs(iph->frag_off) & IP_OFFSET)
> +			goto err;
> +
> +		*ih = iph;
> +		return true;
> +	}
> +
> +err:
> +	return false;
> +}
> +
> +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
> +static inline
> +bool ip_set_get_icmpv6_inner_hdr(const struct sk_buff *skb,
> +				 unsigned int *offset,
> +				 struct ipv6hdr **ih)
> +{
> +	u8 type;
> +	const struct icmp6hdr *ic;
> +	struct icmp6hdr _icmp6h;
> +	struct ipv6hdr _ip6h;
> +	struct ipv6hdr *iph;
> +
> +	if (offset == NULL || ih == NULL)
> +		goto err;
> +
> +	ic = skb_header_pointer(skb, *offset, sizeof(_icmp6h), &_icmp6h);
> +	if (ic == NULL)
> +		goto err;
> +
> +	type = ic->icmp6_type;
> +	if (type == ICMPV6_DEST_UNREACH ||
> +	    type == ICMPV6_PKT_TOOBIG ||
> +	    type == ICMPV6_TIME_EXCEED) {
> +		*offset += sizeof(_icmp6h);
> +		iph = skb_header_pointer(skb, *offset, sizeof(_ip6h), &_ip6h);
> +		if (iph == NULL)
> +			goto err;
> +
> +		*ih = iph;
> +		return true;
> +	}
> +
> +err:
> +	return false;
> +}
> +#else
> +static inline
> +bool ip_set_get_icmpv6_inner_hdr(const struct sk_buff *skb,
> +				 unsigned int offset,
> +				 struct ipv6hdr **ih)
> +{
> +	return false;
> +}
> +#endif
> +
> +#endif /*_IP_SET_ICMP_H*/

Move the functions into ip_set_core.c as non-inline ones and export them.

> diff --git a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
> b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
> index 8024cdf..e9e6586 100644
> --- a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
> +++ b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
> @@ -161,6 +161,8 @@ enum ipset_cmd_flags {
>  		(1 << IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE),
>  	IPSET_FLAG_BIT_MATCH_COUNTERS = 5,
>  	IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS),
> +	IPSET_FLAG_BIT_INNER = 6,
> +	IPSET_FLAG_INNER = (1 << IPSET_FLAG_BIT_INNER),
>  	IPSET_FLAG_BIT_RETURN_NOMATCH = 7,
>  	IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH),
>  	IPSET_FLAG_CMD_MAX = 15,
> diff --git a/kernel/net/netfilter/ipset/ip_set_getport.c
> b/kernel/net/netfilter/ipset/ip_set_getport.c
> index a0d96eb..73ccfb3 100644
> --- a/kernel/net/netfilter/ipset/ip_set_getport.c
> +++ b/kernel/net/netfilter/ipset/ip_set_getport.c
> @@ -19,7 +19,9 @@
>  #include <linux/netfilter_ipv6/ip6_tables.h>
>  #include <net/ip.h>
>  #include <net/ipv6.h>
> +#include <net/icmp.h>
> 
> +#include <linux/netfilter/ipset/ip_set_icmp.h>
>  #include <linux/netfilter/ipset/ip_set_getport.h>
> 
>  /* We must handle non-linear skbs */
> @@ -97,10 +99,10 @@ get_port(const struct sk_buff *skb, int protocol, unsigned
> int protooff,
>  }
> 
>  bool
> -ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
> -		    __be16 *port, u8 *proto)
> +ip_set_get_ipv4_port(const struct sk_buff *skb, bool inner, bool src,
> +		     __be16 *port, u8 *proto)
>  {
> -	const struct iphdr *iph = ip_hdr(skb);
> +	struct iphdr *iph = ip_hdr(skb);
>  	unsigned int protooff = ip_hdrlen(skb);
>  	int protocol = iph->protocol;
> 
> @@ -116,54 +118,93 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
>  		case IPPROTO_UDPLITE:
>  		case IPPROTO_ICMP:
>  			/* Port info not available for fragment offset > 0 */
> -			return false;
> +			goto err;
>  		default:
>  			/* Other protocols doesn't have ports,
>  			   so we can match fragments */
>  			*proto = protocol;
>  			return true;
>  		}
> +	if (inner) {
> +		if (protocol != IPPROTO_ICMP ||
> +		    !ip_set_get_icmpv4_inner_hdr(skb, &protooff, &iph))
> +			goto err;
> 
> +		protocol = iph->protocol;
> +		protooff += iph->ihl*4;
> +	}
>  	return get_port(skb, protocol, protooff, src, port, proto);
> +
> +err:
> +	return false;

If there's nothing else in the error path just a return, then the goto 
is unnecessary.

> +}
> +EXPORT_SYMBOL_GPL(ip_set_get_ipv4_port);
> +
> +bool
> +ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
> +		    __be16 *port, u8 *proto)
> +{
> +	return ip_set_get_ipv4_port(skb, false, src, port, proto);
>  }
>  EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
> 
>  #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
>  bool
> -ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
> -		    __be16 *port, u8 *proto)
> +ip_set_get_ipv6_port(const struct sk_buff *skb, bool inner, bool src,
> +		     __be16 *port, u8 *proto)
>  {
> -	int protoff;
> +	unsigned int protooff;
>  	u8 nexthdr;
>  	__be16 frag_off = 0;
> 
>  	nexthdr = ipv6_hdr(skb)->nexthdr;
>  #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
> -	protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
> +	protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
>  				   &frag_off);
>  #else
> -	protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);
> +	protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);
>  #endif
> -	if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
> -		return false;
> +	if (protooff < 0 || (frag_off & htons(~0x7)) != 0)
> +		goto err;
> +
> +	if (inner) {
> +		struct ipv6hdr *ih;
> +		if (nexthdr != IPPROTO_ICMPV6 ||
> +		    !ip_set_get_icmpv6_inner_hdr(skb, &protooff, &ih))
> +			goto err;
> 
> -	return get_port(skb, nexthdr, protoff, src, port, proto);
> +		nexthdr = ih->nexthdr;
> +		protooff += sizeof(struct ipv6hdr);
> +	}
> +	return get_port(skb, nexthdr, protooff, src, port, proto);
> +
> +err:
> +	return false;
> +}
> +EXPORT_SYMBOL_GPL(ip_set_get_ipv6_port);
> +
> +bool
> +ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
> +		    __be16 *port, u8 *proto)
> +{
> +	return ip_set_get_ipv6_port(skb, false, src, port, proto);
>  }
>  EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
>  #endif
> 
>  bool
> -ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
> +ip_set_get_ipv_port(const struct sk_buff *skb, u8 pf, bool inner, bool src,
> +		    __be16 *port)
>  {
>  	bool ret;
>  	u8 proto;
> 
>  	switch (pf) {
>  	case NFPROTO_IPV4:
> -		ret = ip_set_get_ip4_port(skb, src, port, &proto);
> +		ret = ip_set_get_ipv4_port(skb, inner, src, port, &proto);
>  		break;
>  	case NFPROTO_IPV6:
> -		ret = ip_set_get_ip6_port(skb, src, port, &proto);
> +		ret = ip_set_get_ipv6_port(skb, inner, src, port, &proto);
>  		break;
>  	default:
>  		return false;
> @@ -178,4 +219,11 @@ ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool
> src, __be16 *port)
>  		return false;
>  	}
>  }
> +EXPORT_SYMBOL_GPL(ip_set_get_ipv_port);
> +
> +bool
> +ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
> +{
> +	return ip_set_get_ipv_port(skb, pf, false, src, port);
> +}
>  EXPORT_SYMBOL_GPL(ip_set_get_ip_port);
> 

Best regards,
Jozsef
-
E-mail  : kadlec@xxxxxxxxxxxxxxxxx, kadlecsik.jozsef@xxxxxxxxxxxxx
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux