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

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

 



Hi,

On Wed, 29 May 2013, Dash Four wrote:

> This patch implements "inner" flag option in the 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);

There are a few problems with your patch: some things are duplicated, 
already existing function is reimplemented. There is no error path when 
the inner IPv4/6 header is broken: the returned zero IP address is valid 
for example in the case of the hash:net,iface type. Also, I'd prefer a 
single new function (or two inline functions if their size permits), 
which'd return the success code and in pointers the inner IP header and
the offset to the header (for proto/ports). Read my comments below.
 
> Signed-off-by: Dash Four <mr.dash.four@xxxxxxxxxxxxxx>
> ---
>  kernel/include/linux/netfilter/ipset/ip_set.h      |  167 +++++++++++++++++++
>  .../include/linux/netfilter/ipset/ip_set_getport.h |   16 ++
>  kernel/include/uapi/linux/netfilter/ipset/ip_set.h |    2 +
>  kernel/net/netfilter/ipset/ip_set_bitmap_ip.c      |    4 +-
>  kernel/net/netfilter/ipset/ip_set_bitmap_ipmac.c   |    4 +-
>  kernel/net/netfilter/ipset/ip_set_bitmap_port.c    |   14 +-
>  kernel/net/netfilter/ipset/ip_set_getport.c        |  174
> ++++++++++++++++++++
>  kernel/net/netfilter/ipset/ip_set_hash_ip.c        |   13 +-
>  kernel/net/netfilter/ipset/ip_set_hash_ipport.c    |   37 ++++-
>  kernel/net/netfilter/ipset/ip_set_hash_ipportip.c  |   47 ++++--
>  kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c |   49 ++++--
>  kernel/net/netfilter/ipset/ip_set_hash_net.c       |   15 +-
>  kernel/net/netfilter/ipset/ip_set_hash_netiface.c  |   16 +-
>  kernel/net/netfilter/ipset/ip_set_hash_netport.c   |   41 +++--
>  14 files changed, 542 insertions(+), 57 deletions(-)
> 
> diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h
> b/kernel/include/linux/netfilter/ipset/ip_set.h
> index 8499e25..ac29e16 100644
> --- a/kernel/include/linux/netfilter/ipset/ip_set.h
> +++ b/kernel/include/linux/netfilter/ipset/ip_set.h
> @@ -17,6 +17,9 @@
>  #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>
> @@ -373,6 +376,79 @@ ip4addrptr(const struct sk_buff *skb, bool src, __be32
> *addr)
>  	*addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
>  }
> 
> +static inline __be32
> +ip4inneraddr(const struct sk_buff *skb, bool src)
> +{
> +	struct iphdr _iph;
> +	struct icmphdr _icmph;
> +	u8 type;
> +	const struct iphdr *ih;
> +	const struct icmphdr *ich;
> +	static const size_t len = 8 + sizeof(struct iphdr);
> +
> +	ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
> +	if (ih == NULL || ih->protocol != IPPROTO_ICMP ||
> +	    ntohs(ih->frag_off) & IP_OFFSET)
> +		goto err;
> +
> +	ich = skb_header_pointer(skb, ih->ihl*4,
> +				 sizeof(_icmph), &_icmph);
> +	if (ich == NULL ||
> +	    (ich->type <= NR_ICMP_TYPES && skb->len - ih->ihl*4 < len))
> +		goto err;
> +
> +	type = ich->type;
> +	if (type == ICMP_DEST_UNREACH ||
> +	    type == ICMP_SOURCE_QUENCH ||
> +	    type == ICMP_TIME_EXCEEDED) {
> +		ih = skb_header_pointer(skb, ih->ihl*4 + sizeof(_icmph),
> +					sizeof(_iph), &_iph);
> +		if (ih == NULL || ntohs(ih->frag_off) & IP_OFFSET)
> +			goto err;
> +
> +		return src ? ih->saddr : ih->daddr;
> +	}
> +err:
> +	return 0;
> +}

