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 .flowi6_l3mdev
= l3mdev_master_ifindex_rcu(dev
),
41 .flowlabel
= (* (__be32
*) iph
) & IPV6_FLOWINFO_MASK
,
42 .flowi6_proto
= iph
->nexthdr
,
43 .flowi6_uid
= sock_net_uid(net
, NULL
),
48 if (rpfilter_addr_unicast(&iph
->daddr
)) {
49 memcpy(&fl6
.saddr
, &iph
->daddr
, sizeof(struct in6_addr
));
50 lookup_flags
= RT6_LOOKUP_F_HAS_SADDR
;
55 fl6
.flowi6_mark
= flags
& XT_RPFILTER_VALID_MARK
? skb
->mark
: 0;
57 if (rpfilter_addr_linklocal(&iph
->saddr
)) {
58 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
59 fl6
.flowi6_oif
= dev
->ifindex
;
60 } else if ((flags
& XT_RPFILTER_LOOSE
) == 0)
61 fl6
.flowi6_oif
= dev
->ifindex
;
63 rt
= (void *)ip6_route_lookup(net
, &fl6
, skb
, lookup_flags
);
67 if (rt
->rt6i_flags
& (RTF_REJECT
|RTF_ANYCAST
))
70 if (rt
->rt6i_flags
& RTF_LOCAL
) {
71 ret
= flags
& XT_RPFILTER_ACCEPT_LOCAL
;
75 if (rt
->rt6i_idev
->dev
== dev
||
76 l3mdev_master_ifindex_rcu(rt
->rt6i_idev
->dev
) == dev
->ifindex
||
77 (flags
& XT_RPFILTER_LOOSE
))
85 rpfilter_is_loopback(const struct sk_buff
*skb
, const struct net_device
*in
)
87 return skb
->pkt_type
== PACKET_LOOPBACK
|| in
->flags
& IFF_LOOPBACK
;
90 static bool rpfilter_mt(const struct sk_buff
*skb
, struct xt_action_param
*par
)
92 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
95 bool invert
= info
->flags
& XT_RPFILTER_INVERT
;
97 if (rpfilter_is_loopback(skb
, xt_in(par
)))
101 saddrtype
= ipv6_addr_type(&iph
->saddr
);
102 if (unlikely(saddrtype
== IPV6_ADDR_ANY
))
103 return true ^ invert
; /* not routable: forward path will drop it */
105 return rpfilter_lookup_reverse6(xt_net(par
), skb
, xt_in(par
),
106 info
->flags
) ^ invert
;
109 static int rpfilter_check(const struct xt_mtchk_param
*par
)
111 const struct xt_rpfilter_info
*info
= par
->matchinfo
;
112 unsigned int options
= ~XT_RPFILTER_OPTION_MASK
;
114 if (info
->flags
& options
) {
115 pr_info_ratelimited("unknown options\n");
119 if (strcmp(par
->table
, "mangle") != 0 &&
120 strcmp(par
->table
, "raw") != 0) {
121 pr_info_ratelimited("only valid in \'raw\' or \'mangle\' table, not \'%s\'\n",
129 static struct xt_match rpfilter_mt_reg __read_mostly
= {
131 .family
= NFPROTO_IPV6
,
132 .checkentry
= rpfilter_check
,
133 .match
= rpfilter_mt
,
134 .matchsize
= sizeof(struct xt_rpfilter_info
),
135 .hooks
= (1 << NF_INET_PRE_ROUTING
),
139 static int __init
rpfilter_mt_init(void)
141 return xt_register_match(&rpfilter_mt_reg
);
144 static void __exit
rpfilter_mt_exit(void)
146 xt_unregister_match(&rpfilter_mt_reg
);
149 module_init(rpfilter_mt_init
);
150 module_exit(rpfilter_mt_exit
);