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 enum nft_registers dreg:8; 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 priv->dreg = nft_parse_register(tb[NFTA_XFRM_DREG]); 90 return nft_validate_register_store(ctx, priv->dreg, NULL, 91 NFT_DATA_VALUE, len); 92 } 93 94 /* Return true if key asks for daddr/saddr and current 95 * state does have a valid address (BEET, TUNNEL). 96 */ 97 static bool xfrm_state_addr_ok(enum nft_xfrm_keys k, u8 family, u8 mode) 98 { 99 switch (k) { 100 case NFT_XFRM_KEY_DADDR_IP4: 101 case NFT_XFRM_KEY_SADDR_IP4: 102 if (family == NFPROTO_IPV4) 103 break; 104 return false; 105 case NFT_XFRM_KEY_DADDR_IP6: 106 case NFT_XFRM_KEY_SADDR_IP6: 107 if (family == NFPROTO_IPV6) 108 break; 109 return false; 110 default: 111 return true; 112 } 113 114 return mode == XFRM_MODE_BEET || mode == XFRM_MODE_TUNNEL; 115 } 116 117 static void nft_xfrm_state_get_key(const struct nft_xfrm *priv, 118 struct nft_regs *regs, 119 const struct xfrm_state *state) 120 { 121 u32 *dest = ®s->data[priv->dreg]; 122 123 if (!xfrm_state_addr_ok(priv->key, 124 state->props.family, 125 state->props.mode)) { 126 regs->verdict.code = NFT_BREAK; 127 return; 128 } 129 130 switch (priv->key) { 131 case NFT_XFRM_KEY_UNSPEC: 132 case __NFT_XFRM_KEY_MAX: 133 WARN_ON_ONCE(1); 134 break; 135 case NFT_XFRM_KEY_DADDR_IP4: 136 *dest = state->id.daddr.a4; 137 return; 138 case NFT_XFRM_KEY_DADDR_IP6: 139 memcpy(dest, &state->id.daddr.in6, sizeof(struct in6_addr)); 140 return; 141 case NFT_XFRM_KEY_SADDR_IP4: 142 *dest = state->props.saddr.a4; 143 return; 144 case NFT_XFRM_KEY_SADDR_IP6: 145 memcpy(dest, &state->props.saddr.in6, sizeof(struct in6_addr)); 146 return; 147 case NFT_XFRM_KEY_REQID: 148 *dest = state->props.reqid; 149 return; 150 case NFT_XFRM_KEY_SPI: 151 *dest = state->id.spi; 152 return; 153 } 154 155 regs->verdict.code = NFT_BREAK; 156 } 157 158 static void nft_xfrm_get_eval_in(const struct nft_xfrm *priv, 159 struct nft_regs *regs, 160 const struct nft_pktinfo *pkt) 161 { 162 const struct sec_path *sp = skb_sec_path(pkt->skb); 163 const struct xfrm_state *state; 164 165 if (sp == NULL || sp->len <= priv->spnum) { 166 regs->verdict.code = NFT_BREAK; 167 return; 168 } 169 170 state = sp->xvec[priv->spnum]; 171 nft_xfrm_state_get_key(priv, regs, state); 172 } 173 174 static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv, 175 struct nft_regs *regs, 176 const struct nft_pktinfo *pkt) 177 { 178 const struct dst_entry *dst = skb_dst(pkt->skb); 179 int i; 180 181 for (i = 0; dst && dst->xfrm; 182 dst = ((const struct xfrm_dst *)dst)->child, i++) { 183 if (i < priv->spnum) 184 continue; 185 186 nft_xfrm_state_get_key(priv, regs, dst->xfrm); 187 return; 188 } 189 190 regs->verdict.code = NFT_BREAK; 191 } 192 193 static void nft_xfrm_get_eval(const struct nft_expr *expr, 194 struct nft_regs *regs, 195 const struct nft_pktinfo *pkt) 196 { 197 const struct nft_xfrm *priv = nft_expr_priv(expr); 198 199 switch (priv->dir) { 200 case XFRM_POLICY_IN: 201 nft_xfrm_get_eval_in(priv, regs, pkt); 202 break; 203 case XFRM_POLICY_OUT: 204 nft_xfrm_get_eval_out(priv, regs, pkt); 205 break; 206 default: 207 WARN_ON_ONCE(1); 208 regs->verdict.code = NFT_BREAK; 209 break; 210 } 211 } 212 213 static int nft_xfrm_get_dump(struct sk_buff *skb, 214 const struct nft_expr *expr) 215 { 216 const struct nft_xfrm *priv = nft_expr_priv(expr); 217 218 if (nft_dump_register(skb, NFTA_XFRM_DREG, priv->dreg)) 219 return -1; 220 221 if (nla_put_be32(skb, NFTA_XFRM_KEY, htonl(priv->key))) 222 return -1; 223 if (nla_put_u8(skb, NFTA_XFRM_DIR, priv->dir)) 224 return -1; 225 if (nla_put_be32(skb, NFTA_XFRM_SPNUM, htonl(priv->spnum))) 226 return -1; 227 228 return 0; 229 } 230 231 static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, 232 const struct nft_data **data) 233 { 234 const struct nft_xfrm *priv = nft_expr_priv(expr); 235 unsigned int hooks; 236 237 switch (priv->dir) { 238 case XFRM_POLICY_IN: 239 hooks = (1 << NF_INET_FORWARD) | 240 (1 << NF_INET_LOCAL_IN) | 241 (1 << NF_INET_PRE_ROUTING); 242 break; 243 case XFRM_POLICY_OUT: 244 hooks = (1 << NF_INET_FORWARD) | 245 (1 << NF_INET_LOCAL_OUT) | 246 (1 << NF_INET_POST_ROUTING); 247 break; 248 default: 249 WARN_ON_ONCE(1); 250 return -EINVAL; 251 } 252 253 return nft_chain_validate_hooks(ctx->chain, hooks); 254 } 255 256 257 static struct nft_expr_type nft_xfrm_type; 258 static const struct nft_expr_ops nft_xfrm_get_ops = { 259 .type = &nft_xfrm_type, 260 .size = NFT_EXPR_SIZE(sizeof(struct nft_xfrm)), 261 .eval = nft_xfrm_get_eval, 262 .init = nft_xfrm_get_init, 263 .dump = nft_xfrm_get_dump, 264 .validate = nft_xfrm_validate, 265 }; 266 267 static struct nft_expr_type nft_xfrm_type __read_mostly = { 268 .name = "xfrm", 269 .ops = &nft_xfrm_get_ops, 270 .policy = nft_xfrm_policy, 271 .maxattr = NFTA_XFRM_MAX, 272 .owner = THIS_MODULE, 273 }; 274 275 static int __init nft_xfrm_module_init(void) 276 { 277 return nft_register_expr(&nft_xfrm_type); 278 } 279 280 static void __exit nft_xfrm_module_exit(void) 281 { 282 nft_unregister_expr(&nft_xfrm_type); 283 } 284 285 module_init(nft_xfrm_module_init); 286 module_exit(nft_xfrm_module_exit); 287 288 MODULE_LICENSE("GPL"); 289 MODULE_DESCRIPTION("nf_tables: xfrm/IPSec matching"); 290 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 291 MODULE_AUTHOR("Máté Eckl <ecklm94@gmail.com>"); 292 MODULE_ALIAS_NFT_EXPR("xfrm"); 293