1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2011 Florian Westphal <fw@strlen.de>
5 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6 #include <linux/module.h>
7 #include <linux/skbuff.h>
8 #include <linux/netdevice.h>
9 #include <linux/route.h>
10 #include <net/ip6_fib.h>
11 #include <net/ip6_route.h>
13 #include <linux/netfilter/xt_rpfilter.h>
14 #include <linux/netfilter/x_tables.h>
16 MODULE_LICENSE("GPL");
17 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
18 MODULE_DESCRIPTION("Xtables: IPv6 reverse path filter match");
20 static bool rpfilter_addr_unicast(const struct in6_addr
*addr
)
22 int addr_type
= ipv6_addr_type(addr
);
23 return addr_type
& IPV6_ADDR_UNICAST
;
26 static bool rpfilter_addr_linklocal(const struct in6_addr
*addr
)
28 int addr_type
= ipv6_addr_type(addr
);
29 return addr_type
& IPV6_ADDR_LINKLOCAL
;
32 static bool rpfilter_lookup_reverse6(struct net
*net
, const struct sk_buff
*skb
,
33 const struct net_device
*dev
, u8 flags
)
36 struct ipv6hdr
*iph
= ipv6_hdr(skb
);
39 .flowi6_iif
= LOOPBACK_IFINDEX
,
40 .flowlabel
= (* (__be32
*) iph
) & IPV6_FLOWINFO_MASK
,
41 .flowi6_proto
= iph
->nexthdr
,
46 if (rpfilter_addr_unicast(&iph
->daddr
)) {
47 memcpy(&fl6
.saddr
, &iph
->daddr
, sizeof(struct in6_addr
));
48 lookup_flags
= RT6_LOOKUP_F_HAS_SADDR
;
53 fl6
.flowi6_mark
= flags
& XT_RPFILTER_VALID_MARK
? skb
->mark
: 0;
55 if (rpfilter_addr_linklocal(&iph
->saddr
)) {
56 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
57 fl6
.flowi6_oif
= dev
->ifindex
;
58 } else if ((flags
& XT_RPFILTER_LOOSE
) == 0)
59 fl6
.flowi6_oif
= dev
->ifindex
;
61 rt
= (void *)ip6_route_lookup(net
, &fl6
, skb
, lookup_flags
);
65 if (rt
->rt6i_flags
& (RTF_REJECT
|RTF_ANYCAST
))
68 if (rt
->rt6i_flags
& RTF_LOCAL
) {
69 ret
= flags
& XT_RPFILTER_ACCEPT_LOCAL
;
73 if (rt
->rt6i_idev
->dev
== dev
|| (flags
& XT_RPFILTER_LOOSE
))
81 rpfilter_is_loopback(const struct sk_buff
*skb
, const struct net_device
*in
)
83 return skb
->pkt_type
== PACKET_LOOPBACK
|| in
->flags
& IFF_LOOPBACK
;
86 static bool rpfilter_mt(const struct sk_buff
*skb
, struct xt_action_param
*par
)
88 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
91 bool invert
= info
->flags
& XT_RPFILTER_INVERT
;
93 if (rpfilter_is_loopback(skb
, xt_in(par
)))
97 saddrtype
= ipv6_addr_type(&iph
->saddr
);
98 if (unlikely(saddrtype
== IPV6_ADDR_ANY
))
99 return true ^ invert
; /* not routable: forward path will drop it */
101 return rpfilter_lookup_reverse6(xt_net(par
), skb
, xt_in(par
),
102 info
->flags
) ^ invert
;
105 static int rpfilter_check(const struct xt_mtchk_param
*par
)
107 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
108 unsigned int options
= ~XT_RPFILTER_OPTION_MASK
;
110 if (info
->flags
& options
) {
111 pr_info_ratelimited("unknown options\n");
115 if (strcmp(par
->table
, "mangle") != 0 &&
116 strcmp(par
->table
, "raw") != 0) {
117 pr_info_ratelimited("only valid in \'raw\' or \'mangle\' table, not \'%s\'\n",
125 static struct xt_match rpfilter_mt_reg __read_mostly
= {
127 .family
= NFPROTO_IPV6
,
128 .checkentry
= rpfilter_check
,
129 .match
= rpfilter_mt
,
130 .matchsize
= sizeof(struct xt_rpfilter_info
),
131 .hooks
= (1 << NF_INET_PRE_ROUTING
),
135 static int __init
rpfilter_mt_init(void)
137 return xt_register_match(&rpfilter_mt_reg
);
140 static void __exit
rpfilter_mt_exit(void)
142 xt_unregister_match(&rpfilter_mt_reg
);
145 module_init(rpfilter_mt_init
);
146 module_exit(rpfilter_mt_exit
);