1
2
3
4
5
6
7
8
9
10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11#include <linux/module.h>
12#include <linux/skbuff.h>
13#include <linux/netdevice.h>
14#include <linux/ip.h>
15#include <net/ip.h>
16#include <net/ip_fib.h>
17#include <net/route.h>
18
19#include <linux/netfilter/xt_rpfilter.h>
20#include <linux/netfilter/x_tables.h>
21
22MODULE_LICENSE("GPL");
23MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
24MODULE_DESCRIPTION("iptables: ipv4 reverse path filter match");
25
26
27static __be32 rpfilter_get_saddr(__be32 addr)
28{
29 if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) ||
30 ipv4_is_zeronet(addr))
31 return 0;
32 return addr;
33}
34
35static bool rpfilter_lookup_reverse(struct net *net, struct flowi4 *fl4,
36 const struct net_device *dev, u8 flags)
37{
38 struct fib_result res;
39 bool dev_match;
40 int ret __maybe_unused;
41
42 if (fib_lookup(net, fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
43 return false;
44
45 if (res.type != RTN_UNICAST) {
46 if (res.type != RTN_LOCAL || !(flags & XT_RPFILTER_ACCEPT_LOCAL))
47 return false;
48 }
49 dev_match = false;
50#ifdef CONFIG_IP_ROUTE_MULTIPATH
51 for (ret = 0; ret < res.fi->fib_nhs; ret++) {
52 struct fib_nh *nh = &res.fi->fib_nh[ret];
53
54 if (nh->nh_dev == dev) {
55 dev_match = true;
56 break;
57 }
58 }
59#else
60 if (FIB_RES_DEV(res) == dev)
61 dev_match = true;
62#endif
63 return dev_match || flags & XT_RPFILTER_LOOSE;
64}
65
66static bool
67rpfilter_is_loopback(const struct sk_buff *skb, const struct net_device *in)
68{
69 return skb->pkt_type == PACKET_LOOPBACK || in->flags & IFF_LOOPBACK;
70}
71
72static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
73{
74 const struct xt_rpfilter_info *info;
75 const struct iphdr *iph;
76 struct flowi4 flow;
77 bool invert;
78
79 info = par->matchinfo;
80 invert = info->flags & XT_RPFILTER_INVERT;
81
82 if (rpfilter_is_loopback(skb, xt_in(par)))
83 return true ^ invert;
84
85 iph = ip_hdr(skb);
86 if (ipv4_is_zeronet(iph->saddr)) {
87 if (ipv4_is_lbcast(iph->daddr) ||
88 ipv4_is_local_multicast(iph->daddr))
89 return true ^ invert;
90 }
91
92 flow.flowi4_iif = LOOPBACK_IFINDEX;
93 flow.daddr = iph->saddr;
94 flow.saddr = rpfilter_get_saddr(iph->daddr);
95 flow.flowi4_oif = 0;
96 flow.flowi4_mark = info->flags & XT_RPFILTER_VALID_MARK ? skb->mark : 0;
97 flow.flowi4_tos = RT_TOS(iph->tos);
98 flow.flowi4_scope = RT_SCOPE_UNIVERSE;
99
100 return rpfilter_lookup_reverse(xt_net(par), &flow, xt_in(par), info->flags) ^ invert;
101}
102
103static int rpfilter_check(const struct xt_mtchk_param *par)
104{
105 const struct xt_rpfilter_info *info = par->matchinfo;
106 unsigned int options = ~XT_RPFILTER_OPTION_MASK;
107 if (info->flags & options) {
108 pr_info_ratelimited("unknown options\n");
109 return -EINVAL;
110 }
111
112 if (strcmp(par->table, "mangle") != 0 &&
113 strcmp(par->table, "raw") != 0) {
114 pr_info_ratelimited("only valid in \'raw\' or \'mangle\' table, not \'%s\'\n",
115 par->table);
116 return -EINVAL;
117 }
118
119 return 0;
120}
121
122static struct xt_match rpfilter_mt_reg __read_mostly = {
123 .name = "rpfilter",
124 .family = NFPROTO_IPV4,
125 .checkentry = rpfilter_check,
126 .match = rpfilter_mt,
127 .matchsize = sizeof(struct xt_rpfilter_info),
128 .hooks = (1 << NF_INET_PRE_ROUTING),
129 .me = THIS_MODULE
130};
131
132static int __init rpfilter_mt_init(void)
133{
134 return xt_register_match(&rpfilter_mt_reg);
135}
136
137static void __exit rpfilter_mt_exit(void)
138{
139 xt_unregister_match(&rpfilter_mt_reg);
140}
141
142module_init(rpfilter_mt_init);
143module_exit(rpfilter_mt_exit);
144