1 // SPDX-License-Identifier: GPL-2.0-only
3 * xt_HMARK - Netfilter module to set mark by means of hashing
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");
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);