ip4inneraddr is a duplication of ip4inneraddrptr: if this were the way, 
then ip4inneraddr should simply wrap and call ip4inneraddrptr.

> +static inline void
> +ip4inneraddrptr(const struct sk_buff *skb, bool src, __be32 *addr)
> +{
> +	struct iphdr _iph;
> +	struct icmphdr _icmph;
> +	u8 type;
> +	const struct iphdr *ih;
> +	const struct icmphdr *ich;
> +	static const size_t len = 8 + sizeof(struct iphdr);

I don't like bare numbers as magic constants.

> +	ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
> +	if (ih == NULL || ntohs(ih->frag_off) & IP_OFFSET ||
> +	    ih->protocol != IPPROTO_ICMP)
> +		goto err;
> +
> +	ich = skb_header_pointer(skb, ih->ihl*4,
> +				 sizeof(_icmph), &_icmph);
> +	if (ich == NULL ||
> +	    (ich->type <= NR_ICMP_TYPES && skb->len - ih->ihl*4 < len))
> +		goto err;
> +
> +	type = ich->type;
> +	if (type == ICMP_DEST_UNREACH ||
> +	    type == ICMP_SOURCE_QUENCH ||
> +	    type == ICMP_TIME_EXCEEDED) {
> +		ih = skb_header_pointer(skb, ih->ihl*4 + sizeof(_icmph),
> +					sizeof(_iph), &_iph);
> +		if (ih == NULL || ntohs(ih->frag_off) & IP_OFFSET)
> +			goto err;
> +
> +		*addr = src ? ih->saddr : ih->daddr;
> +		return;
> +	}
> +err:
> +	*addr = 0;
> +}
> +
>  static inline void
>  ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr)
>  {
> @@ -380,6 +456,97 @@ ip6addrptr(const struct sk_buff *skb, bool src, struct
> in6_addr *addr)
>  	       sizeof(*addr));
>  }
> 
> +#define ip6_ext_hdr(hdr)  ((hdr == IPPROTO_HOPOPTS) || \
> +			   (hdr == IPPROTO_ROUTING) || \
> +			   (hdr == IPPROTO_FRAGMENT) || \
> +			   (hdr == IPPROTO_ESP) || \
> +			   (hdr == IPPROTO_AH) || \
> +			   (hdr == IPPROTO_NONE) || \
> +			   (hdr == IPPROTO_DSTOPTS))
> +static inline void
> +ip6inneraddrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr)
> +{
> +	u8 type, currenthdr;
> +	bool fragment = false;
> +	unsigned int ptr, hdrlen = 0;
> +	struct ipv6hdr _ip6h;
> +	struct icmp6hdr _icmp6h;
> +	const struct ipv6hdr *ih;
> +	const struct icmp6hdr *ic;
> +
> +	ih = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h);
> +	if (ih == NULL)
> +		goto err;
> +
> +	ptr = sizeof(struct ipv6hdr);
> +	currenthdr = ih->nexthdr;
> +	while (currenthdr != NEXTHDR_NONE && ip6_ext_hdr(currenthdr)) {
> +		struct ipv6_opt_hdr _hdr;
> +		const struct ipv6_opt_hdr *hp;
> +
> +		hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
> +		if (hp == NULL)
> +			goto err;
> +
> +		switch (currenthdr) {
> +		case IPPROTO_FRAGMENT: {
> +			struct frag_hdr _fhdr;
> +			const struct frag_hdr *fh;
> +
> +			fh = skb_header_pointer(skb, ptr, sizeof(_fhdr),
> +						&_fhdr);
> +			if (fh == NULL)
> +				goto err;
> +			if (ntohs(fh->frag_off) & 0xFFF8)
> +				fragment = true;
> +
> +			hdrlen = 8;
> +			break;
> +		}
> +		case IPPROTO_DSTOPTS:
> +		case IPPROTO_ROUTING:
> +		case IPPROTO_HOPOPTS:
> +			if (fragment)
> +				goto err;
> +
> +			hdrlen = ipv6_optlen(hp);
> +			break;
> +		case IPPROTO_AH:
> +			hdrlen = (hp->hdrlen+2)<<2;
> +			break;
> +		case IPPROTO_ESP:
> +		default:
> +			goto err;
> +		} /* switch IPPROTO */
> +
> +		currenthdr = hp->nexthdr;
> +		ptr += hdrlen;
> +	}

