Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: xt_HMARK: fix endianness and provide consistent hashing

This patch addresses two issues:

a) Fix usage of u32 and __be32 that causes endianess warnings via sparse.
b) Ensure consistent hashing in a cluster that is composed of big and
little endian systems. Thus, we obtain the same hash mark in an
heterogeneous cluster.

Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Hans Schillstrom <hans@schillstrom.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Hans Schillstrom and committed by
Pablo Neira Ayuso
d1992b16 da2e8526

+46 -31
+5
include/linux/netfilter/xt_HMARK.h
··· 27 27 __u16 src; 28 28 __u16 dst; 29 29 } p16; 30 + struct { 31 + __be16 src; 32 + __be16 dst; 33 + } b16; 30 34 __u32 v32; 35 + __be32 b32; 31 36 }; 32 37 33 38 struct xt_hmark_info {
+41 -31
net/netfilter/xt_HMARK.c
··· 32 32 MODULE_ALIAS("ip6t_HMARK"); 33 33 34 34 struct hmark_tuple { 35 - u32 src; 36 - u32 dst; 35 + __be32 src; 36 + __be32 dst; 37 37 union hmark_ports uports; 38 - uint8_t proto; 38 + u8 proto; 39 39 }; 40 40 41 - static inline u32 hmark_addr6_mask(const __u32 *addr32, const __u32 *mask) 41 + static inline __be32 hmark_addr6_mask(const __be32 *addr32, const __be32 *mask) 42 42 { 43 43 return (addr32[0] & mask[0]) ^ 44 44 (addr32[1] & mask[1]) ^ ··· 46 46 (addr32[3] & mask[3]); 47 47 } 48 48 49 - static inline u32 50 - hmark_addr_mask(int l3num, const __u32 *addr32, const __u32 *mask) 49 + static inline __be32 50 + hmark_addr_mask(int l3num, const __be32 *addr32, const __be32 *mask) 51 51 { 52 52 switch (l3num) { 53 53 case AF_INET: ··· 56 56 return hmark_addr6_mask(addr32, mask); 57 57 } 58 58 return 0; 59 + } 60 + 61 + static inline void hmark_swap_ports(union hmark_ports *uports, 62 + const struct xt_hmark_info *info) 63 + { 64 + union hmark_ports hp; 65 + u16 src, dst; 66 + 67 + hp.b32 = (uports->b32 & info->port_mask.b32) | info->port_set.b32; 68 + src = ntohs(hp.b16.src); 69 + dst = ntohs(hp.b16.dst); 70 + 71 + if (dst > src) 72 + uports->v32 = (dst << 16) | src; 73 + else 74 + uports->v32 = (src << 16) | dst; 59 75 } 60 76 61 77 static int ··· 90 74 otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 91 75 rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 92 76 93 - t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.all, 94 - info->src_mask.all); 95 - t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.all, 96 - info->dst_mask.all); 77 + t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.ip6, 78 + info->src_mask.ip6); 79 + t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.ip6, 80 + info->dst_mask.ip6); 97 81 98 82 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)) 99 83 return 0; 100 84 101 85 t->proto = nf_ct_protonum(ct); 102 86 if (t->proto != IPPROTO_ICMP) { 103 - t->uports.p16.src = otuple->src.u.all; 104 - t->uports.p16.dst = rtuple->src.u.all; 105 - t->uports.v32 = (t->uports.v32 & info->port_mask.v32) | 106 - info->port_set.v32; 107 - if (t->uports.p16.dst < t->uports.p16.src) 108 - swap(t->uports.p16.dst, t->uports.p16.src); 87 + t->uports.b16.src = otuple->src.u.all; 88 + t->uports.b16.dst = rtuple->src.u.all; 89 + hmark_swap_ports(&t->uports, info); 109 90 } 110 91 111 92 return 0; ··· 111 98 #endif 112 99 } 113 100 101 + /* This hash function is endian independent, to ensure consistent hashing if 102 + * the cluster is composed of big and little endian systems. */ 114 103 static inline u32 115 104 hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info) 116 105 { 117 106 u32 hash; 107 + u32 src = ntohl(t->src); 108 + u32 dst = ntohl(t->dst); 118 109 119 - if (t->dst < t->src) 120 - swap(t->src, t->dst); 110 + if (dst < src) 111 + swap(src, dst); 121 112 122 - hash = jhash_3words(t->src, t->dst, t->uports.v32, info->hashrnd); 113 + hash = jhash_3words(src, dst, t->uports.v32, info->hashrnd); 123 114 hash = hash ^ (t->proto & info->proto_mask); 124 115 125 116 return (((u64)hash * info->hmodulus) >> 32) + info->hoffset; ··· 143 126 if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0) 144 127 return; 145 128 146 - t->uports.v32 = (t->uports.v32 & info->port_mask.v32) | 147 - info->port_set.v32; 148 - 149 - if (t->uports.p16.dst < t->uports.p16.src) 150 - swap(t->uports.p16.dst, t->uports.p16.src); 129 + hmark_swap_ports(&t->uports, info); 151 130 } 152 131 153 132 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) ··· 191 178 return -1; 192 179 } 193 180 noicmp: 194 - t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.all); 195 - t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.all); 181 + t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.ip6); 182 + t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.ip6); 196 183 197 184 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)) 198 185 return 0; ··· 268 255 } 269 256 } 270 257 271 - t->src = (__force u32) ip->saddr; 272 - t->dst = (__force u32) ip->daddr; 273 - 274 - t->src &= info->src_mask.ip; 275 - t->dst &= info->dst_mask.ip; 258 + t->src = ip->saddr & info->src_mask.ip; 259 + t->dst = ip->daddr & info->dst_mask.ip; 276 260 277 261 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)) 278 262 return 0;