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