Blame view

net/ipv4/netfilter/nft_fib_ipv4.c 5.31 KB
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  /*
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables_core.h>
  #include <net/netfilter/nf_tables.h>
  #include <net/netfilter/nft_fib.h>
  
  #include <net/ip_fib.h>
  #include <net/route.h>
  
  /* don't try to find route from mcast/bcast/zeronet */
  static __be32 get_saddr(__be32 addr)
  {
  	if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) ||
  	    ipv4_is_zeronet(addr))
  		return 0;
  	return addr;
  }
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
28
29
30
31
32
33
34
35
36
37
38
39
  #define DSCP_BITS     0xfc
  
  void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
  			const struct nft_pktinfo *pkt)
  {
  	const struct nft_fib *priv = nft_expr_priv(expr);
  	u32 *dst = &regs->data[priv->dreg];
  	const struct net_device *dev = NULL;
  	const struct iphdr *iph;
  	__be32 addr;
  
  	if (priv->flags & NFTA_FIB_F_IIF)
0e5a1c7eb   Pablo Neira Ayuso   netfilter: nf_tab...
40
  		dev = nft_in(pkt);
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
41
  	else if (priv->flags & NFTA_FIB_F_OIF)
0e5a1c7eb   Pablo Neira Ayuso   netfilter: nf_tab...
42
  		dev = nft_out(pkt);
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
43
44
45
46
47
48
  
  	iph = ip_hdr(pkt->skb);
  	if (priv->flags & NFTA_FIB_F_DADDR)
  		addr = iph->daddr;
  	else
  		addr = iph->saddr;
0e5a1c7eb   Pablo Neira Ayuso   netfilter: nf_tab...
49
  	*dst = inet_dev_addr_type(nft_net(pkt), dev, addr);
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  }
  EXPORT_SYMBOL_GPL(nft_fib4_eval_type);
  
  static int get_ifindex(const struct net_device *dev)
  {
  	return dev ? dev->ifindex : 0;
  }
  
  void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
  		   const struct nft_pktinfo *pkt)
  {
  	const struct nft_fib *priv = nft_expr_priv(expr);
  	u32 *dest = &regs->data[priv->dreg];
  	const struct iphdr *iph;
  	struct fib_result res;
  	struct flowi4 fl4 = {
  		.flowi4_scope = RT_SCOPE_UNIVERSE,
  		.flowi4_iif = LOOPBACK_IFINDEX,
  	};
  	const struct net_device *oif;
  	struct net_device *found;
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  	int i;
  #endif
  
  	/*
  	 * Do not set flowi4_oif, it restricts results (for example, asking
  	 * for oif 3 will get RTN_UNICAST result even if the daddr exits
  	 * on another interface.
  	 *
  	 * Search results for the desired outinterface instead.
  	 */
  	if (priv->flags & NFTA_FIB_F_OIF)
0e5a1c7eb   Pablo Neira Ayuso   netfilter: nf_tab...
83
  		oif = nft_out(pkt);
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
84
  	else if (priv->flags & NFTA_FIB_F_IIF)
0e5a1c7eb   Pablo Neira Ayuso   netfilter: nf_tab...
85
  		oif = nft_in(pkt);
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
86
87
  	else
  		oif = NULL;
6443ebc3f   Liping Zhang   netfilter: rpfilt...
88
89
  	if (nft_hook(pkt) == NF_INET_PRE_ROUTING &&
  	    nft_fib_is_loopback(pkt->skb, nft_in(pkt))) {
055c4b34b   Phil Sutter   netfilter: nft_fi...
90
  		nft_fib_store_result(dest, priv, pkt,
6443ebc3f   Liping Zhang   netfilter: rpfilt...
91
  				     nft_in(pkt)->ifindex);
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
92
93
94
95
  		return;
  	}
  
  	iph = ip_hdr(pkt->skb);
3b760dcb0   Liping Zhang   netfilter: rpfilt...
96
97
98
  	if (ipv4_is_zeronet(iph->saddr)) {
  		if (ipv4_is_lbcast(iph->daddr) ||
  		    ipv4_is_local_multicast(iph->daddr)) {
055c4b34b   Phil Sutter   netfilter: nft_fi...
99
  			nft_fib_store_result(dest, priv, pkt,
3b760dcb0   Liping Zhang   netfilter: rpfilt...
100
101
102
  					     get_ifindex(pkt->skb->dev));
  			return;
  		}
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  	}
  
  	if (priv->flags & NFTA_FIB_F_MARK)
  		fl4.flowi4_mark = pkt->skb->mark;
  
  	fl4.flowi4_tos = iph->tos & DSCP_BITS;
  
  	if (priv->flags & NFTA_FIB_F_DADDR) {
  		fl4.daddr = iph->daddr;
  		fl4.saddr = get_saddr(iph->saddr);
  	} else {
  		fl4.daddr = iph->saddr;
  		fl4.saddr = get_saddr(iph->daddr);
  	}
e0ffdbc78   Liping Zhang   netfilter: nft_fi...
117
  	*dest = 0;
0e5a1c7eb   Pablo Neira Ayuso   netfilter: nf_tab...
118
  	if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
119
120
121
122
123
  		return;
  
  	switch (res.type) {
  	case RTN_UNICAST:
  		break;
6443ebc3f   Liping Zhang   netfilter: rpfilt...
124
  	case RTN_LOCAL: /* Should not see RTN_LOCAL here */
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  		return;
  	default:
  		break;
  	}
  
         if (!oif) {
                 found = FIB_RES_DEV(res);
                 goto ok;
         }
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  	for (i = 0; i < res.fi->fib_nhs; i++) {
  		struct fib_nh *nh = &res.fi->fib_nh[i];
  
  		if (nh->nh_dev == oif) {
  			found = nh->nh_dev;
  			goto ok;
  		}
  	}
  	return;
  #else
  	found = FIB_RES_DEV(res);
  	if (found != oif)
  		return;
  #endif
  ok:
  	switch (priv->result) {
  	case NFT_FIB_RESULT_OIF:
  		*dest = found->ifindex;
  		break;
  	case NFT_FIB_RESULT_OIFNAME:
  		strncpy((char *)dest, found->name, IFNAMSIZ);
  		break;
  	default:
  		WARN_ON_ONCE(1);
  		break;
  	}
  }
  EXPORT_SYMBOL_GPL(nft_fib4_eval);
  
  static struct nft_expr_type nft_fib4_type;
  
  static const struct nft_expr_ops nft_fib4_type_ops = {
  	.type		= &nft_fib4_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_fib)),
  	.eval		= nft_fib4_eval_type,
  	.init		= nft_fib_init,
  	.dump		= nft_fib_dump,
  	.validate	= nft_fib_validate,
  };
  
  static const struct nft_expr_ops nft_fib4_ops = {
  	.type		= &nft_fib4_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_fib)),
  	.eval		= nft_fib4_eval,
  	.init		= nft_fib_init,
  	.dump		= nft_fib_dump,
  	.validate	= nft_fib_validate,
  };
  
  static const struct nft_expr_ops *
  nft_fib4_select_ops(const struct nft_ctx *ctx,
  		    const struct nlattr * const tb[])
  {
  	enum nft_fib_result result;
  
  	if (!tb[NFTA_FIB_RESULT])
  		return ERR_PTR(-EINVAL);
11583438b   Liping Zhang   netfilter: nft_fi...
193
  	result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT]));
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  
  	switch (result) {
  	case NFT_FIB_RESULT_OIF:
  		return &nft_fib4_ops;
  	case NFT_FIB_RESULT_OIFNAME:
  		return &nft_fib4_ops;
  	case NFT_FIB_RESULT_ADDRTYPE:
  		return &nft_fib4_type_ops;
  	default:
  		return ERR_PTR(-EOPNOTSUPP);
  	}
  }
  
  static struct nft_expr_type nft_fib4_type __read_mostly = {
  	.name		= "fib",
d4ef38354   Arushi Singhal   netfilter: Remove...
209
  	.select_ops	= nft_fib4_select_ops,
f6d0cbcf0   Florian Westphal   netfilter: nf_tab...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  	.policy		= nft_fib_policy,
  	.maxattr	= NFTA_FIB_MAX,
  	.family		= NFPROTO_IPV4,
  	.owner		= THIS_MODULE,
  };
  
  static int __init nft_fib4_module_init(void)
  {
  	return nft_register_expr(&nft_fib4_type);
  }
  
  static void __exit nft_fib4_module_exit(void)
  {
  	nft_unregister_expr(&nft_fib4_type);
  }
  
  module_init(nft_fib4_module_init);
  module_exit(nft_fib4_module_exit);
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
  MODULE_ALIAS_NFT_AF_EXPR(2, "fib");