]> git.ipfire.org Git - thirdparty/xtables-addons.git/commitdiff
IPMARK: IPv6 support
authorJan Engelhardt <jengelh@computergmbh.de>
Tue, 8 Apr 2008 18:00:40 +0000 (20:00 +0200)
committerJan Engelhardt <jengelh@computergmbh.de>
Wed, 9 Apr 2008 17:24:01 +0000 (19:24 +0200)
extensions/libxt_IPMARK.c
extensions/libxt_IPMARK.man
extensions/xt_IPMARK.c
extensions/xt_IPMARK.h

index 47e09d032de09226735b64515d991186d1790f9a..91210dd185643d2f681c538ec16d7cfbf4ccef0b 100644 (file)
 #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)
 {
@@ -24,6 +28,7 @@ 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");
 }
 
@@ -31,6 +36,7 @@ static const struct option ipmark_tg_opts[] = {
        { "addr", 1, 0, '1' },
        { "and-mask", 1, 0, '2' },
        { "or-mask", 1, 0, '3' },
+       {.name = "shift",    .has_arg = true, .val = '4'},
        {NULL},
 };
 
@@ -46,6 +52,7 @@ static int ipmark_tg_parse(int c, char **argv, int invert, unsigned int *flags,
                            const void *entry, struct xt_entry_target **target)
 {
        struct xt_ipmark_tginfo *info = (void *)(*target)->data;
+       unsigned int n;
 
        switch (c) {
                char *end;
@@ -78,6 +85,18 @@ static int ipmark_tg_parse(int c, char **argv, int invert, unsigned int *flags,
                *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;
        }
@@ -141,7 +160,24 @@ static struct xtables_target ipmark_tg4_reg = {
        .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);
 }
index e4659b0143ec1f188bee4533a1a85585bec92b4a..8ab0334f637c7e3f8b02c34adca875eece68cb05 100644 (file)
@@ -13,6 +13,11 @@ Perform bitwise `and' on the IP address and this mask.
 .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
@@ -43,3 +48,10 @@ iptables -t mangle -A POSTROUTING -o eth3 -j IPMARK --addr=dst
 .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
index e25bfe6400b0e4b5f7b04f5d12414e5d9050b2ea..b64131ad23eae1e86ad31735c9799e55ad76f9a6 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/version.h>
@@ -11,14 +12,12 @@ MODULE_AUTHOR("Grzegorz Janoszka <Grzegorz@Janoszka.pl>");
 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);
@@ -29,6 +28,7 @@ ipmark_tg(struct sk_buff *skb,
        else
                mark = ntohl(iph->daddr);
 
+       mark >>= ipmarkinfo->shift;
        mark &= ipmarkinfo->andmask;
        mark |= ipmarkinfo->ormask;
 
@@ -36,23 +36,70 @@ ipmark_tg(struct sk_buff *skb,
        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);
index 121e06a2020c6ce6b8d4b5c2b53e88465b89b1b2..67da4e5ab7d93ec0ce3bbac487730e57a8fa6786 100644 (file)
@@ -10,6 +10,7 @@ struct xt_ipmark_tginfo {
        __u32 andmask;
        __u32 ormask;
        __u8 selector;
+       __u8 shift;
 };
 
 #endif /* _LINUX_NETFILTER_XT_IPMARK_H */