IPMARK: IPv6 support

This commit is contained in:
Jan Engelhardt
2008-04-08 20:00:40 +02:00
parent b63ac3be45
commit 2c7b1d5330
4 changed files with 111 additions and 15 deletions

View File

@@ -16,6 +16,10 @@
#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);
}

View 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

View 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);

View File

@@ -10,6 +10,7 @@ struct xt_ipmark_tginfo {
__u32 andmask;
__u32 ormask;
__u8 selector;
__u8 shift;
};
#endif /* _LINUX_NETFILTER_XT_IPMARK_H */