1
2
3
4
5
6
7
8
9#include <linux/kernel.h>
10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/netlink.h>
13#include <linux/netfilter.h>
14#include <linux/netfilter/nf_tables.h>
15#include <net/netfilter/nf_tables_core.h>
16#include <net/netfilter/nf_tables.h>
17#include <linux/in.h>
18#include <net/xfrm.h>
19
20static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = {
21 [NFTA_XFRM_KEY] = { .type = NLA_U32 },
22 [NFTA_XFRM_DIR] = { .type = NLA_U8 },
23 [NFTA_XFRM_SPNUM] = { .type = NLA_U32 },
24 [NFTA_XFRM_DREG] = { .type = NLA_U32 },
25};
26
27struct nft_xfrm {
28 enum nft_xfrm_keys key:8;
29 enum nft_registers dreg:8;
30 u8 dir;
31 u8 spnum;
32};
33
34static int nft_xfrm_get_init(const struct nft_ctx *ctx,
35 const struct nft_expr *expr,
36 const struct nlattr * const tb[])
37{
38 struct nft_xfrm *priv = nft_expr_priv(expr);
39 unsigned int len = 0;
40 u32 spnum = 0;
41 u8 dir;
42
43 if (!tb[NFTA_XFRM_KEY] || !tb[NFTA_XFRM_DIR] || !tb[NFTA_XFRM_DREG])
44 return -EINVAL;
45
46 switch (ctx->family) {
47 case NFPROTO_IPV4:
48 case NFPROTO_IPV6:
49 case NFPROTO_INET:
50 break;
51 default:
52 return -EOPNOTSUPP;
53 }
54
55 priv->key = ntohl(nla_get_u32(tb[NFTA_XFRM_KEY]));
56 switch (priv->key) {
57 case NFT_XFRM_KEY_REQID:
58 case NFT_XFRM_KEY_SPI:
59 len = sizeof(u32);
60 break;
61 case NFT_XFRM_KEY_DADDR_IP4:
62 case NFT_XFRM_KEY_SADDR_IP4:
63 len = sizeof(struct in_addr);
64 break;
65 case NFT_XFRM_KEY_DADDR_IP6:
66 case NFT_XFRM_KEY_SADDR_IP6:
67 len = sizeof(struct in6_addr);
68 break;
69 default:
70 return -EINVAL;
71 }
72
73 dir = nla_get_u8(tb[NFTA_XFRM_DIR]);
74 switch (dir) {
75 case XFRM_POLICY_IN:
76 case XFRM_POLICY_OUT:
77 priv->dir = dir;
78 break;
79 default:
80 return -EINVAL;
81 }
82
83 if (tb[NFTA_XFRM_SPNUM])
84 spnum = ntohl(nla_get_be32(tb[NFTA_XFRM_SPNUM]));
85
86 if (spnum >= XFRM_MAX_DEPTH)
87 return -ERANGE;
88
89 priv->spnum = spnum;
90
91 priv->dreg = nft_parse_register(tb[NFTA_XFRM_DREG]);
92 return nft_validate_register_store(ctx, priv->dreg, NULL,
93 NFT_DATA_VALUE, len);
94}
95
96
97
98
99static bool xfrm_state_addr_ok(enum nft_xfrm_keys k, u8 family, u8 mode)
100{
101 switch (k) {
102 case NFT_XFRM_KEY_DADDR_IP4:
103 case NFT_XFRM_KEY_SADDR_IP4:
104 if (family == NFPROTO_IPV4)
105 break;
106 return false;
107 case NFT_XFRM_KEY_DADDR_IP6:
108 case NFT_XFRM_KEY_SADDR_IP6:
109 if (family == NFPROTO_IPV6)
110 break;
111 return false;
112 default:
113 return true;
114 }
115
116 return mode == XFRM_MODE_BEET || mode == XFRM_MODE_TUNNEL;
117}
118
119static void nft_xfrm_state_get_key(const struct nft_xfrm *priv,
120 struct nft_regs *regs,
121 const struct xfrm_state *state)
122{
123 u32 *dest = ®s->data[priv->dreg];
124
125 if (!xfrm_state_addr_ok(priv->key,
126 state->props.family,
127 state->props.mode)) {
128 regs->verdict.code = NFT_BREAK;
129 return;
130 }
131
132 switch (priv->key) {
133 case NFT_XFRM_KEY_UNSPEC:
134 case __NFT_XFRM_KEY_MAX:
135 WARN_ON_ONCE(1);
136 break;
137 case NFT_XFRM_KEY_DADDR_IP4:
138 *dest = state->id.daddr.a4;
139 return;
140 case NFT_XFRM_KEY_DADDR_IP6:
141 memcpy(dest, &state->id.daddr.in6, sizeof(struct in6_addr));
142 return;
143 case NFT_XFRM_KEY_SADDR_IP4:
144 *dest = state->props.saddr.a4;
145 return;
146 case NFT_XFRM_KEY_SADDR_IP6:
147 memcpy(dest, &state->props.saddr.in6, sizeof(struct in6_addr));
148 return;
149 case NFT_XFRM_KEY_REQID:
150 *dest = state->props.reqid;
151 return;
152 case NFT_XFRM_KEY_SPI:
153 *dest = state->id.spi;
154 return;
155 }
156
157 regs->verdict.code = NFT_BREAK;
158}
159
160static void nft_xfrm_get_eval_in(const struct nft_xfrm *priv,
161 struct nft_regs *regs,
162 const struct nft_pktinfo *pkt)
163{
164 const struct sec_path *sp = pkt->skb->sp;
165 const struct xfrm_state *state;
166
167 if (sp == NULL || sp->len <= priv->spnum) {
168 regs->verdict.code = NFT_BREAK;
169 return;
170 }
171
172 state = sp->xvec[priv->spnum];
173 nft_xfrm_state_get_key(priv, regs, state);
174}
175
176static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv,
177 struct nft_regs *regs,
178 const struct nft_pktinfo *pkt)
179{
180 const struct dst_entry *dst = skb_dst(pkt->skb);
181 int i;
182
183 for (i = 0; dst && dst->xfrm;
184 dst = ((const struct xfrm_dst *)dst)->child, i++) {
185 if (i < priv->spnum)
186 continue;
187
188 nft_xfrm_state_get_key(priv, regs, dst->xfrm);
189 return;
190 }
191
192 regs->verdict.code = NFT_BREAK;
193}
194
195static void nft_xfrm_get_eval(const struct nft_expr *expr,
196 struct nft_regs *regs,
197 const struct nft_pktinfo *pkt)
198{
199 const struct nft_xfrm *priv = nft_expr_priv(expr);
200
201 switch (priv->dir) {
202 case XFRM_POLICY_IN:
203 nft_xfrm_get_eval_in(priv, regs, pkt);
204 break;
205 case XFRM_POLICY_OUT:
206 nft_xfrm_get_eval_out(priv, regs, pkt);
207 break;
208 default:
209 WARN_ON_ONCE(1);
210 regs->verdict.code = NFT_BREAK;
211 break;
212 }
213}
214
215static int nft_xfrm_get_dump(struct sk_buff *skb,
216 const struct nft_expr *expr)
217{
218 const struct nft_xfrm *priv = nft_expr_priv(expr);
219
220 if (nft_dump_register(skb, NFTA_XFRM_DREG, priv->dreg))
221 return -1;
222
223 if (nla_put_be32(skb, NFTA_XFRM_KEY, htonl(priv->key)))
224 return -1;
225 if (nla_put_u8(skb, NFTA_XFRM_DIR, priv->dir))
226 return -1;
227 if (nla_put_be32(skb, NFTA_XFRM_SPNUM, htonl(priv->spnum)))
228 return -1;
229
230 return 0;
231}
232
233static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
234 const struct nft_data **data)
235{
236 const struct nft_xfrm *priv = nft_expr_priv(expr);
237 unsigned int hooks;
238
239 switch (priv->dir) {
240 case XFRM_POLICY_IN:
241 hooks = (1 << NF_INET_FORWARD) |
242 (1 << NF_INET_LOCAL_IN) |
243 (1 << NF_INET_PRE_ROUTING);
244 break;
245 case XFRM_POLICY_OUT:
246 hooks = (1 << NF_INET_FORWARD) |
247 (1 << NF_INET_LOCAL_OUT) |
248 (1 << NF_INET_POST_ROUTING);
249 break;
250 default:
251 WARN_ON_ONCE(1);
252 return -EINVAL;
253 }
254
255 return nft_chain_validate_hooks(ctx->chain, hooks);
256}
257
258
259static struct nft_expr_type nft_xfrm_type;
260static const struct nft_expr_ops nft_xfrm_get_ops = {
261 .type = &nft_xfrm_type,
262 .size = NFT_EXPR_SIZE(sizeof(struct nft_xfrm)),
263 .eval = nft_xfrm_get_eval,
264 .init = nft_xfrm_get_init,
265 .dump = nft_xfrm_get_dump,
266 .validate = nft_xfrm_validate,
267};
268
269static struct nft_expr_type nft_xfrm_type __read_mostly = {
270 .name = "xfrm",
271 .ops = &nft_xfrm_get_ops,
272 .policy = nft_xfrm_policy,
273 .maxattr = NFTA_XFRM_MAX,
274 .owner = THIS_MODULE,
275};
276
277static int __init nft_xfrm_module_init(void)
278{
279 return nft_register_expr(&nft_xfrm_type);
280}
281
282static void __exit nft_xfrm_module_exit(void)
283{
284 nft_unregister_expr(&nft_xfrm_type);
285}
286
287module_init(nft_xfrm_module_init);
288module_exit(nft_xfrm_module_exit);
289
290MODULE_LICENSE("GPL");
291MODULE_DESCRIPTION("nf_tables: xfrm/IPSec matching");
292MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
293MODULE_AUTHOR("Máté Eckl <ecklm94@gmail.com>");
294MODULE_ALIAS_NFT_EXPR("xfrm");
295