Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * xt_HMARK - Netfilter module to set mark by means of hashing
4 *
5 * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
6 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/module.h>
12#include <linux/skbuff.h>
13#include <linux/icmp.h>
14
15#include <linux/netfilter/x_tables.h>
16#include <linux/netfilter/xt_HMARK.h>
17
18#include <net/ip.h>
19#if IS_ENABLED(CONFIG_NF_CONNTRACK)
20#include <net/netfilter/nf_conntrack.h>
21#endif
22#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
23#include <net/ipv6.h>
24#include <linux/netfilter_ipv6/ip6_tables.h>
25#endif
26
27MODULE_LICENSE("GPL");
28MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
29MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
30MODULE_ALIAS("ipt_HMARK");
31MODULE_ALIAS("ip6t_HMARK");
32
33struct hmark_tuple {
34 __be32 src;
35 __be32 dst;
36 union hmark_ports uports;
37 u8 proto;
38};
39
40static inline __be32 hmark_addr6_mask(const __be32 *addr32, const __be32 *mask)
41{
42 return (addr32[0] & mask[0]) ^
43 (addr32[1] & mask[1]) ^
44 (addr32[2] & mask[2]) ^
45 (addr32[3] & mask[3]);
46}
47
48static inline __be32
49hmark_addr_mask(int l3num, const __be32 *addr32, const __be32 *mask)
50{
51 switch (l3num) {
52 case AF_INET:
53 return *addr32 & *mask;
54 case AF_INET6:
55 return hmark_addr6_mask(addr32, mask);
56 }
57 return 0;
58}
59
60static inline void hmark_swap_ports(union hmark_ports *uports,
61 const struct xt_hmark_info *info)
62{
63 union hmark_ports hp;
64 u16 src, dst;
65
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);
69
70 if (dst > src)
71 uports->v32 = (dst << 16) | src;
72 else
73 uports->v32 = (src << 16) | dst;
74}
75
76static int
77hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,
78 const struct xt_hmark_info *info)
79{
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;
85
86 if (ct == NULL)
87 return -1;
88
89 otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
90 rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
91
92 t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.ip6,
93 info->src_mask.ip6);
94 t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.ip6,
95 info->dst_mask.ip6);
96
97 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
98 return 0;
99
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);
105 }
106
107 return 0;
108#else
109 return -1;
110#endif
111}
112
113/* This hash function is endian independent, to ensure consistent hashing if
114 * the cluster is composed of big and little endian systems. */
115static inline u32
116hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info)
117{
118 u32 hash;
119 u32 src = ntohl(t->src);
120 u32 dst = ntohl(t->dst);
121
122 if (dst < src)
123 swap(src, dst);
124
125 hash = jhash_3words(src, dst, t->uports.v32, info->hashrnd);
126 hash = hash ^ (t->proto & info->proto_mask);
127
128 return reciprocal_scale(hash, info->hmodulus) + info->hoffset;
129}
130
131static void
132hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff,
133 struct hmark_tuple *t, const struct xt_hmark_info *info)
134{
135 int protoff;
136
137 protoff = proto_ports_offset(t->proto);
138 if (protoff < 0)
139 return;
140
141 nhoff += protoff;
142 if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0)
143 return;
144
145 hmark_swap_ports(&t->uports, info);
146}
147
148#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
149static int get_inner6_hdr(const struct sk_buff *skb, int *offset)
150{
151 struct icmp6hdr *icmp6h, _ih6;
152
153 icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6);
154 if (icmp6h == NULL)
155 return 0;
156
157 if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) {
158 *offset += sizeof(struct icmp6hdr);
159 return 1;
160 }
161 return 0;
162}
163
164static int
165hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
166 const struct xt_hmark_info *info)
167{
168 struct ipv6hdr *ip6, _ip6;
169 int flag = IP6_FH_F_AUTH;
170 unsigned int nhoff = 0;
171 u16 fragoff = 0;
172 int nexthdr;
173
174 ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb));
175 nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
176 if (nexthdr < 0)
177 return 0;
178 /* No need to check for icmp errors on fragments */
179 if ((flag & IP6_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
180 goto noicmp;
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);
184 if (ip6 == NULL)
185 return -1;
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);
189 if (nexthdr < 0)
190 return -1;
191 }
192noicmp:
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);
195
196 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
197 return 0;
198
199 t->proto = nexthdr;
200 if (t->proto == IPPROTO_ICMPV6)
201 return 0;
202
203 if (flag & IP6_FH_F_FRAG)
204 return 0;
205
206 hmark_set_tuple_ports(skb, nhoff, t, info);
207 return 0;
208}
209
210static unsigned int
211hmark_tg_v6(struct sk_buff *skb, const struct xt_action_param *par)
212{
213 const struct xt_hmark_info *info = par->targinfo;
214 struct hmark_tuple t;
215
216 memset(&t, 0, sizeof(struct hmark_tuple));
217
218 if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
219 if (hmark_ct_set_htuple(skb, &t, info) < 0)
220 return XT_CONTINUE;
221 } else {
222 if (hmark_pkt_set_htuple_ipv6(skb, &t, info) < 0)
223 return XT_CONTINUE;
224 }
225
226 skb->mark = hmark_hash(&t, info);
227 return XT_CONTINUE;
228}
229#endif
230
231static int get_inner_hdr(const struct sk_buff *skb, int iphsz, int *nhoff)
232{
233 const struct icmphdr *icmph;
234 struct icmphdr _ih;
235
236 /* Not enough header? */
237 icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih);
238 if (icmph == NULL || icmph->type > NR_ICMP_TYPES)
239 return 0;
240
241 /* Error message? */
242 if (!icmp_is_err(icmph->type))
243 return 0;
244
245 *nhoff += iphsz + sizeof(_ih);
246 return 1;
247}
248
249static int
250hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t,
251 const struct xt_hmark_info *info)
252{
253 struct iphdr *ip, _ip;
254 int nhoff = skb_network_offset(skb);
255
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);
261 if (ip == NULL)
262 return -1;
263 }
264 }
265
266 t->src = ip->saddr & info->src_mask.ip;
267 t->dst = ip->daddr & info->dst_mask.ip;
268
269 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
270 return 0;
271
272 t->proto = ip->protocol;
273
274 /* ICMP has no ports, skip */
275 if (t->proto == IPPROTO_ICMP)
276 return 0;
277
278 /* follow-up fragments don't contain ports, skip all fragments */
279 if (ip_is_fragment(ip))
280 return 0;
281
282 hmark_set_tuple_ports(skb, (ip->ihl * 4) + nhoff, t, info);
283
284 return 0;
285}
286
287static unsigned int
288hmark_tg_v4(struct sk_buff *skb, const struct xt_action_param *par)
289{
290 const struct xt_hmark_info *info = par->targinfo;
291 struct hmark_tuple t;
292
293 memset(&t, 0, sizeof(struct hmark_tuple));
294
295 if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
296 if (hmark_ct_set_htuple(skb, &t, info) < 0)
297 return XT_CONTINUE;
298 } else {
299 if (hmark_pkt_set_htuple_ipv4(skb, &t, info) < 0)
300 return XT_CONTINUE;
301 }
302
303 skb->mark = hmark_hash(&t, info);
304 return XT_CONTINUE;
305}
306
307static int hmark_tg_check(const struct xt_tgchk_param *par)
308{
309 const struct xt_hmark_info *info = par->targinfo;
310 const char *errmsg = "proto mask must be zero with L3 mode";
311
312 if (!info->hmodulus)
313 return -EINVAL;
314
315 if (info->proto_mask &&
316 (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)))
317 goto err;
318
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))))
322 return -EINVAL;
323
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";
328 goto err;
329 }
330 return 0;
331err:
332 pr_info_ratelimited("%s\n", errmsg);
333 return -EINVAL;
334}
335
336static struct xt_target hmark_tg_reg[] __read_mostly = {
337 {
338 .name = "HMARK",
339 .family = NFPROTO_IPV4,
340 .target = hmark_tg_v4,
341 .targetsize = sizeof(struct xt_hmark_info),
342 .checkentry = hmark_tg_check,
343 .me = THIS_MODULE,
344 },
345#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
346 {
347 .name = "HMARK",
348 .family = NFPROTO_IPV6,
349 .target = hmark_tg_v6,
350 .targetsize = sizeof(struct xt_hmark_info),
351 .checkentry = hmark_tg_check,
352 .me = THIS_MODULE,
353 },
354#endif
355};
356
357static int __init hmark_tg_init(void)
358{
359 return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
360}
361
362static void __exit hmark_tg_exit(void)
363{
364 xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
365}
366
367module_init(hmark_tg_init);
368module_exit(hmark_tg_exit);
1/*
2 * xt_HMARK - Netfilter module to set mark by means of hashing
3 *
4 * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
5 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
6 *
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.
10 */
11
12#include <linux/module.h>
13#include <linux/skbuff.h>
14#include <linux/icmp.h>
15
16#include <linux/netfilter/x_tables.h>
17#include <linux/netfilter/xt_HMARK.h>
18
19#include <net/ip.h>
20#if IS_ENABLED(CONFIG_NF_CONNTRACK)
21#include <net/netfilter/nf_conntrack.h>
22#endif
23#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
24#include <net/ipv6.h>
25#include <linux/netfilter_ipv6/ip6_tables.h>
26#endif
27
28MODULE_LICENSE("GPL");
29MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
30MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
31MODULE_ALIAS("ipt_HMARK");
32MODULE_ALIAS("ip6t_HMARK");
33
34struct hmark_tuple {
35 __be32 src;
36 __be32 dst;
37 union hmark_ports uports;
38 u8 proto;
39};
40
41static inline __be32 hmark_addr6_mask(const __be32 *addr32, const __be32 *mask)
42{
43 return (addr32[0] & mask[0]) ^
44 (addr32[1] & mask[1]) ^
45 (addr32[2] & mask[2]) ^
46 (addr32[3] & mask[3]);
47}
48
49static inline __be32
50hmark_addr_mask(int l3num, const __be32 *addr32, const __be32 *mask)
51{
52 switch (l3num) {
53 case AF_INET:
54 return *addr32 & *mask;
55 case AF_INET6:
56 return hmark_addr6_mask(addr32, mask);
57 }
58 return 0;
59}
60
61static 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;
75}
76
77static int
78hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,
79 const struct xt_hmark_info *info)
80{
81#if IS_ENABLED(CONFIG_NF_CONNTRACK)
82 enum ip_conntrack_info ctinfo;
83 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
84 struct nf_conntrack_tuple *otuple;
85 struct nf_conntrack_tuple *rtuple;
86
87 if (ct == NULL || nf_ct_is_untracked(ct))
88 return -1;
89
90 otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
91 rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
92
93 t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.ip6,
94 info->src_mask.ip6);
95 t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.ip6,
96 info->dst_mask.ip6);
97
98 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
99 return 0;
100
101 t->proto = nf_ct_protonum(ct);
102 if (t->proto != IPPROTO_ICMP) {
103 t->uports.b16.src = otuple->src.u.all;
104 t->uports.b16.dst = rtuple->src.u.all;
105 hmark_swap_ports(&t->uports, info);
106 }
107
108 return 0;
109#else
110 return -1;
111#endif
112}
113
114/* This hash function is endian independent, to ensure consistent hashing if
115 * the cluster is composed of big and little endian systems. */
116static inline u32
117hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info)
118{
119 u32 hash;
120 u32 src = ntohl(t->src);
121 u32 dst = ntohl(t->dst);
122
123 if (dst < src)
124 swap(src, dst);
125
126 hash = jhash_3words(src, dst, t->uports.v32, info->hashrnd);
127 hash = hash ^ (t->proto & info->proto_mask);
128
129 return (((u64)hash * info->hmodulus) >> 32) + info->hoffset;
130}
131
132static void
133hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff,
134 struct hmark_tuple *t, const struct xt_hmark_info *info)
135{
136 int protoff;
137
138 protoff = proto_ports_offset(t->proto);
139 if (protoff < 0)
140 return;
141
142 nhoff += protoff;
143 if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0)
144 return;
145
146 hmark_swap_ports(&t->uports, info);
147}
148
149#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
150static int get_inner6_hdr(const struct sk_buff *skb, int *offset)
151{
152 struct icmp6hdr *icmp6h, _ih6;
153
154 icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6);
155 if (icmp6h == NULL)
156 return 0;
157
158 if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) {
159 *offset += sizeof(struct icmp6hdr);
160 return 1;
161 }
162 return 0;
163}
164
165static int
166hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
167 const struct xt_hmark_info *info)
168{
169 struct ipv6hdr *ip6, _ip6;
170 int flag = IP6T_FH_F_AUTH;
171 unsigned int nhoff = 0;
172 u16 fragoff = 0;
173 int nexthdr;
174
175 ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb));
176 nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
177 if (nexthdr < 0)
178 return 0;
179 /* No need to check for icmp errors on fragments */
180 if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
181 goto noicmp;
182 /* Use inner header in case of ICMP errors */
183 if (get_inner6_hdr(skb, &nhoff)) {
184 ip6 = skb_header_pointer(skb, nhoff, sizeof(_ip6), &_ip6);
185 if (ip6 == NULL)
186 return -1;
187 /* If AH present, use SPI like in ESP. */
188 flag = IP6T_FH_F_AUTH;
189 nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
190 if (nexthdr < 0)
191 return -1;
192 }
193noicmp:
194 t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.ip6);
195 t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.ip6);
196
197 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
198 return 0;
199
200 t->proto = nexthdr;
201 if (t->proto == IPPROTO_ICMPV6)
202 return 0;
203
204 if (flag & IP6T_FH_F_FRAG)
205 return 0;
206
207 hmark_set_tuple_ports(skb, nhoff, t, info);
208 return 0;
209}
210
211static unsigned int
212hmark_tg_v6(struct sk_buff *skb, const struct xt_action_param *par)
213{
214 const struct xt_hmark_info *info = par->targinfo;
215 struct hmark_tuple t;
216
217 memset(&t, 0, sizeof(struct hmark_tuple));
218
219 if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
220 if (hmark_ct_set_htuple(skb, &t, info) < 0)
221 return XT_CONTINUE;
222 } else {
223 if (hmark_pkt_set_htuple_ipv6(skb, &t, info) < 0)
224 return XT_CONTINUE;
225 }
226
227 skb->mark = hmark_hash(&t, info);
228 return XT_CONTINUE;
229}
230#endif
231
232static int get_inner_hdr(const struct sk_buff *skb, int iphsz, int *nhoff)
233{
234 const struct icmphdr *icmph;
235 struct icmphdr _ih;
236
237 /* Not enough header? */
238 icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih);
239 if (icmph == NULL || icmph->type > NR_ICMP_TYPES)
240 return 0;
241
242 /* Error message? */
243 if (icmph->type != ICMP_DEST_UNREACH &&
244 icmph->type != ICMP_SOURCE_QUENCH &&
245 icmph->type != ICMP_TIME_EXCEEDED &&
246 icmph->type != ICMP_PARAMETERPROB &&
247 icmph->type != ICMP_REDIRECT)
248 return 0;
249
250 *nhoff += iphsz + sizeof(_ih);
251 return 1;
252}
253
254static int
255hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t,
256 const struct xt_hmark_info *info)
257{
258 struct iphdr *ip, _ip;
259 int nhoff = skb_network_offset(skb);
260
261 ip = (struct iphdr *) (skb->data + nhoff);
262 if (ip->protocol == IPPROTO_ICMP) {
263 /* Use inner header in case of ICMP errors */
264 if (get_inner_hdr(skb, ip->ihl * 4, &nhoff)) {
265 ip = skb_header_pointer(skb, nhoff, sizeof(_ip), &_ip);
266 if (ip == NULL)
267 return -1;
268 }
269 }
270
271 t->src = ip->saddr & info->src_mask.ip;
272 t->dst = ip->daddr & info->dst_mask.ip;
273
274 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
275 return 0;
276
277 t->proto = ip->protocol;
278
279 /* ICMP has no ports, skip */
280 if (t->proto == IPPROTO_ICMP)
281 return 0;
282
283 /* follow-up fragments don't contain ports, skip all fragments */
284 if (ip->frag_off & htons(IP_MF | IP_OFFSET))
285 return 0;
286
287 hmark_set_tuple_ports(skb, (ip->ihl * 4) + nhoff, t, info);
288
289 return 0;
290}
291
292static unsigned int
293hmark_tg_v4(struct sk_buff *skb, const struct xt_action_param *par)
294{
295 const struct xt_hmark_info *info = par->targinfo;
296 struct hmark_tuple t;
297
298 memset(&t, 0, sizeof(struct hmark_tuple));
299
300 if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
301 if (hmark_ct_set_htuple(skb, &t, info) < 0)
302 return XT_CONTINUE;
303 } else {
304 if (hmark_pkt_set_htuple_ipv4(skb, &t, info) < 0)
305 return XT_CONTINUE;
306 }
307
308 skb->mark = hmark_hash(&t, info);
309 return XT_CONTINUE;
310}
311
312static int hmark_tg_check(const struct xt_tgchk_param *par)
313{
314 const struct xt_hmark_info *info = par->targinfo;
315
316 if (!info->hmodulus) {
317 pr_info("xt_HMARK: hash modulus can't be zero\n");
318 return -EINVAL;
319 }
320 if (info->proto_mask &&
321 (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))) {
322 pr_info("xt_HMARK: proto mask must be zero with L3 mode\n");
323 return -EINVAL;
324 }
325 if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI_MASK) &&
326 (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT_MASK) |
327 XT_HMARK_FLAG(XT_HMARK_DPORT_MASK)))) {
328 pr_info("xt_HMARK: spi-mask and port-mask can't be combined\n");
329 return -EINVAL;
330 }
331 if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI) &&
332 (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT) |
333 XT_HMARK_FLAG(XT_HMARK_DPORT)))) {
334 pr_info("xt_HMARK: spi-set and port-set can't be combined\n");
335 return -EINVAL;
336 }
337 return 0;
338}
339
340static struct xt_target hmark_tg_reg[] __read_mostly = {
341 {
342 .name = "HMARK",
343 .family = NFPROTO_IPV4,
344 .target = hmark_tg_v4,
345 .targetsize = sizeof(struct xt_hmark_info),
346 .checkentry = hmark_tg_check,
347 .me = THIS_MODULE,
348 },
349#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
350 {
351 .name = "HMARK",
352 .family = NFPROTO_IPV6,
353 .target = hmark_tg_v6,
354 .targetsize = sizeof(struct xt_hmark_info),
355 .checkentry = hmark_tg_check,
356 .me = THIS_MODULE,
357 },
358#endif
359};
360
361static int __init hmark_tg_init(void)
362{
363 return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
364}
365
366static void __exit hmark_tg_exit(void)
367{
368 xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
369}
370
371module_init(hmark_tg_init);
372module_exit(hmark_tg_exit);