Subject: Re: [v11 PATCH 2/3] NETFILTER module xt_hmark, new target for HASH based fwmark

From: Pablo Neira Ayuso <[email-masked]>
Date: Thu, 12 Apr 2012 17:54:42 +0200

Hi Hans,

On Thu, Mar 22, 2012 at 12:59:52PM +0100, Hans Schillstrom wrote:
> The target allows you to create rules in the "raw" and "mangle" tables
> which alter the netfilter mark (nfmark) field within a given range.
> First a 32 bit hash value is generated then modulus by <limit> and
> finally an offset is added before it's written to nfmark.
> Prior to routing, the nfmark can influence the routing method (see
> "Use netfilter MARK value as routing key") and can also be used by
> other subsystems to change their behavior.
> 
> man page
>    HMARK
>        This  module  does  the  same  as MARK, i.e. set an fwmark, but the mark
>        is based on a hash value.  The hash is based on saddr, daddr, sport,
>        dport and proto. The same mark will be produced independent of direction
>        if no masks is set or the same masks is used for src and dest.
>        The hash mark could be adjusted by modulus and finally an offset could
>        be added, i.e the final mark will be within a range. ICMP error will use
>        the the original message for hash calculation not the icmp it self.
> 
>        Note: IPv4 packets with nf_defrag_ipv4 loaded will be defragmented before they reach hmark,
>              IPv6 nf_defrag is not implemented this way, hence fragmented ipv6 packets will reach hmark.
>              Default behavior is to completely ignore any fragment if it reach hmark.
>              --hmark-method L3 is fragment safe since neither ports or L4 protocol field is used.
>              None of the parameters effect the packet it self only the calculated hash value.
> 
>        Parameters: Short hand methods
> 
>        --hmark-method L3
>               Do not use L4 protocol field, ports or spi, only Layer 3 addresses,
>               mask length of L3 addresses can still be used. Fragment or not
>               does not matter in this case since only L3 address can be used in
>               calc. of hash value.
> 
>        --hmark-method L3-4 (Default)
>               Include  L4  in  calculation. of hash value i.e. all masks below are valid.
>               Fragments will be ignored. (i.e no hash value produced)
> 
>        For all masks default is all "1:s", to disable a field use mask 0
> 
>        --hmark-src-mask length
>               The length of the mask to AND the source address with (saddr & value).
> 
>        --hmark-dst-mask length
>               The length of the mask to AND the dest. address with (daddr & value).
> 
>        --hmark-sport-mask value
>               A 16 bit value to AND the src port with (sport & value).
> 
>        --hmark-dport-mask value
>               A 16 bit value to AND the dest port with (dport & value).
> 
>        --hmark-sport-set value
>               A 16 bit value to OR the src port with (sport | value).
> 
>        --hmark-dport-set value
>               A 16 bit value to OR the dest port with (dport | value).
> 
>        --hmark-spi-mask value
>               Value to AND the spi field with (spi & value) valid for proto esp or ah.
> 
>        --hmark-spi-set value
>               Value to OR the spi field with (spi | value) valid for proto esp or ah.
> 
>        --hmark-proto-mask value
>               An 8 bit value to AND the L4 proto field with (proto & value).
> 
>        --hmark-ct
>               When flag is set, conntrack data should be used. Useful when NAT internal
>               addressed should be used in calculation.  Be careful when using DNAT
>               since mangle table is handled before nat table. I.e it will not work as
>               expected to put HMARK in table mangle and PREROUTING chain. The  initial
>               packet will have it's hash based on the original address,
>               while the rest of the flow will use the NAT:ed address.
> 
>        --hmark-rnd value
>               A 32 bit initial value for hash calc, default is 0xc175a3b8.
> 
>        Final processing of the mark in order of execution.
> 
>        --hmark-mod value (must be > 0)
>               The easiest way to describe this is:  hash = hash mod <value>
> 
>        --hmark-offset value
>               The easiest way to describe this is:  hash = hash + <value>
> 
>        Examples:
> 
>        Default rule handles all TCP, UDP, SCTP, ESP & AH
> 
>               iptables -t mangle -A PREROUTING -m state --state NEW,ESTABLISHED,RELATED
>                -j HMARK --hmark-offset 10000 --hmark-mod 10
> 
>        Handle SCTP and hash dest port only and produce a nfmark between 100-119.
> 
>               iptables -t mangle -A PREROUTING -p SCTP -j HMARK --src-mask 0 --dst-mask 0
>                --sp-mask 0 --offset 100 --mod 20
> 
>        Fragment safe Layer 3 only, that keep a class C network flow together
> 
>               iptables -t mangle -A PREROUTING -j HMARK --method L3 --src-mask 24 --mod 20 --offset 100
> 
> Rev 11
>     Two comments changed
> Rev 10
>      Even more simplified NAT handling just one switch --hmark-ct
>      some renaming and some minor changes.
>      Changes are based on Pablos review.
> 
> Rev 9
>       Simplified NAT selections, cleanup of comments, added checkentry()
>       change of #ifdef to #if IS_ENABLED and dependency.
>       Some minor formating.
>       Most changes are based on Pablos review.
> 
> Rev 8
>       method L3 / L3-4 added i.e. Fragment handling changed to
>       don't handle in "method L3-4"
>       Syntax change in user mode more NF compatible.
>       Most changes are based on Pablos review.
> 
> Rev 7
>       IPv6 descending into icmp error hdr didn't work as expected
>       with ipv6_find_hdr() Now it works as expected.
> 
> Rev 6
>       Compile options with or without conntrack fixed.
>       __ipv6_find_hdr() replaced by ipv6_find_hdr()
> 
> Rev 5
>       IPv6 rewritten uses __ipv6_find_hdr() (P. Mc Hardy)
>       Full mask and address used for IPv6 smask and dmask (J.Engelhart)
>       Changes due to comments by Pablo Neira Ayuso  and Eric Dumazet
>       i.e uses of skb_header_pointer() and Null check of info->hmod
>       Man page changes
> 
> Rev 4
>       different targets for IPv4 and IPv6
>       Changes based on review by Pablo.
> 
> Rev 3
>       Support added to SCTP for IPv6
> Rev 2
>       IPv6 header scan changed to follow RFC 2640
>       IPv4 icmp echo fragmented does now use proto as ipv6
>       IPv6 pskb_may_pull() check is done in every time in header loop.
>       IPv4 nat support added.
>       default added in IPv6 loop and null check of hp
> 
> Signed-off-by: Hans Schillstrom <[email-masked]>
> ---
>  include/linux/netfilter/xt_HMARK.h |   62 +++++++
>  net/netfilter/Kconfig              |   18 ++
>  net/netfilter/Makefile             |    1 +
>  net/netfilter/xt_HMARK.c           |  319 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 400 insertions(+), 0 deletions(-)
>  create mode 100644 include/linux/netfilter/xt_HMARK.h
>  create mode 100644 net/netfilter/xt_HMARK.c
> 
> diff --git a/include/linux/netfilter/xt_HMARK.h b/include/linux/netfilter/xt_HMARK.h
> new file mode 100644
> index 0000000..cdf4a8f
> --- /dev/null
> +++ b/include/linux/netfilter/xt_HMARK.h
> @@ -0,0 +1,62 @@
> +#ifndef XT_HMARK_H_
> +#define XT_HMARK_H_
> +
> +#include <linux/types.h>
> +
> +enum {
> +	XT_HMARK_NONE,
> +	XT_HMARK_SADR_AND,
> +	XT_HMARK_DADR_AND,
> +	XT_HMARK_SPI_AND,
> +	XT_HMARK_SPI_OR,
> +	XT_HMARK_SPORT_AND,
> +	XT_HMARK_DPORT_AND,
> +	XT_HMARK_SPORT_OR,
> +	XT_HMARK_DPORT_OR,
> +	XT_HMARK_PROTO_AND,
> +	XT_HMARK_RND,
> +	XT_HMARK_MODULUS,
> +	XT_HMARK_OFFSET,
> +	XT_HMARK_CT,
> +	XT_HMARK_METHOD_L3,
> +	XT_HMARK_METHOD_L3_4,
> +	XT_F_HMARK_SADR_AND    = 1 << XT_HMARK_SADR_AND,
> +	XT_F_HMARK_DADR_AND    = 1 << XT_HMARK_DADR_AND,
> +	XT_F_HMARK_SPI_AND     = 1 << XT_HMARK_SPI_AND,
> +	XT_F_HMARK_SPI_OR      = 1 << XT_HMARK_SPI_OR,
> +	XT_F_HMARK_SPORT_AND   = 1 << XT_HMARK_SPORT_AND,
> +	XT_F_HMARK_DPORT_AND   = 1 << XT_HMARK_DPORT_AND,
> +	XT_F_HMARK_SPORT_OR    = 1 << XT_HMARK_SPORT_OR,
> +	XT_F_HMARK_DPORT_OR    = 1 << XT_HMARK_DPORT_OR,
> +	XT_F_HMARK_PROTO_AND   = 1 << XT_HMARK_PROTO_AND,
> +	XT_F_HMARK_RND         = 1 << XT_HMARK_RND,
> +	XT_F_HMARK_MODULUS     = 1 << XT_HMARK_MODULUS,
> +	XT_F_HMARK_OFFSET      = 1 << XT_HMARK_OFFSET,
> +	XT_F_HMARK_CT          = 1 << XT_HMARK_CT,
> +	XT_F_HMARK_METHOD_L3   = 1 << XT_HMARK_METHOD_L3,
> +	XT_F_HMARK_METHOD_L3_4 = 1 << XT_HMARK_METHOD_L3_4,
> +};
> +
> +union hmark_ports {
> +	struct {
> +		__u16	src;
> +		__u16	dst;
> +	} p16;
> +	__u32	v32;
> +};
> +
> +struct xt_hmark_info {
> +	union nf_inet_addr	src_mask;	/* Source address mask */
> +	union nf_inet_addr	dst_mask;	/* Dest address mask */
> +	union hmark_ports	port_mask;
> +	union hmark_ports	port_set;
> +	__u32			spi_mask;
> +	__u32			spi_set;
> +	__u32			flags;		/* Print out only */
> +	__u16			proto_mask;	/* L4 Proto mask */
> +	__u32			hashrnd;
> +	__u32			hmodulus;	/* Modulus */
> +	__u32			hoffset;	/* Offset */
> +};
> +
> +#endif /* XT_HMARK_H_ */
> diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
> index f8ac4ef..a775804 100644
> --- a/net/netfilter/Kconfig
> +++ b/net/netfilter/Kconfig
> @@ -488,6 +488,24 @@ config NETFILTER_XT_TARGET_HL
>  	since you can easily create immortal packets that loop
>  	forever on the network.
>  
> +config NETFILTER_XT_TARGET_HMARK
> +	tristate '"HMARK" target support'
> +	depends on (IP6_NF_IPTABLES || IP6_NF_IPTABLES=n)

