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

netfilter: add xt_hmark target for hash-based skb marking

The target allows you to create rules in the "raw" and "mangle" tables
which set the skbuff mark by means of hash calculation within a given
range. The nfmark can influence the routing method (see "Use netfilter
MARK value as routing key") and can also be used by other subsystems to
change their behaviour.

[ Part of this patch has been refactorized and modified by Pablo Neira Ayuso ]

Signed-off-by: Hans Schillstrom <hans.schillstrom@ericsson.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Hans Schillstrom and committed by
Pablo Neira Ayuso
cf308a1f 84018f55

+423
+45
include/linux/netfilter/xt_HMARK.h
··· 1 + #ifndef XT_HMARK_H_ 2 + #define XT_HMARK_H_ 3 + 4 + #include <linux/types.h> 5 + 6 + enum { 7 + XT_HMARK_SADDR_MASK, 8 + XT_HMARK_DADDR_MASK, 9 + XT_HMARK_SPI, 10 + XT_HMARK_SPI_MASK, 11 + XT_HMARK_SPORT, 12 + XT_HMARK_DPORT, 13 + XT_HMARK_SPORT_MASK, 14 + XT_HMARK_DPORT_MASK, 15 + XT_HMARK_PROTO_MASK, 16 + XT_HMARK_RND, 17 + XT_HMARK_MODULUS, 18 + XT_HMARK_OFFSET, 19 + XT_HMARK_CT, 20 + XT_HMARK_METHOD_L3, 21 + XT_HMARK_METHOD_L3_4, 22 + }; 23 + #define XT_HMARK_FLAG(flag) (1 << flag) 24 + 25 + union hmark_ports { 26 + struct { 27 + __u16 src; 28 + __u16 dst; 29 + } p16; 30 + __u32 v32; 31 + }; 32 + 33 + struct xt_hmark_info { 34 + union nf_inet_addr src_mask; 35 + union nf_inet_addr dst_mask; 36 + union hmark_ports port_mask; 37 + union hmark_ports port_set; 38 + __u32 flags; 39 + __u16 proto_mask; 40 + __u32 hashrnd; 41 + __u32 hmodulus; 42 + __u32 hoffset; /* Mark offset to start from */ 43 + }; 44 + 45 + #endif /* XT_HMARK_H_ */
+15
net/netfilter/Kconfig
··· 509 509 since you can easily create immortal packets that loop 510 510 forever on the network. 511 511 512 + config NETFILTER_XT_TARGET_HMARK 513 + tristate '"HMARK" target support' 514 + depends on (IP6_NF_IPTABLES || IP6_NF_IPTABLES=n) 515 + depends on NETFILTER_ADVANCED 516 + ---help--- 517 + This option adds the "HMARK" target. 518 + 519 + The target allows you to create rules in the "raw" and "mangle" tables 520 + which set the skbuff mark by means of hash calculation within a given 521 + range. The nfmark can influence the routing method (see "Use netfilter 522 + MARK value as routing key") and can also be used by other subsystems to 523 + change their behaviour. 524 + 525 + To compile it as a module, choose M here. If unsure, say N. 526 + 512 527 config NETFILTER_XT_TARGET_IDLETIMER 513 528 tristate "IDLETIMER target support" 514 529 depends on NETFILTER_ADVANCED
+1
net/netfilter/Makefile
··· 59 59 obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o 60 60 obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o 61 61 obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o 62 + obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o 62 63 obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o 63 64 obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o 64 65 obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
+362
net/netfilter/xt_HMARK.c
··· 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 + 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"); 33 + 34 + struct hmark_tuple { 35 + u32 src; 36 + u32 dst; 37 + union hmark_ports uports; 38 + uint8_t proto; 39 + }; 40 + 41 + static inline u32 hmark_addr6_mask(const __u32 *addr32, const __u32 *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 + 49 + static inline u32 50 + hmark_addr_mask(int l3num, const __u32 *addr32, const __u32 *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 + 61 + static int 62 + hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t, 63 + const struct xt_hmark_info *info) 64 + { 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; 70 + 71 + if (ct == NULL || nf_ct_is_untracked(ct)) 72 + return -1; 73 + 74 + otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 75 + rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 76 + 77 + t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.all, 78 + info->src_mask.all); 79 + t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.all, 80 + info->dst_mask.all); 81 + 82 + if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)) 83 + return 0; 84 + 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) | 90 + info->port_set.v32; 91 + if (t->uports.p16.dst < t->uports.p16.src) 92 + swap(t->uports.p16.dst, t->uports.p16.src); 93 + } 94 + 95 + return 0; 96 + #else 97 + return -1; 98 + #endif 99 + } 100 + 101 + static inline u32 102 + hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info) 103 + { 104 + u32 hash; 105 + 106 + if (t->dst < t->src) 107 + swap(t->src, t->dst); 108 + 109 + hash = jhash_3words(t->src, t->dst, t->uports.v32, info->hashrnd); 110 + hash = hash ^ (t->proto & info->proto_mask); 111 + 112 + return (hash % info->hmodulus) + info->hoffset; 113 + } 114 + 115 + static void 116 + hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff, 117 + struct hmark_tuple *t, const struct xt_hmark_info *info) 118 + { 119 + int protoff; 120 + 121 + protoff = proto_ports_offset(t->proto); 122 + if (protoff < 0) 123 + return; 124 + 125 + nhoff += protoff; 126 + if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0) 127 + return; 128 + 129 + t->uports.v32 = (t->uports.v32 & info->port_mask.v32) | 130 + info->port_set.v32; 131 + 132 + if (t->uports.p16.dst < t->uports.p16.src) 133 + swap(t->uports.p16.dst, t->uports.p16.src); 134 + } 135 + 136 + #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 137 + static int get_inner6_hdr(const struct sk_buff *skb, int *offset) 138 + { 139 + struct icmp6hdr *icmp6h, _ih6; 140 + 141 + icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6); 142 + if (icmp6h == NULL) 143 + return 0; 144 + 145 + if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) { 146 + *offset += sizeof(struct icmp6hdr); 147 + return 1; 148 + } 149 + return 0; 150 + } 151 + 152 + static int 153 + hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t, 154 + const struct xt_hmark_info *info) 155 + { 156 + struct ipv6hdr *ip6, _ip6; 157 + int flag = IP6T_FH_F_AUTH; 158 + unsigned int nhoff = 0; 159 + u16 fragoff = 0; 160 + int nexthdr; 161 + 162 + ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb)); 163 + nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag); 164 + if (nexthdr < 0) 165 + return 0; 166 + /* No need to check for icmp errors on fragments */ 167 + if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6)) 168 + goto noicmp; 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); 172 + if (ip6 == NULL) 173 + return -1; 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); 177 + if (nexthdr < 0) 178 + return -1; 179 + } 180 + noicmp: 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); 183 + 184 + if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)) 185 + return 0; 186 + 187 + t->proto = nexthdr; 188 + if (t->proto == IPPROTO_ICMPV6) 189 + return 0; 190 + 191 + if (flag & IP6T_FH_F_FRAG) 192 + return 0; 193 + 194 + hmark_set_tuple_ports(skb, nhoff, t, info); 195 + return 0; 196 + } 197 + 198 + static unsigned int 199 + hmark_tg_v6(struct sk_buff *skb, const struct xt_action_param *par) 200 + { 201 + const struct xt_hmark_info *info = par->targinfo; 202 + struct hmark_tuple t; 203 + 204 + memset(&t, 0, sizeof(struct hmark_tuple)); 205 + 206 + if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) { 207 + if (hmark_ct_set_htuple(skb, &t, info) < 0) 208 + return XT_CONTINUE; 209 + } else { 210 + if (hmark_pkt_set_htuple_ipv6(skb, &t, info) < 0) 211 + return XT_CONTINUE; 212 + } 213 + 214 + skb->mark = hmark_hash(&t, info); 215 + return XT_CONTINUE; 216 + } 217 + #endif 218 + 219 + static int get_inner_hdr(const struct sk_buff *skb, int iphsz, int *nhoff) 220 + { 221 + const struct icmphdr *icmph; 222 + struct icmphdr _ih; 223 + 224 + /* Not enough header? */ 225 + icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih); 226 + if (icmph == NULL && icmph->type > NR_ICMP_TYPES) 227 + return 0; 228 + 229 + /* Error message? */ 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) 235 + return 0; 236 + 237 + *nhoff += iphsz + sizeof(_ih); 238 + return 1; 239 + } 240 + 241 + static int 242 + hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t, 243 + const struct xt_hmark_info *info) 244 + { 245 + struct iphdr *ip, _ip; 246 + int nhoff = skb_network_offset(skb); 247 + 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); 253 + if (ip == NULL) 254 + return -1; 255 + } 256 + } 257 + 258 + t->src = (__force u32) ip->saddr; 259 + t->dst = (__force u32) ip->daddr; 260 + 261 + t->src &= info->src_mask.ip; 262 + t->dst &= info->dst_mask.ip; 263 + 264 + if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3)) 265 + return 0; 266 + 267 + t->proto = ip->protocol; 268 + 269 + /* ICMP has no ports, skip */ 270 + if (t->proto == IPPROTO_ICMP) 271 + return 0; 272 + 273 + /* follow-up fragments don't contain ports, skip all fragments */ 274 + if (ip->frag_off & htons(IP_MF | IP_OFFSET)) 275 + return 0; 276 + 277 + hmark_set_tuple_ports(skb, (ip->ihl * 4) + nhoff, t, info); 278 + 279 + return 0; 280 + } 281 + 282 + static unsigned int 283 + hmark_tg_v4(struct sk_buff *skb, const struct xt_action_param *par) 284 + { 285 + const struct xt_hmark_info *info = par->targinfo; 286 + struct hmark_tuple t; 287 + 288 + memset(&t, 0, sizeof(struct hmark_tuple)); 289 + 290 + if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) { 291 + if (hmark_ct_set_htuple(skb, &t, info) < 0) 292 + return XT_CONTINUE; 293 + } else { 294 + if (hmark_pkt_set_htuple_ipv4(skb, &t, info) < 0) 295 + return XT_CONTINUE; 296 + } 297 + 298 + skb->mark = hmark_hash(&t, info); 299 + return XT_CONTINUE; 300 + } 301 + 302 + static int hmark_tg_check(const struct xt_tgchk_param *par) 303 + { 304 + const struct xt_hmark_info *info = par->targinfo; 305 + 306 + if (!info->hmodulus) { 307 + pr_info("xt_HMARK: hash modulus can't be zero\n"); 308 + return -EINVAL; 309 + } 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"); 313 + return -EINVAL; 314 + } 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"); 319 + return -EINVAL; 320 + } 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"); 325 + return -EINVAL; 326 + } 327 + return 0; 328 + } 329 + 330 + static struct xt_target hmark_tg_reg[] __read_mostly = { 331 + { 332 + .name = "HMARK", 333 + .family = NFPROTO_IPV4, 334 + .target = hmark_tg_v4, 335 + .targetsize = sizeof(struct xt_hmark_info), 336 + .checkentry = hmark_tg_check, 337 + .me = THIS_MODULE, 338 + }, 339 + #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 340 + { 341 + .name = "HMARK", 342 + .family = NFPROTO_IPV6, 343 + .target = hmark_tg_v6, 344 + .targetsize = sizeof(struct xt_hmark_info), 345 + .checkentry = hmark_tg_check, 346 + .me = THIS_MODULE, 347 + }, 348 + #endif 349 + }; 350 + 351 + static int __init hmark_tg_init(void) 352 + { 353 + return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg)); 354 + } 355 + 356 + static void __exit hmark_tg_exit(void) 357 + { 358 + xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg)); 359 + } 360 + 361 + module_init(hmark_tg_init); 362 + module_exit(hmark_tg_exit);