The code segment above is the reimplementation of ipv6_skip_exthdr.

> +	if (fragment || currenthdr != IPPROTO_ICMPV6)
> +		goto err;
> +
> +	ic = skb_header_pointer(skb, ptr, 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) {
> +		ih = skb_header_pointer(skb, ptr + sizeof(_icmp6h),
> +					sizeof(_ip6h), &_ip6h);
> +		if (ih == NULL)
> +			goto err;
> +
> +		memcpy(addr, src ? &ih->saddr : &ih->daddr,
> +		       sizeof(*addr));
> +		return;
> +	}
> +
> +err:
> +	memset(addr, 0, sizeof(*addr));
> +}

Zero valued IP addresses are valid, so it's not a proper error handling in 
the ip[46]inneraddrptr functions.

These all can be replaced by a function which skips the external header 
and points to the inner one, something like:

int
ipset_inner_header(const struct sk_buff *skb, u8 pf, u8 *offset,
		   void *inner)
{
	/* IPv4 */
	...
	/* IPv6 */
	nexthdr = ipv6_hdr(skb)->nexthdr;
	*offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), 
				   &nexthdr, &frag_off);
	/* Validate offset and frag_off */
	/* Check ICMPv6, update offset */
	/* Copy inner IPv6 header to *inner by skb_header_pointer */

	return success/error;
}

Please note, I updated the ipset git tree with a patch which adds the 
a missing verification of frag_off to ip_set_get_ip6_port().

