@@ -0,0 +1,469 @@
+/*
+ * Shared library add-on to iptables to add HMARK target support.
+ *
+ * The kernel module calculates a hash value that can be modified by modulus
+ * and an offset. The hash value is based on a direction independent
+ * five tuple: src & dst addr src & dst ports and protocol.
+ * However src & dst port can be masked and are not used for fragmented
+ * packets, ESP and AH don't have ports so SPI will be used instead.
+ * For ICMP error messages the hash mark values will be calculated on
+ * the source packet i.e. the packet caused the error (If sufficient
+ * amount of data exists).
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "xtables.h"
+#include <linux/netfilter/xt_HMARK.h>
+
+
+#define DEF_HRAND 0xc175a3b8 /* Default "random" value to jhash */
+
+#define XT_F_HMARK_L4_OPTS (XT_F_HMARK_SPI_AND | XT_F_HMARK_SPI_OR\
+ | XT_F_HMARK_SPORT_AND | XT_F_HMARK_SPORT_OR\
+ | XT_F_HMARK_DPORT_AND | XT_F_HMARK_DPORT_OR\
+ | XT_F_HMARK_PROTO_AND)
+
+static void HMARK_help(void)
+{
+ printf(
+"HMARK target options, i.e. modify hash calculation by:\n"
+" --hmark-method <method> Overall L3/L4 and fragment behavior\n"
+" L3 Fragment safe, do not use ports or proto\n"
+" i.e. Fragments don't need special care.\n"
+" L3-4 (Default) Fragment unsafe, use ports and proto\n"
+" if defrag off in conntrack\n"
+" no hmark on any part of a fragment\n"
+" Limit/modify the calculated hash mark by:\n"
+" --hmark-mod value nfmark modulus value\n"
+" --hmark-offset value Last action add value to nfmark\n\n"
+" Fine tuning of what will be included in hash calculation\n"
+" --hmark-src-mask length Source address mask length\n"
+" --hmark-dst-mask length Dest address mask length\n"
+" --hmark-sport-mask value Mask src port with value\n"
+" --hmark-dport-mask value Mask dst port with value\n"
+" --hmark-spi-mask value For esp and ah AND spi with value\n"
+" --hmark-sport-set value OR src port with value\n"
+" --hmark-dport-set value OR dst port with value\n"
+" --hmark-spi-set value For esp and ah OR spi with value\n"
+" --hmark-proto-mask value Mask Protocol with value\n"
+" --hmark-rnd Initial Random value to hash cacl.\n"
+" For NAT in IPv4: src part from original/reply tuple will always be used\n"
+" i.e. orig src part will be used as src address/port.\n"
+" reply src part will be used as dst address/port\n"
+" Make sure to qualify the rule in a proper way when using NAT flag\n"
+" When --ct is used only tracked connections will match\n"
+" --hmark-ct Force conntrack orig and rely tuples as\n"
+" source and destination.\n\n"
+" In many cases hmark can be omitted i.e. --src-mask can be used\n");
+}
+
+#define hi struct xt_hmark_info
+
+static const struct xt_option_entry HMARK_opts[]= {
+ { .name = "hmark-method",
+ .type = XTTYPE_STRING,
+ .id = XT_HMARK_METHOD_L3
+ },
+ { .name = "hmark-src-mask",
+ .type = XTTYPE_PLENMASK,
+ .id = XT_HMARK_SADR_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, src_mask)
+ },
+ { .name = "hmark-dst-mask",
+ .type = XTTYPE_PLENMASK,
+ .id = XT_HMARK_DADR_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, dst_mask)
+ },
+ { .name = "hmark-sport-mask",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_SPORT_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_mask.p16.src)
+ },
+ { .name = "hmark-dport-mask",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_DPORT_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_mask.p16.dst)
+ },
+ { .name = "hmark-spi-mask",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_SPI_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, spi_mask)
+ },
+ { .name = "hmark-sport-set",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_SPORT_OR,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_set.p16.src)
+ },
+ { .name = "hmark-dport-set",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_DPORT_OR,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_set.p16.dst)
+ },
+ { .name = "hmark-spi-set",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_SPI_OR,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, spi_set)
+ },
+ { .name = "hmark-proto-mask",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_PROTO_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, proto_mask)
+ },
+ { .name = "hmark-rnd",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_RND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, hashrnd)
+ },
+ { .name = "hmark-mod",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_MODULUS,
+ .min = 1,
+ .flags = XTOPT_PUT | XTOPT_MAND,
+ XTOPT_POINTER(hi, hmodulus)
+ },
+ { .name = "hmark-offset",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_OFFSET,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, hoffset)
+ },
+ { .name = "hmark-ct",
+ .type = XTTYPE_NONE,
+ .id = XT_HMARK_CT
+ },
+
+ { .name = "method",
+ .type = XTTYPE_STRING,
+ .id = XT_HMARK_METHOD_L3
+ },
+ { .name = "src-mask",
+ .type = XTTYPE_PLENMASK,
+ .id = XT_HMARK_SADR_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, src_mask)
+ },
+ { .name = "dst-mask",
+ .type = XTTYPE_PLENMASK,
+ .id = XT_HMARK_DADR_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, dst_mask)
+ },
+ { .name = "sport-mask",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_SPORT_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_mask.p16.src)
+ },
+ { .name = "dport-mask", .type = XTTYPE_UINT16,
+ .id = XT_HMARK_DPORT_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_mask.p16.dst)
+ },
+ { .name = "spi-mask",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_SPI_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, spi_mask)
+ },
+ { .name = "sport-set",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_SPORT_OR,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_set.p16.src)
+ },
+ { .name = "dport-set",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_DPORT_OR,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, port_set.p16.dst)
+ },
+ { .name = "spi-set",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_SPI_OR,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, spi_set)
+ },
+ { .name = "proto-mask",
+ .type = XTTYPE_UINT16,
+ .id = XT_HMARK_PROTO_AND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, proto_mask)
+ },
+ { .name = "rnd",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_RND,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, hashrnd)
+ },
+ { .name = "mod",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_MODULUS,
+ .min = 1,
+ .flags = XTOPT_PUT,
+ XTOPT_MAND, XTOPT_POINTER(hi, hmodulus)
+ },
+ { .name = "offset",
+ .type = XTTYPE_UINT32,
+ .id = XT_HMARK_OFFSET,
+ .flags = XTOPT_PUT,
+ XTOPT_POINTER(hi, hoffset)
+ },
+ { .name = "ct",
+ .type = XTTYPE_NONE,
+ .id = XT_HMARK_CT
+ },
+ XTOPT_TABLEEND,
+};
+
+static void HMARK_parse(struct xt_option_call *cb)
+{
+ struct xt_hmark_info *info = cb->data;
+
+ if (!cb->xflags) {
+ memset(info, 0xff, sizeof(struct xt_hmark_info));
+ info->port_set.v32 = 0;
+ info->flags = 0;
+ info->spi_set = 0;
+ info->hoffset = 0;
+ info->hashrnd = DEF_HRAND;
+ }
+ xtables_option_parse(cb);
+
+ switch (cb->entry->id) {
+ case XT_HMARK_SPI_AND:
+ info->spi_mask = htonl(cb->val.u32);
+ break;
+ case XT_HMARK_SPI_OR:
+ info->spi_set = htonl(cb->val.u32);
+ break;
+ case XT_HMARK_SPORT_AND:
+ info->port_mask.p16.src = htons(cb->val.u16);
+ break;
+ case XT_HMARK_DPORT_AND:
+ info->port_mask.p16.dst = htons(cb->val.u16);
+ break;
+ case XT_HMARK_SPORT_OR:
+ info->port_set.p16.src = htons(cb->val.u16);
+ break;
+ case XT_HMARK_DPORT_OR:
+ info->port_set.p16.dst = htons(cb->val.u16);
+ break;
+ case XT_HMARK_MODULUS:
+ if (info->hmodulus == 0) {
+ xtables_error(PARAMETER_PROBLEM,
+ "xxx modulus 0 ? "
+ "thats a div by 0");
+ info->hmodulus = 0xffffffff;
+ }
+ break;
+ case XT_HMARK_METHOD_L3:
+ if (strcmp(cb->arg, "L3") == 0) {
+ info->proto_mask = 0;
+ cb->xflags &= ~XT_F_HMARK_METHOD_L3_4;
+ } else if (strcmp(cb->arg, "L3-4") == 0) {
+ cb->xflags &= ~XT_F_HMARK_METHOD_L3;
+ cb->xflags |= XT_F_HMARK_METHOD_L3_4;
+ }
+ }
+ info->flags = cb->xflags;
+}
+
+static void HMARK_check(struct xt_fcheck_call *cb)
+{
+ if (!(cb->xflags & XT_F_HMARK_MODULUS))
+ xtables_error(PARAMETER_PROBLEM, "HMARK: the --hmark-mod, "
+ "is not set, or zero wich is a div by zero");
+ /* Check for invalid options */
+ if (cb->xflags & XT_F_HMARK_METHOD_L3 &&
+ (cb->xflags & XT_F_HMARK_L4_OPTS))
+ xtables_error(PARAMETER_PROBLEM, "HMARK: --hmark-method L3, "
+ "can not be combined by an Layer 4 options: "
+ "port, spi or proto ");
+}
+/*
+ * Common print for IPv4 & IPv6
+ */
+static void HMARK_print(const struct xt_hmark_info *info)
+{
+ if (info->flags & XT_F_HMARK_METHOD_L3) {
+ printf("method L3 ");
+ } else {
+ if (info->flags & XT_F_HMARK_METHOD_L3_4)
+ printf("method L3-4 ");
+ if (info->flags & XT_F_HMARK_SPORT_AND)
+ printf("sport-mask 0x%x ", htons(info->port_mask.p16.src));
+ if (info->flags & XT_F_HMARK_DPORT_AND)
+ printf("dport-mask 0x%x ", htons(info->port_mask.p16.dst));
+ if (info->flags & XT_F_HMARK_SPI_AND)
+ printf("spi-mask 0x%x ", htonl(info->spi_mask));
+ if (info->flags & XT_F_HMARK_SPORT_OR)
+ printf("sport-set 0x%x ", htons(info->port_set.p16.src));
+ if (info->flags & XT_F_HMARK_DPORT_OR)
+ printf("dport-set 0x%x ", htons(info->port_set.p16.dst));
+ if (info->flags & XT_F_HMARK_SPI_OR)
+ printf("spi-set 0x%x ", htonl(info->spi_set));
+ if (info->flags & XT_F_HMARK_PROTO_AND)
+ printf("proto-mask 0x%x ", info->proto_mask);
+ }
+ if (info->flags & XT_F_HMARK_RND)
+ printf("rnd 0x%x ", info->hashrnd);
+
+}
+
+static void HMARK_ip6_print(const void *ip,
+ const struct xt_entry_target *target, int numeric)
+{
+ const struct xt_hmark_info *info =
+ (const struct xt_hmark_info *)target->data;
+
+ printf(" HMARK ");
+ if (info->flags & XT_F_HMARK_MODULUS)
+ printf("%% 0x%x ", info->hmodulus);
+ if (info->flags & XT_F_HMARK_OFFSET)
+ printf("+ 0x%x ", info->hoffset);
+ if (info->flags & XT_F_HMARK_CT)
+ printf("ct, ");
+ if (info->flags & XT_F_HMARK_SADR_AND)
+ printf("src-mask %s ",
+ xtables_ip6mask_to_numeric(&info->src_mask.in6) + 1);
+ if (info->flags & XT_F_HMARK_DADR_AND)
+ printf("dst-mask %s ",
+ xtables_ip6mask_to_numeric(&info->dst_mask.in6) + 1);
+ HMARK_print(info);
+}
+static void HMARK_ip4_print(const void *ip,
+ const struct xt_entry_target *target, int numeric)
+{
+ const struct xt_hmark_info *info =
+ (const struct xt_hmark_info *)target->data;
+
+ printf(" HMARK ");
+ if (info->flags & XT_F_HMARK_MODULUS)
+ printf("%% 0x%x ", info->hmodulus);
+ if (info->flags & XT_F_HMARK_OFFSET)
+ printf("+ 0x%x ", info->hoffset);
+ if (info->flags & XT_F_HMARK_CT)
+ printf("ct, ");
+ if (info->flags & XT_F_HMARK_SADR_AND)
+ printf("src-mask %s ",
+ xtables_ipmask_to_numeric(&info->src_mask.in) + 1);
+ if (info->flags & XT_F_HMARK_DADR_AND)
+ printf("dst-mask %s ",
+ xtables_ipmask_to_numeric(&info->dst_mask.in) + 1);
+ HMARK_print(info);
+}
+static void HMARK_save(const struct xt_hmark_info *info)
+{
+ if (info->flags & XT_F_HMARK_METHOD_L3) {
+ printf(" --hmark-method L3");
+ } else {
+ if (info->flags & XT_F_HMARK_METHOD_L3_4)
+ printf(" --hmark-method L3-4");
+ if (info->flags & XT_F_HMARK_SPORT_AND)
+ printf(" --hmark-sport-mask 0x%x",
+ htons(info->port_mask.p16.src));
+ if (info->flags & XT_F_HMARK_DPORT_AND)
+ printf(" --hmark-dport-mask 0x%x",
+ htons(info->port_mask.p16.dst));
+ if (info->flags & XT_F_HMARK_SPI_AND)
+ printf(" --hmark-spi-mask 0x%x",
+ htonl(info->spi_mask));
+ if (info->flags & XT_F_HMARK_SPORT_OR)
+ printf(" --hmark-sport-set 0x%x",
+ htons(info->port_set.p16.src));
+ if (info->flags & XT_F_HMARK_DPORT_OR)
+ printf(" --hmark-dport-set 0x%x",
+ htons(info->port_set.p16.dst));
+ if (info->flags & XT_F_HMARK_SPI_OR)
+ printf(" --hmark-spi-set 0x%x", htonl(info->spi_set));
+ if (info->flags & XT_F_HMARK_PROTO_AND)
+ printf(" --hmark-proto-mask 0x%x", info->proto_mask);
+ }
+ if (info->flags & XT_F_HMARK_RND)
+ printf(" --hmark-rnd 0x%x", info->hashrnd);
+ if (info->flags & XT_F_HMARK_MODULUS)
+ printf(" --hmark-mod 0x%x", info->hmodulus);
+ if (info->flags & XT_F_HMARK_OFFSET)
+ printf(" --hmark-offset 0x%x", info->hoffset);
+ if (info->flags & XT_F_HMARK_CT)
+ printf(" --hmark-ct");
+}
+
+static void HMARK_ip6_save(const void *ip, const struct xt_entry_target *target)
+{
+ const struct xt_hmark_info *info =
+ (const struct xt_hmark_info *)target->data;
+
+ if (info->flags & XT_F_HMARK_SADR_AND)
+ printf(" --hmark-src-mask %s",
+ xtables_ip6mask_to_numeric(&info->src_mask.in6) + 1);
+ if (info->flags & XT_F_HMARK_DADR_AND)
+ printf(" --hmark-dst-mask %s",
+ xtables_ip6mask_to_numeric(&info->dst_mask.in6) + 1);
+ HMARK_save(info);
+}
+
+static void HMARK_ip4_save(const void *ip, const struct xt_entry_target *target)
+{
+ const struct xt_hmark_info *info =
+ (const struct xt_hmark_info *)target->data;
+
+ if (info->flags & XT_F_HMARK_SADR_AND)
+ printf(" --hmark-src-mask %s",
+ xtables_ipmask_to_numeric(&info->src_mask.in) + 1);
+ if (info->flags & XT_F_HMARK_DADR_AND)
+ printf(" --hmark-dst-mask %s",
+ xtables_ipmask_to_numeric(&info->dst_mask.in) + 1);
+ HMARK_save(info);
+}
+
+static struct xtables_target mark_tg_reg[] = {
+ {
+ .family = NFPROTO_IPV4,
+ .name = "HMARK",
+ .version = XTABLES_VERSION,
+ .revision = 0,
+ .size = XT_ALIGN(sizeof(struct xt_hmark_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_hmark_info)),
+ .help = HMARK_help,
+ .print = HMARK_ip4_print,
+ .save = HMARK_ip4_save,
+ .x6_parse = HMARK_parse,
+ .x6_fcheck = HMARK_check,
+ .x6_options = HMARK_opts,
+ },
+ {
+ .family = NFPROTO_IPV6,
+ .name = "HMARK",
+ .version = XTABLES_VERSION,
+ .revision = 0,
+ .size = XT_ALIGN(sizeof(struct xt_hmark_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_hmark_info)),
+ .help = HMARK_help,
+ .print = HMARK_ip6_print,
+ .save = HMARK_ip6_save,
+ .x6_parse = HMARK_parse,
+ .x6_fcheck = HMARK_check,
+ .x6_options = HMARK_opts,
+ },
+};
+
+void _init(void)
+{
+ xtables_register_targets(mark_tg_reg, ARRAY_SIZE(mark_tg_reg));
+}
+