do we really need this dependency above?

> +	depends on NETFILTER_ADVANCED
> +	---help---
> +	This option adds the "HMARK" target.
> +
> +	The target allows you to create rules in the "raw" and "mangle" tables
> +	which alter the netfilter mark (nfmark) field within a given range.
> +	First a 32 bit hash value is generated then modulus by <limit> and
> +	finally an offset is added before it's written to nfmark.
> +
> +	Prior to routing, the nfmark can influence the routing method (see
> +	"Use netfilter MARK value as routing key") and can also be used by
> +	other subsystems to change their behavior.
> +
> +	The mark match can also be used to match nfmark produced by this module.
> +
>  config NETFILTER_XT_TARGET_IDLETIMER
>  	tristate  "IDLETIMER target support"
>  	depends on NETFILTER_ADVANCED
> diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
> index 40f4c3d..2712ba0 100644
> --- a/net/netfilter/Makefile
> +++ b/net/netfilter/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
>  obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
>  obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
>  obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
> +obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
>  obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
>  obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
>  obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
> diff --git a/net/netfilter/xt_HMARK.c b/net/netfilter/xt_HMARK.c
> new file mode 100644
> index 0000000..d90549d
> --- /dev/null
> +++ b/net/netfilter/xt_HMARK.c
> @@ -0,0 +1,319 @@
> +/*
> + * xt_hmark - Netfilter module to set mark as hash value
> + *
> + * (C) 2012 Hans Schillstrom <[email-masked]>
> + *
> + *Description:
> + *	This module calculates a hash value that can be modified by modulus
> + *	and an offset, i.e. it is possible to produce a skb->mark within a range
> + *	The hash value is based on a direction independent five tuple:
> + *	src & dst addr src & dst ports and protocol.
> + *	There is two distinct modes for hash calculation:
> + *
> + *	This program is free software; you can redistribute it and/or modify
> + *	it under the terms of the GNU General Public License version 2 as
> + *	published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/skbuff.h>
> +#include <net/ip.h>
> +#include <linux/icmp.h>
> +
> +#include <linux/netfilter/xt_HMARK.h>
> +#include <linux/netfilter/x_tables.h>
> +#include <net/netfilter/nf_conntrack.h>
> +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
> +#include <net/ipv6.h>
> +#include <linux/netfilter_ipv6/ip6_tables.h>
> +#endif
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Hans Schillstrom <[email-masked]>");
> +MODULE_DESCRIPTION("Xtables: Packet range mark operations by Hash value");
> +MODULE_ALIAS("ipt_HMARK");
> +MODULE_ALIAS("ip6t_HMARK");
> +
> +/*
> + * ICMP, get header offset if icmp error
> + */
> +static int get_inner_hdr(struct sk_buff *skb, int iphsz, int *nhoff)
> +{
> +	const struct icmphdr *icmph;
> +	struct icmphdr _ih;
> +
> +	/* Not enough header? */
> +	icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih);
> +	if (icmph == NULL && icmph->type > NR_ICMP_TYPES)
> +		return 0;
> +
> +	/* Error message? */
> +	if (icmph->type != ICMP_DEST_UNREACH &&
> +	    icmph->type != ICMP_SOURCE_QUENCH &&
> +	    icmph->type != ICMP_TIME_EXCEEDED &&
> +	    icmph->type != ICMP_PARAMETERPROB &&
> +	    icmph->type != ICMP_REDIRECT)
> +		return 0;
> +
> +	*nhoff += iphsz + sizeof(_ih);
> +	return 1;
> +}
> +
> +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
> +/*
> + * Get ipv6 header offset if icmp error
> + */
> +static int get_inner6_hdr(struct sk_buff *skb, int *offset)
> +{
> +	struct icmp6hdr *icmp6h, _ih6;
> +
> +	icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6);
> +	if (icmp6h == NULL)
> +		return 0;
> +
> +	if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) {
> +		*offset += sizeof(struct icmp6hdr);
> +		return 1;
> +	}
> +	return 0;
> +}
> +/*
> + * Calculate hash based fw-mark, on the five tuple if possible.
> + * special cases :
> + *  - Fragments do not use ports not even on the first fragment,
> + *    nf_defrag_ipv6.ko don't defrag for us like it do in ipv4.
> + *    This might be changed in the future.
> + *  - On ICMP errors the inner header will be used.
> + *  - Tunnels no ports
> + *  - ESP & AH uses SPI
> + * @returns XT_CONTINUE
> + */
> +static unsigned int
> +hmark_v6(struct sk_buff *skb, const struct xt_action_param *par)
> +{
> +	const struct xt_hmark_info *info = par->targinfo;
> +	struct ipv6hdr *ip6, _ip6;
> +	int poff, flag = IP6T_FH_F_AUTH; /* Ports offset, find_hdr flags */
> +	union hmark_ports uports;
> +	u32 addr_src, addr_dst, hash, nhoffs = 0;
> +	u16 fragoff = 0;
> +	u8 nexthdr;
> +
> +	ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb));
> +	nexthdr = ipv6_find_hdr(skb, &nhoffs, -1, &fragoff, &flag);
> +	if (nexthdr < 0)
> +		return XT_CONTINUE;
> +	/* No need to check for icmp errors on fragments */
> +	if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
> +		goto noicmp;
> +	/* if an icmp error, use the inner header */
> +	if (get_inner6_hdr(skb, &nhoffs)) {
> +		ip6 = skb_header_pointer(skb, nhoffs, sizeof(_ip6), &_ip6);
> +		if (!ip6)
> +			return XT_CONTINUE;
> +		/* Treat AH as ESP, use SPI nothing else. */
> +		flag = IP6T_FH_F_AUTH;
> +		nexthdr = ipv6_find_hdr(skb, &nhoffs, -1, &fragoff, &flag);
> +		if (nexthdr < 0)
> +			return XT_CONTINUE;
> +	}
> +noicmp:
> +	addr_src = (__force u32)
> +		(ip6->saddr.s6_addr32[0] & info->src_mask.in6.s6_addr32[0]) ^
> +		(ip6->saddr.s6_addr32[1] & info->src_mask.in6.s6_addr32[1]) ^
> +		(ip6->saddr.s6_addr32[2] & info->src_mask.in6.s6_addr32[2]) ^
> +		(ip6->saddr.s6_addr32[3] & info->src_mask.in6.s6_addr32[3]);
> +	addr_dst = (__force u32)
> +		(ip6->daddr.s6_addr32[0] & info->dst_mask.in6.s6_addr32[0]) ^
> +		(ip6->daddr.s6_addr32[1] & info->dst_mask.in6.s6_addr32[1]) ^
> +		(ip6->daddr.s6_addr32[2] & info->dst_mask.in6.s6_addr32[2]) ^
> +		(ip6->daddr.s6_addr32[3] & info->dst_mask.in6.s6_addr32[3]);
> +
> +	uports.v32 = 0;
> +	if ((info->flags & XT_F_HMARK_METHOD_L3) ||
> +	    (nexthdr == IPPROTO_ICMPV6))
> +		goto no_ports;
> +	/* Is next header valid for port or SPI calculation ? */
> +	poff = proto_ports_offset(nexthdr);
> +	if ((flag & IP6T_FH_F_FRAG) || poff < 0)
> +		return XT_CONTINUE;
> +
> +	nhoffs += poff;
> +	if (skb_copy_bits(skb, nhoffs, &uports, sizeof(uports)) < 0)
> +		return XT_CONTINUE;
> +
> +	if ((nexthdr == IPPROTO_ESP) || (nexthdr == IPPROTO_AH))
> +		uports.v32 = (uports.v32 & info->spi_mask) | info->spi_set;
> +	else {
> +		uports.v32 = (uports.v32 & info->port_mask.v32) |
> +			      info->port_set.v32;
> +		/* get a consistent hash (same value in any flow dirs.) */
> +		if (uports.p16.dst < uports.p16.src)
> +			swap(uports.p16.dst, uports.p16.src);
> +	}
> +
> +no_ports:
> +	nexthdr &= info->proto_mask;
> +	/* get a consistent hash (same value in any flow direction) */
> +	if (addr_dst < addr_src)
> +		swap(addr_src, addr_dst);
> +
> +	hash = jhash_3words(addr_src, addr_dst, uports.v32, info->hashrnd) ^ nexthdr;
> +	skb->mark = (hash % info->hmodulus) + info->hoffset;
> +	return XT_CONTINUE;
> +}
> +#endif
> +/*
> + * Calculate hash based fw-mark, on the five tuple if possible.
> + * special cases :
> + *  - Fragments do not use ports not even on the first fragment,
> + *    unless nf_defrag_xx.ko is used.
> + *  - On ICMP errors the inner header will be used.
> + *  - Tunnels no ports
> + *  - ESP & AH uses SPI
> + * @returns XT_CONTINUE
> + */
> +static unsigned int
> +hmark_v4(struct sk_buff *skb, const struct xt_action_param *par)
> +{
> +	const struct xt_hmark_info *info = par->targinfo;
> +	struct iphdr *ip, _ip;
> +	int nhoff, poff, frag = 0;
> +	union hmark_ports uports;
> +	u32 addr_src, addr_dst, hash;
> +	u8 ip_proto;
> +
> +	nhoff = skb_network_offset(skb);
> +	ip = (struct iphdr *) (skb->data + nhoff);
> +	if (ip->protocol == IPPROTO_ICMP) {
> +		/* if an icmp error, calc hash on inner header */
> +		if (get_inner_hdr(skb, ip->ihl * 4, &nhoff)) {
> +			ip = skb_header_pointer(skb, nhoff, sizeof(_ip), &_ip);
> +			if (!ip)
> +				return XT_CONTINUE;
> +		}
> +	}
> +
> +	ip_proto = ip->protocol;
> +	if (ip->frag_off & htons(IP_MF | IP_OFFSET))
> +		frag = 1;
> +
> +	addr_src = (__force u32) ip->saddr;
> +	addr_dst = (__force u32) ip->daddr;
> +	uports.v32 = 0;
> +/* conntrack take care of ICMP relation */
> +#if IS_ENABLED(CONFIG_NF_CONNTRACK)
> +	if (info->flags & XT_F_HMARK_CT) {
> +		struct nf_conntrack_tuple *otuple;
> +		struct nf_conntrack_tuple *rtuple;
> +		enum ip_conntrack_info ctinfo;
> +		struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
> +
> +		if (!ct || nf_ct_is_untracked(ct))
> +			return XT_CONTINUE;
> +
> +		otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
> +		rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
> +
> +		addr_src       = (__force u32)otuple->src.u3.in.s_addr;
> +		uports.p16.src = otuple->src.u.udp.port;
> +		addr_dst       = (__force u32)rtuple->src.u3.in.s_addr;
> +		uports.p16.dst = rtuple->src.u.udp.port;
> +	}

There's an inconsistency here. No conntrack support for IPv6.

I'd suggest to split hmark_v4 into two functions by checking:

... hmark_v4(...)
{
        if (info->flags & XT_F_HMARK_CT)
                ret = hmark_tg_ct_v4(...)
        else
                ret = hmark_tg_v4(...)

        return ret;
}

Same thing for IPv6. Those function will look smaller, and that's good
to make the code more maintainable.

You can define some hmark_hash_v4 and hmark_hash_v6 function that you
may want to inline.

Another suggestion in case you may need to extend HMARK in
the future. I think some info->type field to specify the 

info->type can be HMARK_T_PKT or HMARK_T_CT.

Thus, the code above would look like:

... hmark_v4(...)
{
        switch(info->type) {
        case HMARK_T_PKT:
                ret = hmark_tg_ct_v4(...)
                break;
        case HMARK_T_CT:
                ret = hmark_tg_v4(...)
                break;
        }
}

But *this is only a suggestion*, of course.

> +#endif
> +	addr_src &= info->src_mask.ip;
> +	addr_dst &= info->dst_mask.ip;
> +
> +	if ((info->flags & XT_F_HMARK_METHOD_L3) ||
> +	    (ip_proto == IPPROTO_ICMP)) {
> +		uports.v32 = 0;
> +		goto noports;
> +	}
> +	/* Check if ports can be used in hash calculation. */
> +	poff = proto_ports_offset(ip_proto);
> +	if (frag || poff < 0)
> +		return XT_CONTINUE;
> +
> +	/* if --ct not given, get ports from skb */
> +	if (!uports.v32) {
> +		nhoff += (ip->ihl * 4) + poff;
> +		if (skb_copy_bits(skb, nhoff, &uports, sizeof(uports)) < 0)
> +			return XT_CONTINUE;
> +	}
> +
> +	if (ip_proto == IPPROTO_ESP || ip_proto == IPPROTO_AH)
> +		uports.v32 = (uports.v32 & info->spi_mask) | info->spi_set;
> +	else {
> +		uports.v32 = (uports.v32 & info->port_mask.v32) |
> +				info->port_set.v32;
> +		/* get a consistent hash (same value in any flow dirs.) */
> +		if (uports.p16.dst < uports.p16.src)
> +			swap(uports.p16.src, uports.p16.dst);
> +	}
> +
> +noports:
> +	/* get a consistent hash (same value in any flow direction) */
> +	if (addr_dst < addr_src)
> +		swap(addr_src, addr_dst);
> +
> +	hash = jhash_3words(addr_src, addr_dst, uports.v32, info->hashrnd);
> +	hash = hash ^ (ip_proto & info->proto_mask);
> +	skb->mark = (hash % info->hmodulus) + info->hoffset;
> +	return XT_CONTINUE;
> +}
> +
> +static int hmark_check(const struct xt_tgchk_param *par)
> +{
> +	const struct xt_hmark_info *info = par->targinfo;
> +
> +	if (!info->hmodulus) {
> +		pr_info("HMARK: hmark-mod can't be zero\n");
> +		return -EINVAL;
> +	}
> +	if (info->proto_mask && (info->flags & XT_F_HMARK_METHOD_L3)) {
> +		pr_info("HMARK: When method L3 proto mask must be zero\n");
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static struct xt_target hmark_tg_reg[] __read_mostly = {
> +	{
> +		.name           = "HMARK",
> +		.revision       = 0,
> +		.family         = NFPROTO_IPV4,
> +		.target         = hmark_v4,
> +		.targetsize     = sizeof(struct xt_hmark_info),
> +		.checkentry     = hmark_check,
> +		.me             = THIS_MODULE,
> +	},
> +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
> +	{
> +		.name           = "HMARK",
> +		.revision       = 0,
> +		.family         = NFPROTO_IPV6,
> +		.target         = hmark_v6,
> +		.targetsize     = sizeof(struct xt_hmark_info),
> +		.checkentry     = hmark_check,
> +		.me             = THIS_MODULE,
> +	},
> +#endif
> +};
> +
> +static int __init hmark_mt_init(void)
> +{
> +	int ret;
> +
> +	ret = xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
> +	if (ret < 0)
> +		return ret;
> +	return 0;
> +}
> +
> +static void __exit hmark_mt_exit(void)
> +{
> +	xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
> +}
> +
> +module_init(hmark_mt_init);
> +module_exit(hmark_mt_exit);
> -- 
> 1.7.2.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
> the body of a message to [email-masked]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html