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