xt_HMARK.c 9.1 KB

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