#define IPT_AND_MASK_USED 2
#define IPT_OR_MASK_USED 4
+enum {
+ FL_SHIFT = 1 << 3,
+};
+
/* Function which prints out usage message. */
static void ipmark_tg_help(void)
{
" --addr src/dst use source or destination ip address\n"
" --and-mask value logical AND ip address with this value becomes MARK\n"
" --or-mask value logical OR ip address with this value becomes MARK\n"
+" --shift value shift address right by value before copying to mark\n"
"\n");
}
{ "addr", 1, 0, '1' },
{ "and-mask", 1, 0, '2' },
{ "or-mask", 1, 0, '3' },
+ {.name = "shift", .has_arg = true, .val = '4'},
{NULL},
};
const void *entry, struct xt_entry_target **target)
{
struct xt_ipmark_tginfo *info = (void *)(*target)->data;
+ unsigned int n;
switch (c) {
char *end;
*flags |= IPT_OR_MASK_USED;
break;
+ case '4':
+ param_act(P_ONLY_ONCE, "IPMARK", "--shift", *flags & FL_SHIFT);
+ param_act(P_NO_INVERT, "IPMARK", "--shift", invert);
+ /*
+ * Anything >31 does not make sense for IPv4, but it still
+ * does the right thing.
+ */
+ if (!strtonum(optarg, NULL, &n, 0, 128))
+ param_act(P_BAD_VALUE, "IPMARK", "--shift", optarg);
+ info->shift = n;
+ return true;
+
default:
return 0;
}
.extra_opts = ipmark_tg_opts,
};
+static struct xtables_target ipmark_tg6_reg = {
+ .version = XTABLES_VERSION,
+ .name = "IPMARK",
+ .family = PF_INET6,
+ .revision = 0,
+ .size = XT_ALIGN(sizeof(struct xt_ipmark_tginfo)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_ipmark_tginfo)),
+ .help = ipmark_tg_help,
+ .init = ipmark_tg_init,
+ .parse = ipmark_tg_parse,
+ .final_check = ipmark_tg_check,
+ .print = ipmark_tg_print,
+ .save = ipmark_tg_save,
+ .extra_opts = ipmark_tg_opts,
+};
+
static void _init(void)
{
xtables_register_target(&ipmark_tg4_reg);
+ xtables_register_target(&ipmark_tg6_reg);
}
.TP
.BI "--or-mask " "mask"
Perform bitwise `or' on the IP address and this mask.
+.TP
+\fB--shift\fP \fIvalue\fP
+Shift addresses to the right by the given number of bits before taking it
+as a mark. (This is done before ANDing or ORing it.) This option is needed
+to select part of an IPv6 address, because marks are only 32 bits in size.
.P
The order of IP address bytes is reversed to meet "human order of bytes":
192.168.0.1 is 0xc0a80001. At first the `and' operation is performed, then
.P
On the routers with hundreds of users there should be significant load
decrease (e.g. twice).
+.PP
+(IPv6 example) If the source address is of the form
+2001:db8:45:1d:20d:93ff:fe9b:e443 and the resulting mark should be 0x93ff,
+then a right-shift of 16 is needed first:
+.IP
+-t mangle -A PREROUTING -s 2001:db8::/32 -j IPMARK --addr src --shift 16
+--and-mask 0xFFFF
#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/version.h>
MODULE_DESCRIPTION("IP tables IPMARK: mark based on ip address");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_IPMARK");
+MODULE_ALIAS("ip6t_IPMARK");
static unsigned int
-ipmark_tg(struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- unsigned int hooknum,
- const struct xt_target *target,
- const void *targinfo)
+ipmark_tg4(struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, unsigned int hooknum,
+ const struct xt_target *target, const void *targinfo)
{
const struct xt_ipmark_tginfo *ipmarkinfo = targinfo;
const struct iphdr *iph = ip_hdr(skb);
else
mark = ntohl(iph->daddr);
+ mark >>= ipmarkinfo->shift;
mark &= ipmarkinfo->andmask;
mark |= ipmarkinfo->ormask;
return XT_CONTINUE;
}
-static struct xt_target ipt_ipmark_reg = {
- .name = "IPMARK",
- .family = AF_INET,
- .table = "mangle",
- .target = ipmark_tg,
- .targetsize = sizeof(struct xt_ipmark_tginfo),
- .me = THIS_MODULE
+/* Function is safe for any value of @s */
+static __u32 ipmark_from_ip6(const struct in6_addr *a, unsigned int s)
+{
+ unsigned int q = s % 32;
+ __u32 mask;
+
+ if (s >= 128)
+ return 0;
+
+ mask = ntohl(a->s6_addr32[3 - s/32]) >> q;
+ if (s > 0 && s < 96 && q != 0)
+ mask |= ntohl(a->s6_addr32[2 - s/32]) << (32 - q);
+ return mask;
+}
+
+static unsigned int
+ipmark_tg6(struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, unsigned int hooknum,
+ const struct xt_target *target, const void *targinfo)
+{
+ const struct xt_ipmark_tginfo *info = targinfo;
+ const struct ipv6hdr *iph = ipv6_hdr(skb);
+ __u32 mark;
+
+ if (info->selector == XT_IPMARK_SRC)
+ mark = ipmark_from_ip6(&iph->saddr, info->shift);
+ else
+ mark = ipmark_from_ip6(&iph->daddr, info->shift);
+
+ mark &= info->andmask;
+ mark |= info->ormask;
+ skb_nfmark(skb) = mark;
+ return XT_CONTINUE;
+}
+
+static struct xt_target ipmark_tg_reg[] __read_mostly = {
+ {
+ .name = "IPMARK",
+ .revision = 0,
+ .family = PF_INET,
+ .table = "mangle",
+ .target = ipmark_tg4,
+ .targetsize = XT_ALIGN(sizeof(struct xt_ipmark_tginfo)),
+ .me = THIS_MODULE,
+ },
+ {
+ .name = "IPMARK",
+ .revision = 0,
+ .family = PF_INET6,
+ .table = "mangle",
+ .target = ipmark_tg6,
+ .targetsize = XT_ALIGN(sizeof(struct xt_ipmark_tginfo)),
+ .me = THIS_MODULE,
+ },
};
static int __init ipmark_tg_init(void)
{
- return xt_register_target(&ipt_ipmark_reg);
+ return xt_register_targets(ipmark_tg_reg, ARRAY_SIZE(ipmark_tg_reg));
}
static void __exit ipmark_tg_exit(void)
{
- xt_unregister_target(&ipt_ipmark_reg);
+ xt_unregister_targets(ipmark_tg_reg, ARRAY_SIZE(ipmark_tg_reg));
}
module_init(ipmark_tg_init);
__u32 andmask;
__u32 ormask;
__u8 selector;
+ __u8 shift;
};
#endif /* _LINUX_NETFILTER_XT_IPMARK_H */