>  /* Calculate the bytes required to store the inclusive range of a-b */
>  static inline int
>  bitmap_bytes(u32 a, u32 b)
> diff --git a/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> b/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> index 90d0930..549d013 100644
> --- a/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> +++ b/kernel/include/linux/netfilter/ipset/ip_set_getport.h
> @@ -4,20 +4,36 @@
>  extern bool ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
>  				__be16 *port, u8 *proto);
> 
> +extern bool ip_set_get_inner_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_ip6_port(const struct sk_buff *skb, bool src,
>  				__be16 *port, u8 *proto);
> +
> +extern bool ip_set_get_inner_ip6_port(const struct sk_buff *skb, bool src,
> +				__be16 *port, u8 *proto);
>  #else
>  static inline bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
>  				       __be16 *port, u8 *proto)
>  {
>  	return false;
>  }
> +
> +static inline bool ip_set_get_inner_ip6_port(const struct sk_buff *skb,
> +					     bool src,
> +					     __be16 *port, u8 *proto)
> +{
> +	return false;
> +}
>  #endif
> 
>  extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src,
>  				__be16 *port);
> 
> +extern bool ip_set_get_inner_ip_port(const struct sk_buff *skb, u8 pf,
> +				     bool src, __be16 *port);
> +
>  static inline bool ip_set_proto_with_ports(u8 proto)
>  {
>  	switch (proto) {
> 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_bitmap_ip.c
> b/kernel/net/netfilter/ipset/ip_set_bitmap_ip.c
> index ce99d26..7552d6d 100644
> --- a/kernel/net/netfilter/ipset/ip_set_bitmap_ip.c
> +++ b/kernel/net/netfilter/ipset/ip_set_bitmap_ip.c
> @@ -116,7 +116,9 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff
> *skb,
>  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
>  	u32 ip;
> 
> -	ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
> +	ip = opt->cmdflags & IPSET_FLAG_INNER ?
> +		ntohl(ip4inneraddr(skb, opt->flags & IPSET_DIM_ONE_SRC)) :
> +		ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
>  	if (ip < map->first_ip || ip > map->last_ip)
>  		return -IPSET_ERR_BITMAP_RANGE;

With the ipset_inner_header function, all these looks like this: we need 
new variables for the IP header, pointer to it and offset. If the 
IPSET_FLAG_INNER is true, ipset_inner_header is called and the returned 
error code handled or the pointer to IP header is updated. Something like 
this:

	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
	u32 ip;
	struct iphdr _iphdr, *iphdr = ip_hdr(skb);
	u8 offset = 0;

	if (opt->cmdflags & IPSET_FLAG_INNER) {
		if (ipset_inner_header(skb, NFPROTO_IPV4, &offset, &_iphdr))
			return -EINVAL;
		iphdr = &_iphdr;
	}
	....

Current ip[46]addr[ptr] functions of course need to be modified to use the 
IP header structures instead of the skbuff one.

> diff --git a/kernel/net/netfilter/ipset/ip_set_bitmap_ipmac.c
> b/kernel/net/netfilter/ipset/ip_set_bitmap_ipmac.c
> index 6d5bad9..df5f3b4 100644
> --- a/kernel/net/netfilter/ipset/ip_set_bitmap_ipmac.c
> +++ b/kernel/net/netfilter/ipset/ip_set_bitmap_ipmac.c
> @@ -223,7 +223,9 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff
> *skb,
>  	if (!(opt->flags & IPSET_DIM_TWO_SRC))
>  		return 0;
> 
> -	ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
> +	ip = opt->cmdflags & IPSET_FLAG_INNER ?
> +		ntohl(ip4inneraddr(skb, opt->flags & IPSET_DIM_ONE_SRC)) :
> +		ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
>  	if (ip < map->first_ip || ip > map->last_ip)
>  		return -IPSET_ERR_BITMAP_RANGE;
> 
> diff --git a/kernel/net/netfilter/ipset/ip_set_bitmap_port.c
> b/kernel/net/netfilter/ipset/ip_set_bitmap_port.c
> index b220489..d29395b 100644
> --- a/kernel/net/netfilter/ipset/ip_set_bitmap_port.c
> +++ b/kernel/net/netfilter/ipset/ip_set_bitmap_port.c
> @@ -110,9 +110,17 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff
> *skb,
>  	__be16 __port;
>  	u16 port = 0;
> 
> -	if (!ip_set_get_ip_port(skb, opt->family,
> -				opt->flags & IPSET_DIM_ONE_SRC, &__port))
> -		return -EINVAL;
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip_port(skb, opt->family,
> +					opt->flags & IPSET_DIM_ONE_SRC,
> +					&__port))
> +			return -EINVAL;
> +	} else {
> +		if (!ip_set_get_ip_port(skb, opt->family,
> +					opt->flags & IPSET_DIM_ONE_SRC,
> +					&__port))
> +			return -EINVAL;
> +	}
> 
>  	port = ntohs(__port);

The ip_set_get*port functions need the new offset argument too.
 
