2 * Copyright (c) 2011 Florian Westphal <fw@strlen.de>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 #include <linux/module.h>
10 #include <linux/skbuff.h>
11 #include <linux/netdevice.h>
12 #include <linux/route.h>
13 #include <net/ip6_fib.h>
14 #include <net/ip6_route.h>
16 #include <linux/netfilter/xt_rpfilter.h>
17 #include <linux/netfilter/x_tables.h>
19 MODULE_LICENSE("GPL");
20 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
21 MODULE_DESCRIPTION("Xtables: IPv6 reverse path filter match");
23 static bool rpfilter_addr_unicast(const struct in6_addr
*addr
)
25 int addr_type
= ipv6_addr_type(addr
);
26 return addr_type
& IPV6_ADDR_UNICAST
;
29 static bool rpfilter_lookup_reverse6(const struct sk_buff
*skb
,
30 const struct net_device
*dev
, u8 flags
)
33 struct ipv6hdr
*iph
= ipv6_hdr(skb
);
36 .flowlabel
= (* (__be32
*) iph
) & IPV6_FLOWINFO_MASK
,
37 .flowi6_proto
= iph
->nexthdr
,
42 if (rpfilter_addr_unicast(&iph
->daddr
)) {
43 memcpy(&fl6
.saddr
, &iph
->daddr
, sizeof(struct in6_addr
));
44 lookup_flags
= RT6_LOOKUP_F_HAS_SADDR
;
49 fl6
.flowi6_mark
= flags
& XT_RPFILTER_VALID_MARK
? skb
->mark
: 0;
50 if ((flags
& XT_RPFILTER_LOOSE
) == 0) {
51 fl6
.flowi6_oif
= dev
->ifindex
;
52 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
55 rt
= (void *) ip6_route_lookup(dev_net(dev
), &fl6
, lookup_flags
);
59 if (rt
->rt6i_flags
& (RTF_REJECT
|RTF_ANYCAST
))
62 if (rt
->rt6i_flags
& RTF_LOCAL
) {
63 ret
= flags
& XT_RPFILTER_ACCEPT_LOCAL
;
67 if (rt
->rt6i_idev
->dev
== dev
|| (flags
& XT_RPFILTER_LOOSE
))
70 dst_release(&rt
->dst
);
74 static bool rpfilter_mt(const struct sk_buff
*skb
, struct xt_action_param
*par
)
76 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
79 bool invert
= info
->flags
& XT_RPFILTER_INVERT
;
81 if (par
->in
->flags
& IFF_LOOPBACK
)
85 saddrtype
= ipv6_addr_type(&iph
->saddr
);
86 if (unlikely(saddrtype
== IPV6_ADDR_ANY
))
87 return true ^ invert
; /* not routable: forward path will drop it */
89 return rpfilter_lookup_reverse6(skb
, par
->in
, info
->flags
) ^ invert
;
92 static int rpfilter_check(const struct xt_mtchk_param
*par
)
94 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
95 unsigned int options
= ~XT_RPFILTER_OPTION_MASK
;
97 if (info
->flags
& options
) {
98 pr_info("unknown options encountered");
102 if (strcmp(par
->table
, "mangle") != 0 &&
103 strcmp(par
->table
, "raw") != 0) {
104 pr_info("match only valid in the \'raw\' "
105 "or \'mangle\' tables, not \'%s\'.\n", par
->table
);
112 static struct xt_match rpfilter_mt_reg __read_mostly
= {
114 .family
= NFPROTO_IPV6
,
115 .checkentry
= rpfilter_check
,
116 .match
= rpfilter_mt
,
117 .matchsize
= sizeof(struct xt_rpfilter_info
),
118 .hooks
= (1 << NF_INET_PRE_ROUTING
),
122 static int __init
rpfilter_mt_init(void)
124 return xt_register_match(&rpfilter_mt_reg
);
127 static void __exit
rpfilter_mt_exit(void)
129 xt_unregister_match(&rpfilter_mt_reg
);
132 module_init(rpfilter_mt_init
);
133 module_exit(rpfilter_mt_exit
);