@@ -0,0 +1,335 @@
+/*
+ * 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 */
+
+static void HMARK_help(void)
+{
+ printf(
+"HMARK target options, i.e. modify hash calculation by:\n"
+" --hmark-smask length Source address mask length\n"
+" --hmark-dmask length Dest address mask length\n"
+" --hmark-sp-mask value Mask src port with value\n"
+" --hmark-dp-mask value Mask dst port with value\n"
+" --hmark-spi-mask value For esp and ah AND spi with value\n"
+" --hmark-sp-set value OR src port with value\n"
+" --hmark-dp-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 Random value to hash cacl.\n"
+" Limit/modify the calculated hash mark by:\n"
+" --hmark-mod value nfmark modulus value\n"
+" --hmark-offs value Last action add value to nfmark\n"
+" For NAT in IPv4 the original address can be used in the return path.\n"
+" Make sure to qualify the statement in a proper way when using nat flags\n"
+" --hmark-dnat Replace src addr with original dst addr\n"
+" --hmark-snat Replace dst addr with original src addr\n"
+" In many cases hmark can be omitted i.e. --smask can be used\n");
+}
+
+#define hi struct xt_hmark_info
+
+static const struct xt_option_entry HMARK_opts[] = {
+ { .name = "hmark-smask", .type = XTTYPE_PLENMASK, .id = XT_HMARK_SADR_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, smask)
+ },
+ { .name = "hmark-dmask", .type = XTTYPE_PLENMASK, .id = XT_HMARK_DADR_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, dmask)
+ },
+ { .name = "hmark-sp-mask", .type = XTTYPE_UINT16, .id = XT_HMARK_SPORT_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pmask.p16.src)
+ },
+ { .name = "hmark-dp-mask", .type = XTTYPE_UINT16, .id = XT_HMARK_DPORT_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pmask.p16.dst)
+ },
+ { .name = "hmark-spi-mask", .type = XTTYPE_UINT32, .id = XT_HMARK_SPI_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, spimask)
+ },
+ { .name = "hmark-sp-set", .type = XTTYPE_UINT16, .id = XT_HMARK_SPORT_OR,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pset.p16.src)
+ },
+ { .name = "hmark-dp-set", .type = XTTYPE_UINT16, .id = XT_HMARK_DPORT_OR,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pset.p16.dst)
+ },
+ { .name = "hmark-spi-set", .type = XTTYPE_UINT32, .id = XT_HMARK_SPI_OR,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, spiset)
+ },
+ { .name = "hmark-proto-mask", .type = XTTYPE_UINT16, .id = XT_HMARK_PROTO_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, prmask)
+ },
+ { .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,
+ .flags = XTOPT_PUT | XTOPT_MAND, XTOPT_POINTER(hi, hmod)
+ },
+ { .name = "hmark-offs", .type = XTTYPE_UINT32, .id = XT_HMARK_OFFSET,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, hoffs)
+ },
+ { .name = "hmark-dnat", .type = XTTYPE_NONE, .id = XT_HMARK_USE_DNAT },
+ { .name = "hmark-snat", .type = XTTYPE_NONE, .id = XT_HMARK_USE_SNAT },
+
+ { .name = "smask", .type = XTTYPE_PLENMASK, .id = XT_HMARK_SADR_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, smask)
+ },
+ { .name = "dmask", .type = XTTYPE_PLENMASK, .id = XT_HMARK_DADR_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, dmask)
+ },
+ { .name = "sp-mask", .type = XTTYPE_UINT16, .id = XT_HMARK_SPORT_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pmask.p16.src)
+ },
+ { .name = "dp-mask", .type = XTTYPE_UINT16, .id = XT_HMARK_DPORT_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pmask.p16.dst)
+ },
+ { .name = "spi-mask", .type = XTTYPE_UINT32, .id = XT_HMARK_SPI_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, spimask)
+ },
+ { .name = "sp-set", .type = XTTYPE_UINT16, .id = XT_HMARK_SPORT_OR,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pset.p16.src)
+ },
+ { .name = "dp-set", .type = XTTYPE_UINT16, .id = XT_HMARK_DPORT_OR,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, pset.p16.dst)
+ },
+ { .name = "spi-set", .type = XTTYPE_UINT32, .id = XT_HMARK_SPI_OR,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, spiset)
+ },
+ { .name = "proto-mask", .type = XTTYPE_UINT16, .id = XT_HMARK_PROTO_AND,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, prmask)
+ },
+ { .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,
+ .flags = XTOPT_PUT | XTOPT_MAND, XTOPT_POINTER(hi, hmod)
+ },
+ { .name = "offs", .type = XTTYPE_UINT32, .id = XT_HMARK_OFFSET,
+ .flags = XTOPT_PUT, XTOPT_POINTER(hi, hoffs)
+ },
+ { .name = "dnat", .type = XTTYPE_NONE, .id = XT_HMARK_USE_DNAT },
+ { .name = "snat", .type = XTTYPE_NONE, .id = XT_HMARK_USE_SNAT },
+ 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->pset.v32 = 0;
+ info->flags = 0;
+ info->spiset = 0;
+ info->hoffs = 0;
+ info->hashrnd = DEF_HRAND;
+ }
+ xtables_option_parse(cb);
+
+ switch (cb->entry->id) {
+ case XT_HMARK_SPI_AND:
+ info->spimask = htonl(cb->val.u32);
+ break;
+ case XT_HMARK_SPI_OR:
+ info->spiset = htonl(cb->val.u32);
+ break;
+ case XT_HMARK_SPORT_AND:
+ info->pmask.p16.src = htons(cb->val.u16);
+ break;
+ case XT_HMARK_DPORT_AND:
+ info->pmask.p16.dst = htons(cb->val.u16);
+ break;
+ case XT_HMARK_SPORT_OR:
+ info->pset.p16.src = htons(cb->val.u16);
+ break;
+ case XT_HMARK_DPORT_OR:
+ info->pset.p16.dst = htons(cb->val.u16);
+ break;
+ case XT_HMARK_MODULUS:
+ if (info->hmod == 0) {
+ xtables_error(PARAMETER_PROBLEM,
+ "xxx modulus 0 ? "
+ "thats a div by 0");
+ info->hmod = 0xffffffff;
+ }
+ break;
+ }
+ 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, that means the nfmark will be in range"
+ " 0 - 0xffffffff");
+}
+/*
+ * Common print for IPv4 & IPv6
+ */
+static void HMARK_print(const struct xt_hmark_info *info)
+{
+ if (info->flags & (1 << XT_HMARK_SPORT_AND))
+ printf("sp-mask 0x%x ", htons(info->pmask.p16.src));
+ if (info->flags & (1 << XT_HMARK_DPORT_AND))
+ printf("dp-mask 0x%x ", htons(info->pmask.p16.dst));
+ if (info->flags & (1 << XT_HMARK_SPI_AND))
+ printf("spi-mask 0x%x ", htonl(info->spimask));
+ if (info->flags & (1 << XT_HMARK_SPORT_OR))
+ printf("sp-set 0x%x ", htons(info->pset.p16.src));
+ if (info->flags & (1 << XT_HMARK_DPORT_OR))
+ printf("dp-set 0x%x ", htons(info->pset.p16.dst));
+ if (info->flags & (1 << XT_HMARK_SPI_OR))
+ printf("spi-set 0x%x ", htonl(info->spiset));
+ if (info->flags & (1 << XT_HMARK_PROTO_AND))
+ printf("proto-mask 0x%x ", info->prmask);
+ if (info->flags & (1 << XT_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 & (1 << XT_HMARK_MODULUS))
+ printf("%% 0x%x ", info->hmod);
+ if (info->flags & (1 << XT_HMARK_OFFSET))
+ printf("+ 0x%x ", info->hoffs);
+ if (info->flags & (1 << XT_HMARK_USE_SNAT))
+ printf("snat, ");
+ if (info->flags & (1 << XT_HMARK_SADR_AND))
+ printf("smask %s ", xtables_ip6mask_to_numeric(&info->smask.in6) + 1);
+ if (info->flags & (1 << XT_HMARK_USE_DNAT))
+ printf("dnat, ");
+ if (info->flags & (1 << XT_HMARK_DADR_AND))
+ printf("dmask %s ", xtables_ip6mask_to_numeric(&info->dmask.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 & (1 << XT_HMARK_MODULUS))
+ printf("%% 0x%x ", info->hmod);
+ if (info->flags & (1 << XT_HMARK_OFFSET))
+ printf("+ 0x%x ", info->hoffs);
+ if (info->flags & (1 << XT_HMARK_USE_SNAT))
+ printf("snat, ");
+ if (info->flags & (1 << XT_HMARK_SADR_AND))
+ printf("smask %s ", xtables_ipmask_to_numeric(&info->smask.in) + 1);
+ if (info->flags & (1 << XT_HMARK_USE_DNAT))
+ printf("dnat, ");
+ if (info->flags & (1 << XT_HMARK_DADR_AND))
+ printf("dmask %s ", xtables_ipmask_to_numeric(&info->dmask.in) + 1);
+ HMARK_print(info);
+}
+static void HMARK_save(const struct xt_hmark_info *info)
+{
+ if (info->flags & (1 << XT_HMARK_SPORT_AND))
+ printf(" --hmark-sp-mask 0x%x", htons(info->pmask.p16.src));
+ if (info->flags & (1 << XT_HMARK_DPORT_AND))
+ printf(" --hmark-dp-mask 0x%x", htons(info->pmask.p16.dst));
+ if (info->flags & (1 << XT_HMARK_SPI_AND))
+ printf(" --hmark-spi-mask 0x%x", htonl(info->spimask));
+ if (info->flags & (1 << XT_HMARK_SPORT_OR))
+ printf(" --hmark-sp-set 0x%x", htons(info->pset.p16.src));
+ if (info->flags & (1 << XT_HMARK_DPORT_OR))
+ printf(" --hmark-dp-set 0x%x", htons(info->pset.p16.dst));
+ if (info->flags & (1 << XT_HMARK_SPI_OR))
+ printf(" --hmark-spi-set 0x%x", htonl(info->spiset));
+ if (info->flags & (1 << XT_HMARK_PROTO_AND))
+ printf(" --hmark-proto-mask 0x%x", info->prmask);
+ if (info->flags & (1 << XT_HMARK_RND))
+ printf(" --hmark-rnd 0x%x", info->hashrnd);
+ if (info->flags & (1 << XT_HMARK_MODULUS))
+ printf(" --hmark-mod 0x%x", info->hmod);
+ if (info->flags & (1 << XT_HMARK_OFFSET))
+ printf(" --hmark-offs 0x%x", info->hoffs);
+ if (info->flags & (1 << XT_HMARK_USE_DNAT))
+ printf(" --hmark-dnat");
+ if (info->flags & (1 << XT_HMARK_USE_SNAT))
+ printf(" --hmark-snat");
+}
+
+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 & (1 << XT_HMARK_SADR_AND))
+ printf(" --hmark-smask %s", xtables_ip6mask_to_numeric(&info->smask.in6) + 1);
+ if (info->flags & (1 << XT_HMARK_DADR_AND))
+ printf(" --hmark-dmask %s", xtables_ip6mask_to_numeric(&info->dmask.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 & (1 << XT_HMARK_SADR_AND))
+ printf(" --hmark-smask %s", xtables_ipmask_to_numeric(&info->smask.in) + 1);
+ if (info->flags & (1 << XT_HMARK_DADR_AND))
+ printf(" --hmark-dmask %s", xtables_ipmask_to_numeric(&info->dmask.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));
+}
+