> diff --git a/kernel/net/netfilter/ipset/ip_set_getport.c
> b/kernel/net/netfilter/ipset/ip_set_getport.c
> index 6c019b3..f6ba3e8 100644
> --- a/kernel/net/netfilter/ipset/ip_set_getport.c
> +++ b/kernel/net/netfilter/ipset/ip_set_getport.c
> @@ -19,6 +19,7 @@
>  #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_getport.h>
> 
> @@ -128,6 +129,47 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
>  }
>  EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
> 
> +bool
> +ip_set_get_inner_ip4_port(const struct sk_buff *skb, bool src,
> +			  __be16 *port, u8 *proto)
> +{
> +	unsigned int protooff;
> +	u8 type;
> +	struct iphdr _iph;
> +	struct icmphdr _icmph;
> +	const struct iphdr *ih;
> +	const struct icmphdr *ich;
> +	static const size_t len = 8 + sizeof(struct iphdr);
> +
> +	ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
> +	if (ih == NULL || ih->protocol != IPPROTO_ICMP ||
> +	    ntohs(ih->frag_off) & IP_OFFSET)
> +		goto err;
> +
> +	protooff = ih->ihl*4;
> +	ich = skb_header_pointer(skb, protooff, sizeof(_icmph), &_icmph);
> +	if (ich == NULL ||
> +	    (ich->type <= NR_ICMP_TYPES && skb->len - protooff < len))
> +		goto err;
> +
> +	type = ich->type;
> +	if (type == ICMP_DEST_UNREACH ||
> +	    type == ICMP_SOURCE_QUENCH ||
> +	    type == ICMP_TIME_EXCEEDED) {
> +		protooff += sizeof(_icmph);
> +		ih = skb_header_pointer(skb, protooff, sizeof(_iph), &_iph);
> +		if (ih == NULL || ntohs(ih->frag_off) & IP_OFFSET)
> +			goto err;
> +
> +		protooff += ih->ihl*4;
> +		return get_port(skb, ih->protocol, protooff, src,
> +				port, proto);
> +	}
> +err:
> +	return false;
> +}
> +EXPORT_SYMBOL_GPL(ip_set_get_inner_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,
> @@ -152,6 +194,109 @@ ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
>  	return get_port(skb, nexthdr, protooff, src, port, proto);
>  }
>  EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
> +
> +#define ip6_ext_hdr(hdr)  ((hdr == IPPROTO_HOPOPTS) || \
> +			   (hdr == IPPROTO_ROUTING) || \
> +			   (hdr == IPPROTO_FRAGMENT) || \
> +			   (hdr == IPPROTO_ESP) || \
> +			   (hdr == IPPROTO_AH) || \
> +			   (hdr == IPPROTO_NONE) || \
> +			   (hdr == IPPROTO_DSTOPTS))
> +
> +bool process_ip6_hdr(const struct sk_buff *skb, u8 *currenthdr,
> +		     const unsigned int offset, unsigned int *ptr)
> +{
> +	unsigned int hdrlen = 0;
> +	bool fragment = false;
> +	struct ipv6hdr _ip6h;
> +	const struct ipv6hdr *ih;
> +
> +	ih = skb_header_pointer(skb, offset, sizeof(_ip6h), &_ip6h);
> +	if (ih == NULL)
> +		goto err;
> +
> +	*ptr = offset + sizeof(struct ipv6hdr);
> +	*currenthdr = ih->nexthdr;
> +	while (*currenthdr != NEXTHDR_NONE && ip6_ext_hdr(*currenthdr)) {
> +		struct ipv6_opt_hdr _hdr;
> +		const struct ipv6_opt_hdr *hp;
> +
> +		hp = skb_header_pointer(skb, *ptr, sizeof(_hdr), &_hdr);
> +		if (hp == NULL)
> +			goto err;
> +
> +		switch (*currenthdr) {
> +		case IPPROTO_FRAGMENT: {
> +			struct frag_hdr _fhdr;
> +			const struct frag_hdr *fh;
> +
> +			fh = skb_header_pointer(skb, *ptr, sizeof(_fhdr),
> +						&_fhdr);
> +			if (fh == NULL)
> +				goto err;
> +			if (ntohs(fh->frag_off) & 0xFFF8)
> +				fragment = true;
> +
> +			hdrlen = 8;
> +			break;
> +		}
> +		case IPPROTO_DSTOPTS:
> +		case IPPROTO_ROUTING:
> +		case IPPROTO_HOPOPTS:
> +			if (fragment)
> +				goto err;
> +
> +			hdrlen = ipv6_optlen(hp);
> +			break;
> +		case IPPROTO_AH:
> +			hdrlen = (hp->hdrlen+2)<<2;
> +			break;
> +		case IPPROTO_ESP:
> +		default:
> +			goto err;
> +		} /* switch IPPROTO */
> +
> +		*currenthdr = hp->nexthdr;
> +		*ptr += hdrlen;
> +	}
> +	return !fragment;
> +
> +err:
> +	return false;
> +}
> +
> +bool
> +ip_set_get_inner_ip6_port(const struct sk_buff *skb, bool src,
> +		    __be16 *port, u8 *proto)
> +{
> +	u8 type, currenthdr = 0;
> +	unsigned int ptr = 0;
> +	struct icmp6hdr _icmp6h;
> +	const struct icmp6hdr *ic;
> +
> +	if (!process_ip6_hdr(skb, &currenthdr, 0, &ptr) ||
> +	    currenthdr != IPPROTO_ICMPV6)
> +		goto err;
> +
> +	ic = skb_header_pointer(skb, ptr, 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) {
> +		if (!process_ip6_hdr(skb, &currenthdr,
> +				ptr + sizeof(_icmp6h), &ptr) || ptr < 0)
> +			goto err;
> +
> +		return get_port(skb, currenthdr, ptr, src, port, proto);
> +	}
> +
> +err:
> +	return false;
> +}
> +EXPORT_SYMBOL_GPL(ip_set_get_inner_ip6_port);
>  #endif
> 
>  bool
> @@ -181,3 +326,32 @@ ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool
> src, __be16 *port)
>  	}
>  }
>  EXPORT_SYMBOL_GPL(ip_set_get_ip_port);
> +
> +bool
> +ip_set_get_inner_ip_port(const struct sk_buff *skb, u8 pf, bool src,
> +			 __be16 *port)
> +{
> +	bool ret;
> +	u8 proto;
> +
> +	switch (pf) {
> +	case NFPROTO_IPV4:
> +		ret = ip_set_get_inner_ip4_port(skb, src, port, &proto);
> +		break;
> +	case NFPROTO_IPV6:
> +		ret = ip_set_get_inner_ip6_port(skb, src, port, &proto);
> +		break;
> +	default:
> +		return false;
> +	}
> +	if (!ret)
> +		return ret;
> +	switch (proto) {
> +	case IPPROTO_TCP:
> +	case IPPROTO_UDP:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(ip_set_get_inner_ip_port);

With the offset argument, all of the above can eliminated. (Here you again 
duplicated and reimplemented code.)

> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ip.c
> b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
> index 260c9a8..e9b19a8 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_ip.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
> @@ -102,7 +102,11 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff
> *skb,
>  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
>  	__be32 ip;
> 
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
> +	} else {
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
> +	}
>  	ip &= ip_set_netmask(h->netmask);
>  	if (ip == 0)
>  		return -EINVAL;
> @@ -255,7 +259,12 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff
> *skb,
>  	struct hash_ip6_elem e = {};
>  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
> 
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip.in6);
> +	} else {
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	}
>  	hash_ip6_netmask(&e.ip, h->netmask);
>  	if (ipv6_addr_any(&e.ip.in6))
>  		return -EINVAL;
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
> b/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
> index 64caad3..f3528ff 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
> @@ -121,11 +121,22 @@ hash_ipport4_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  	struct hash_ipport4_elem e = { };
>  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
> 
> -	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip4_port(skb,
> +					opt->flags & IPSET_DIM_TWO_SRC,
> +					&e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +			       &e.ip);
> +	} else {
> +		if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +	}
> 
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
>  }
> 
> @@ -311,11 +322,21 @@ hash_ipport6_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  	struct hash_ipport6_elem e = { };
>  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
> 
> -	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip6_port(skb,
> +					 opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip.in6);
> +	} else {
> +		if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> 
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	}
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
>  }
> 
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
> b/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
> index 2873bbc..8b3bdee 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
> @@ -125,12 +125,23 @@ hash_ipportip4_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  	struct hash_ipportip4_elem e = { };
>  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
> 
> -	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> -
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip4_port(skb,
> +					opt->flags & IPSET_DIM_TWO_SRC,
> +				 	&e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_THREE_SRC,
> +				&e.ip2);
> +	} else {
> +		if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +				 	 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
> +	}
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
>  }
> 
> @@ -324,12 +335,24 @@ hash_ipportip6_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  	struct hash_ipportip6_elem e = { };
>  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
> 
> -	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> -
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip6_port(skb,
> +					opt->flags & IPSET_DIM_TWO_SRC,
> +					&e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip.in6);
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_THREE_SRC,
> +				&e.ip2.in6);
> +	} else {
> +		if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
> +	}
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
>  }
> 
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
> b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
> index db0e761..154c836 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
> @@ -177,12 +177,24 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK - 1;
> 
> -	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> -
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip4_port(skb,
> +					opt->flags & IPSET_DIM_TWO_SRC,
> +					&e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip);
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_THREE_SRC,
> +				&e.ip2);
> +	} else {
> +		if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
> +	}
>  	e.ip2 &= ip_set_netmask(e.cidr + 1);
> 
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
> @@ -461,12 +473,25 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK - 1;
> 
> -	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> -
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip6_port(skb,
> +					opt->flags & IPSET_DIM_TWO_SRC,
> +					&e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip.in6);
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_THREE_SRC,
> +				&e.ip2.in6);
> +	} else {
> +		if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC,
> +			   &e.ip2.in6);
> +	}
>  	ip6_netmask(&e.ip2, e.cidr + 1);
> 
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_net.c
> b/kernel/net/netfilter/ipset/ip_set_hash_net.c
> index 846ec80..431706a 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_net.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_net.c
> @@ -151,8 +151,13 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff
> *skb,
>  		return -EINVAL;
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK;
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +			       &e.ip);
> +	} else {
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +	}
> 
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
>  	e.ip &= ip_set_netmask(e.cidr);
> 
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
> @@ -346,8 +351,12 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff
> *skb,
>  		return -EINVAL;
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK;
> -
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip.in6);
> +	} else {
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	}
>  	ip6_netmask(&e.ip, e.cidr);
> 
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
> b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
> index 8f0e496..51415fb 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
> @@ -275,8 +275,12 @@ hash_netiface4_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  		return -EINVAL;
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK;
> -
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip);
> +	} else {
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +	}
>  	e.ip &= ip_set_netmask(e.cidr);
> 
>  #define IFACE(dir)	(par->dir ? par->dir->name : NULL)
> @@ -544,8 +548,12 @@ hash_netiface6_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  		return -EINVAL;
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK;
> -
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip.in6);
> +	} else {
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	}
>  	ip6_netmask(&e.ip, e.cidr);
> 
>  	if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netport.c
> b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
> index 021d716..376a28e 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_netport.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
> @@ -169,11 +169,21 @@ hash_netport4_kadt(struct ip_set *set, const struct
> sk_buff *skb,
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK - 1;
> 
> -	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> -
> -	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip4_port(skb,
> +					opt->flags & IPSET_DIM_TWO_SRC,
> +					&e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip);
> +	} else {
> +		if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
> +	}
>  	e.ip &= ip_set_netmask(e.cidr + 1);
> 
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
> @@ -413,12 +423,21 @@ hash_netport6_kadt(struct ip_set *set, const struct
> sk_buff *skb,
> 
>  	if (adt == IPSET_TEST)
>  		e.cidr = HOST_MASK - 1;
> -
> -	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> -				 &e.port, &e.proto))
> -		return -EINVAL;
> -
> -	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	if (opt->cmdflags & IPSET_FLAG_INNER) {
> +		if (!ip_set_get_inner_ip6_port(skb,
> +					opt->flags & IPSET_DIM_TWO_SRC,
> +					&e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip6inneraddrptr(skb, opt->flags & IPSET_DIM_ONE_SRC,
> +				&e.ip.in6);
> +	} else {
> +		if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
> +					 &e.port, &e.proto))
> +			return -EINVAL;
> +
> +		ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
> +	}
>  	ip6_netmask(&e.ip, e.cidr + 1);
> 
>  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);

I'd prefer the patch to be split into two parts: the first which 
implements the new functions and changes in the core and the second which 
applies them to the set types. Thanks.

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