1 // SPDX-License-Identifier: GPL-2.0-only
3 * xt_HMARK - Netfilter module to set mark by means of hashing
5 * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
6 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <linux/icmp.h>
15 #include <linux/netfilter/x_tables.h>
16 #include <linux/netfilter/xt_HMARK.h>
19 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
20 #include <net/netfilter/nf_conntrack.h>
22 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
24 #include <linux/netfilter_ipv6/ip6_tables.h>
27 MODULE_LICENSE("GPL");
28 MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
29 MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
30 MODULE_ALIAS("ipt_HMARK");
31 MODULE_ALIAS("ip6t_HMARK");
36 union hmark_ports uports
;
40 static inline __be32
hmark_addr6_mask(const __be32
*addr32
, const __be32
*mask
)
42 return (addr32
[0] & mask
[0]) ^
43 (addr32
[1] & mask
[1]) ^
44 (addr32
[2] & mask
[2]) ^
45 (addr32
[3] & mask
[3]);
49 hmark_addr_mask(int l3num
, const __be32
*addr32
, const __be32
*mask
)
53 return *addr32
& *mask
;
55 return hmark_addr6_mask(addr32
, mask
);
60 static inline void hmark_swap_ports(union hmark_ports
*uports
,
61 const struct xt_hmark_info
*info
)
66 hp
.b32
= (uports
->b32
& info
->port_mask
.b32
) | info
->port_set
.b32
;
67 src
= ntohs(hp
.b16
.src
);
68 dst
= ntohs(hp
.b16
.dst
);
71 uports
->v32
= (dst
<< 16) | src
;
73 uports
->v32
= (src
<< 16) | dst
;
77 hmark_ct_set_htuple(const struct sk_buff
*skb
, struct hmark_tuple
*t
,
78 const struct xt_hmark_info
*info
)
80 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
81 enum ip_conntrack_info ctinfo
;
82 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
83 struct nf_conntrack_tuple
*otuple
;
84 struct nf_conntrack_tuple
*rtuple
;
89 otuple
= &ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
;
90 rtuple
= &ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
;
92 t
->src
= hmark_addr_mask(otuple
->src
.l3num
, otuple
->src
.u3
.ip6
,
94 t
->dst
= hmark_addr_mask(otuple
->src
.l3num
, rtuple
->src
.u3
.ip6
,
97 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
))
100 t
->proto
= nf_ct_protonum(ct
);
101 if (t
->proto
!= IPPROTO_ICMP
) {
102 t
->uports
.b16
.src
= otuple
->src
.u
.all
;
103 t
->uports
.b16
.dst
= rtuple
->src
.u
.all
;
104 hmark_swap_ports(&t
->uports
, info
);
113 /* This hash function is endian independent, to ensure consistent hashing if
114 * the cluster is composed of big and little endian systems. */
116 hmark_hash(struct hmark_tuple
*t
, const struct xt_hmark_info
*info
)
119 u32 src
= ntohl(t
->src
);
120 u32 dst
= ntohl(t
->dst
);
125 hash
= jhash_3words(src
, dst
, t
->uports
.v32
, info
->hashrnd
);
126 hash
= hash
^ (t
->proto
& info
->proto_mask
);
128 return reciprocal_scale(hash
, info
->hmodulus
) + info
->hoffset
;
132 hmark_set_tuple_ports(const struct sk_buff
*skb
, unsigned int nhoff
,
133 struct hmark_tuple
*t
, const struct xt_hmark_info
*info
)
137 protoff
= proto_ports_offset(t
->proto
);
142 if (skb_copy_bits(skb
, nhoff
, &t
->uports
, sizeof(t
->uports
)) < 0)
145 hmark_swap_ports(&t
->uports
, info
);
148 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
149 static int get_inner6_hdr(const struct sk_buff
*skb
, int *offset
)
151 struct icmp6hdr
*icmp6h
, _ih6
;
153 icmp6h
= skb_header_pointer(skb
, *offset
, sizeof(_ih6
), &_ih6
);
157 if (icmp6h
->icmp6_type
&& icmp6h
->icmp6_type
< 128) {
158 *offset
+= sizeof(struct icmp6hdr
);
165 hmark_pkt_set_htuple_ipv6(const struct sk_buff
*skb
, struct hmark_tuple
*t
,
166 const struct xt_hmark_info
*info
)
168 struct ipv6hdr
*ip6
, _ip6
;
169 int flag
= IP6_FH_F_AUTH
;
170 unsigned int nhoff
= 0;
174 ip6
= (struct ipv6hdr
*) (skb
->data
+ skb_network_offset(skb
));
175 nexthdr
= ipv6_find_hdr(skb
, &nhoff
, -1, &fragoff
, &flag
);
178 /* No need to check for icmp errors on fragments */
179 if ((flag
& IP6_FH_F_FRAG
) || (nexthdr
!= IPPROTO_ICMPV6
))
181 /* Use inner header in case of ICMP errors */
182 if (get_inner6_hdr(skb
, &nhoff
)) {
183 ip6
= skb_header_pointer(skb
, nhoff
, sizeof(_ip6
), &_ip6
);
186 /* If AH present, use SPI like in ESP. */
187 flag
= IP6_FH_F_AUTH
;
188 nexthdr
= ipv6_find_hdr(skb
, &nhoff
, -1, &fragoff
, &flag
);
193 t
->src
= hmark_addr6_mask(ip6
->saddr
.s6_addr32
, info
->src_mask
.ip6
);
194 t
->dst
= hmark_addr6_mask(ip6
->daddr
.s6_addr32
, info
->dst_mask
.ip6
);
196 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
))
200 if (t
->proto
== IPPROTO_ICMPV6
)
203 if (flag
& IP6_FH_F_FRAG
)
206 hmark_set_tuple_ports(skb
, nhoff
, t
, info
);
211 hmark_tg_v6(struct sk_buff
*skb
, const struct xt_action_param
*par
)
213 const struct xt_hmark_info
*info
= par
->targinfo
;
214 struct hmark_tuple t
;
216 memset(&t
, 0, sizeof(struct hmark_tuple
));
218 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_CT
)) {
219 if (hmark_ct_set_htuple(skb
, &t
, info
) < 0)
222 if (hmark_pkt_set_htuple_ipv6(skb
, &t
, info
) < 0)
226 skb
->mark
= hmark_hash(&t
, info
);
231 static int get_inner_hdr(const struct sk_buff
*skb
, int iphsz
, int *nhoff
)
233 const struct icmphdr
*icmph
;
236 /* Not enough header? */
237 icmph
= skb_header_pointer(skb
, *nhoff
+ iphsz
, sizeof(_ih
), &_ih
);
238 if (icmph
== NULL
|| icmph
->type
> NR_ICMP_TYPES
)
242 if (!icmp_is_err(icmph
->type
))
245 *nhoff
+= iphsz
+ sizeof(_ih
);
250 hmark_pkt_set_htuple_ipv4(const struct sk_buff
*skb
, struct hmark_tuple
*t
,
251 const struct xt_hmark_info
*info
)
253 struct iphdr
*ip
, _ip
;
254 int nhoff
= skb_network_offset(skb
);
256 ip
= (struct iphdr
*) (skb
->data
+ nhoff
);
257 if (ip
->protocol
== IPPROTO_ICMP
) {
258 /* Use inner header in case of ICMP errors */
259 if (get_inner_hdr(skb
, ip
->ihl
* 4, &nhoff
)) {
260 ip
= skb_header_pointer(skb
, nhoff
, sizeof(_ip
), &_ip
);
266 t
->src
= ip
->saddr
& info
->src_mask
.ip
;
267 t
->dst
= ip
->daddr
& info
->dst_mask
.ip
;
269 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
))
272 t
->proto
= ip
->protocol
;
274 /* ICMP has no ports, skip */
275 if (t
->proto
== IPPROTO_ICMP
)
278 /* follow-up fragments don't contain ports, skip all fragments */
279 if (ip_is_fragment(ip
))
282 hmark_set_tuple_ports(skb
, (ip
->ihl
* 4) + nhoff
, t
, info
);
288 hmark_tg_v4(struct sk_buff
*skb
, const struct xt_action_param
*par
)
290 const struct xt_hmark_info
*info
= par
->targinfo
;
291 struct hmark_tuple t
;
293 memset(&t
, 0, sizeof(struct hmark_tuple
));
295 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_CT
)) {
296 if (hmark_ct_set_htuple(skb
, &t
, info
) < 0)
299 if (hmark_pkt_set_htuple_ipv4(skb
, &t
, info
) < 0)
303 skb
->mark
= hmark_hash(&t
, info
);
307 static int hmark_tg_check(const struct xt_tgchk_param
*par
)
309 const struct xt_hmark_info
*info
= par
->targinfo
;
310 const char *errmsg
= "proto mask must be zero with L3 mode";
315 if (info
->proto_mask
&&
316 (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
)))
319 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_SPI_MASK
) &&
320 (info
->flags
& (XT_HMARK_FLAG(XT_HMARK_SPORT_MASK
) |
321 XT_HMARK_FLAG(XT_HMARK_DPORT_MASK
))))
324 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_SPI
) &&
325 (info
->flags
& (XT_HMARK_FLAG(XT_HMARK_SPORT
) |
326 XT_HMARK_FLAG(XT_HMARK_DPORT
)))) {
327 errmsg
= "spi-set and port-set can't be combined";
332 pr_info_ratelimited("%s\n", errmsg
);
336 static struct xt_target hmark_tg_reg
[] __read_mostly
= {
339 .family
= NFPROTO_IPV4
,
340 .target
= hmark_tg_v4
,
341 .targetsize
= sizeof(struct xt_hmark_info
),
342 .checkentry
= hmark_tg_check
,
345 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
348 .family
= NFPROTO_IPV6
,
349 .target
= hmark_tg_v6
,
350 .targetsize
= sizeof(struct xt_hmark_info
),
351 .checkentry
= hmark_tg_check
,
357 static int __init
hmark_tg_init(void)
359 return xt_register_targets(hmark_tg_reg
, ARRAY_SIZE(hmark_tg_reg
));
362 static void __exit
hmark_tg_exit(void)
364 xt_unregister_targets(hmark_tg_reg
, ARRAY_SIZE(hmark_tg_reg
));
367 module_init(hmark_tg_init
);
368 module_exit(hmark_tg_exit
);