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(struct net
*net
, const struct sk_buff
*skb
,
30 const struct net_device
*dev
, u8 flags
)
33 struct ipv6hdr
*iph
= ipv6_hdr(skb
);
36 .flowi6_iif
= LOOPBACK_IFINDEX
,
37 .flowlabel
= (* (__be32
*) iph
) & IPV6_FLOWINFO_MASK
,
38 .flowi6_proto
= iph
->nexthdr
,
43 if (rpfilter_addr_unicast(&iph
->daddr
)) {
44 memcpy(&fl6
.saddr
, &iph
->daddr
, sizeof(struct in6_addr
));
45 lookup_flags
= RT6_LOOKUP_F_HAS_SADDR
;
50 fl6
.flowi6_mark
= flags
& XT_RPFILTER_VALID_MARK
? skb
->mark
: 0;
51 if ((flags
& XT_RPFILTER_LOOSE
) == 0) {
52 fl6
.flowi6_oif
= dev
->ifindex
;
53 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
56 rt
= (void *) ip6_route_lookup(net
, &fl6
, lookup_flags
);
60 if (rt
->rt6i_flags
& (RTF_REJECT
|RTF_ANYCAST
))
63 if (rt
->rt6i_flags
& RTF_LOCAL
) {
64 ret
= flags
& XT_RPFILTER_ACCEPT_LOCAL
;
68 if (rt
->rt6i_idev
->dev
== dev
|| (flags
& XT_RPFILTER_LOOSE
))
76 rpfilter_is_loopback(const struct sk_buff
*skb
, const struct net_device
*in
)
78 return skb
->pkt_type
== PACKET_LOOPBACK
|| in
->flags
& IFF_LOOPBACK
;
81 static bool rpfilter_mt(const struct sk_buff
*skb
, struct xt_action_param
*par
)
83 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
86 bool invert
= info
->flags
& XT_RPFILTER_INVERT
;
88 if (rpfilter_is_loopback(skb
, xt_in(par
)))
92 saddrtype
= ipv6_addr_type(&iph
->saddr
);
93 if (unlikely(saddrtype
== IPV6_ADDR_ANY
))
94 return true ^ invert
; /* not routable: forward path will drop it */
96 return rpfilter_lookup_reverse6(xt_net(par
), skb
, xt_in(par
),
97 info
->flags
) ^ invert
;
100 static int rpfilter_check(const struct xt_mtchk_param
*par
)
102 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
103 unsigned int options
= ~XT_RPFILTER_OPTION_MASK
;
105 if (info
->flags
& options
) {
106 pr_info("unknown options encountered");
110 if (strcmp(par
->table
, "mangle") != 0 &&
111 strcmp(par
->table
, "raw") != 0) {
112 pr_info("match only valid in the \'raw\' "
113 "or \'mangle\' tables, not \'%s\'.\n", par
->table
);
120 static struct xt_match rpfilter_mt_reg __read_mostly
= {
122 .family
= NFPROTO_IPV6
,
123 .checkentry
= rpfilter_check
,
124 .match
= rpfilter_mt
,
125 .matchsize
= sizeof(struct xt_rpfilter_info
),
126 .hooks
= (1 << NF_INET_PRE_ROUTING
),
130 static int __init
rpfilter_mt_init(void)
132 return xt_register_match(&rpfilter_mt_reg
);
135 static void __exit
rpfilter_mt_exit(void)
137 xt_unregister_match(&rpfilter_mt_reg
);
140 module_init(rpfilter_mt_init
);
141 module_exit(rpfilter_mt_exit
);