2 * xt_HMARK - Netfilter module to set mark by means of hashing
4 * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
5 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
12 #include <linux/module.h>
13 #include <linux/skbuff.h>
14 #include <linux/icmp.h>
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter/xt_HMARK.h>
20 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
21 #include <net/netfilter/nf_conntrack.h>
23 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
25 #include <linux/netfilter_ipv6/ip6_tables.h>
28 MODULE_LICENSE("GPL");
29 MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
30 MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
31 MODULE_ALIAS("ipt_HMARK");
32 MODULE_ALIAS("ip6t_HMARK");
37 union hmark_ports uports
;
41 static inline u32
hmark_addr6_mask(const __u32
*addr32
, const __u32
*mask
)
43 return (addr32
[0] & mask
[0]) ^
44 (addr32
[1] & mask
[1]) ^
45 (addr32
[2] & mask
[2]) ^
46 (addr32
[3] & mask
[3]);
50 hmark_addr_mask(int l3num
, const __u32
*addr32
, const __u32
*mask
)
54 return *addr32
& *mask
;
56 return hmark_addr6_mask(addr32
, mask
);
62 hmark_ct_set_htuple(const struct sk_buff
*skb
, struct hmark_tuple
*t
,
63 const struct xt_hmark_info
*info
)
65 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
66 enum ip_conntrack_info ctinfo
;
67 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
68 struct nf_conntrack_tuple
*otuple
;
69 struct nf_conntrack_tuple
*rtuple
;
71 if (ct
== NULL
|| nf_ct_is_untracked(ct
))
74 otuple
= &ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
;
75 rtuple
= &ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
;
77 t
->src
= hmark_addr_mask(otuple
->src
.l3num
, otuple
->src
.u3
.all
,
79 t
->dst
= hmark_addr_mask(otuple
->src
.l3num
, rtuple
->src
.u3
.all
,
82 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
))
85 t
->proto
= nf_ct_protonum(ct
);
86 if (t
->proto
!= IPPROTO_ICMP
) {
87 t
->uports
.p16
.src
= otuple
->src
.u
.all
;
88 t
->uports
.p16
.dst
= rtuple
->src
.u
.all
;
89 t
->uports
.v32
= (t
->uports
.v32
& info
->port_mask
.v32
) |
91 if (t
->uports
.p16
.dst
< t
->uports
.p16
.src
)
92 swap(t
->uports
.p16
.dst
, t
->uports
.p16
.src
);
102 hmark_hash(struct hmark_tuple
*t
, const struct xt_hmark_info
*info
)
107 swap(t
->src
, t
->dst
);
109 hash
= jhash_3words(t
->src
, t
->dst
, t
->uports
.v32
, info
->hashrnd
);
110 hash
= hash
^ (t
->proto
& info
->proto_mask
);
112 return (((u64
)hash
* info
->hmodulus
) >> 32) + info
->hoffset
;
116 hmark_set_tuple_ports(const struct sk_buff
*skb
, unsigned int nhoff
,
117 struct hmark_tuple
*t
, const struct xt_hmark_info
*info
)
121 protoff
= proto_ports_offset(t
->proto
);
126 if (skb_copy_bits(skb
, nhoff
, &t
->uports
, sizeof(t
->uports
)) < 0)
129 t
->uports
.v32
= (t
->uports
.v32
& info
->port_mask
.v32
) |
132 if (t
->uports
.p16
.dst
< t
->uports
.p16
.src
)
133 swap(t
->uports
.p16
.dst
, t
->uports
.p16
.src
);
136 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
137 static int get_inner6_hdr(const struct sk_buff
*skb
, int *offset
)
139 struct icmp6hdr
*icmp6h
, _ih6
;
141 icmp6h
= skb_header_pointer(skb
, *offset
, sizeof(_ih6
), &_ih6
);
145 if (icmp6h
->icmp6_type
&& icmp6h
->icmp6_type
< 128) {
146 *offset
+= sizeof(struct icmp6hdr
);
153 hmark_pkt_set_htuple_ipv6(const struct sk_buff
*skb
, struct hmark_tuple
*t
,
154 const struct xt_hmark_info
*info
)
156 struct ipv6hdr
*ip6
, _ip6
;
157 int flag
= IP6T_FH_F_AUTH
;
158 unsigned int nhoff
= 0;
162 ip6
= (struct ipv6hdr
*) (skb
->data
+ skb_network_offset(skb
));
163 nexthdr
= ipv6_find_hdr(skb
, &nhoff
, -1, &fragoff
, &flag
);
166 /* No need to check for icmp errors on fragments */
167 if ((flag
& IP6T_FH_F_FRAG
) || (nexthdr
!= IPPROTO_ICMPV6
))
169 /* Use inner header in case of ICMP errors */
170 if (get_inner6_hdr(skb
, &nhoff
)) {
171 ip6
= skb_header_pointer(skb
, nhoff
, sizeof(_ip6
), &_ip6
);
174 /* If AH present, use SPI like in ESP. */
175 flag
= IP6T_FH_F_AUTH
;
176 nexthdr
= ipv6_find_hdr(skb
, &nhoff
, -1, &fragoff
, &flag
);
181 t
->src
= hmark_addr6_mask(ip6
->saddr
.s6_addr32
, info
->src_mask
.all
);
182 t
->dst
= hmark_addr6_mask(ip6
->daddr
.s6_addr32
, info
->dst_mask
.all
);
184 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
))
188 if (t
->proto
== IPPROTO_ICMPV6
)
191 if (flag
& IP6T_FH_F_FRAG
)
194 hmark_set_tuple_ports(skb
, nhoff
, t
, info
);
199 hmark_tg_v6(struct sk_buff
*skb
, const struct xt_action_param
*par
)
201 const struct xt_hmark_info
*info
= par
->targinfo
;
202 struct hmark_tuple t
;
204 memset(&t
, 0, sizeof(struct hmark_tuple
));
206 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_CT
)) {
207 if (hmark_ct_set_htuple(skb
, &t
, info
) < 0)
210 if (hmark_pkt_set_htuple_ipv6(skb
, &t
, info
) < 0)
214 skb
->mark
= hmark_hash(&t
, info
);
219 static int get_inner_hdr(const struct sk_buff
*skb
, int iphsz
, int *nhoff
)
221 const struct icmphdr
*icmph
;
224 /* Not enough header? */
225 icmph
= skb_header_pointer(skb
, *nhoff
+ iphsz
, sizeof(_ih
), &_ih
);
226 if (icmph
== NULL
|| icmph
->type
> NR_ICMP_TYPES
)
230 if (icmph
->type
!= ICMP_DEST_UNREACH
&&
231 icmph
->type
!= ICMP_SOURCE_QUENCH
&&
232 icmph
->type
!= ICMP_TIME_EXCEEDED
&&
233 icmph
->type
!= ICMP_PARAMETERPROB
&&
234 icmph
->type
!= ICMP_REDIRECT
)
237 *nhoff
+= iphsz
+ sizeof(_ih
);
242 hmark_pkt_set_htuple_ipv4(const struct sk_buff
*skb
, struct hmark_tuple
*t
,
243 const struct xt_hmark_info
*info
)
245 struct iphdr
*ip
, _ip
;
246 int nhoff
= skb_network_offset(skb
);
248 ip
= (struct iphdr
*) (skb
->data
+ nhoff
);
249 if (ip
->protocol
== IPPROTO_ICMP
) {
250 /* Use inner header in case of ICMP errors */
251 if (get_inner_hdr(skb
, ip
->ihl
* 4, &nhoff
)) {
252 ip
= skb_header_pointer(skb
, nhoff
, sizeof(_ip
), &_ip
);
258 t
->src
= (__force u32
) ip
->saddr
;
259 t
->dst
= (__force u32
) ip
->daddr
;
261 t
->src
&= info
->src_mask
.ip
;
262 t
->dst
&= info
->dst_mask
.ip
;
264 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
))
267 t
->proto
= ip
->protocol
;
269 /* ICMP has no ports, skip */
270 if (t
->proto
== IPPROTO_ICMP
)
273 /* follow-up fragments don't contain ports, skip all fragments */
274 if (ip
->frag_off
& htons(IP_MF
| IP_OFFSET
))
277 hmark_set_tuple_ports(skb
, (ip
->ihl
* 4) + nhoff
, t
, info
);
283 hmark_tg_v4(struct sk_buff
*skb
, const struct xt_action_param
*par
)
285 const struct xt_hmark_info
*info
= par
->targinfo
;
286 struct hmark_tuple t
;
288 memset(&t
, 0, sizeof(struct hmark_tuple
));
290 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_CT
)) {
291 if (hmark_ct_set_htuple(skb
, &t
, info
) < 0)
294 if (hmark_pkt_set_htuple_ipv4(skb
, &t
, info
) < 0)
298 skb
->mark
= hmark_hash(&t
, info
);
302 static int hmark_tg_check(const struct xt_tgchk_param
*par
)
304 const struct xt_hmark_info
*info
= par
->targinfo
;
306 if (!info
->hmodulus
) {
307 pr_info("xt_HMARK: hash modulus can't be zero\n");
310 if (info
->proto_mask
&&
311 (info
->flags
& XT_HMARK_FLAG(XT_HMARK_METHOD_L3
))) {
312 pr_info("xt_HMARK: proto mask must be zero with L3 mode\n");
315 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_SPI_MASK
) &&
316 (info
->flags
& (XT_HMARK_FLAG(XT_HMARK_SPORT_MASK
) |
317 XT_HMARK_FLAG(XT_HMARK_DPORT_MASK
)))) {
318 pr_info("xt_HMARK: spi-mask and port-mask can't be combined\n");
321 if (info
->flags
& XT_HMARK_FLAG(XT_HMARK_SPI
) &&
322 (info
->flags
& (XT_HMARK_FLAG(XT_HMARK_SPORT
) |
323 XT_HMARK_FLAG(XT_HMARK_DPORT
)))) {
324 pr_info("xt_HMARK: spi-set and port-set can't be combined\n");
330 static struct xt_target hmark_tg_reg
[] __read_mostly
= {
333 .family
= NFPROTO_IPV4
,
334 .target
= hmark_tg_v4
,
335 .targetsize
= sizeof(struct xt_hmark_info
),
336 .checkentry
= hmark_tg_check
,
339 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
342 .family
= NFPROTO_IPV6
,
343 .target
= hmark_tg_v6
,
344 .targetsize
= sizeof(struct xt_hmark_info
),
345 .checkentry
= hmark_tg_check
,
351 static int __init
hmark_tg_init(void)
353 return xt_register_targets(hmark_tg_reg
, ARRAY_SIZE(hmark_tg_reg
));
356 static void __exit
hmark_tg_exit(void)
358 xt_unregister_targets(hmark_tg_reg
, ARRAY_SIZE(hmark_tg_reg
));
361 module_init(hmark_tg_init
);
362 module_exit(hmark_tg_exit
);
This page took 0.037414 seconds and 5 git commands to generate.