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

Configure Feed

Select the types of activity you want to include in your feed.

at v3.5-rc1 362 lines 9.1 kB view raw
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 u32 src; 36 u32 dst; 37 union hmark_ports uports; 38 uint8_t proto; 39}; 40 41static 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 49static inline u32 50hmark_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 61static int 62hmark_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 101static inline u32 102hmark_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 (((u64)hash * info->hmodulus) >> 32) + info->hoffset; 113} 114 115static void 116hmark_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) 137static 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 152static int 153hmark_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 } 180noicmp: 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 198static unsigned int 199hmark_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 219static 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 241static int 242hmark_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 282static unsigned int 283hmark_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 302static 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 330static 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 351static int __init hmark_tg_init(void) 352{ 353 return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg)); 354} 355 356static void __exit hmark_tg_exit(void) 357{ 358 xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg)); 359} 360 361module_init(hmark_tg_init); 362module_exit(hmark_tg_exit);