Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.2-rc2 294 lines 6.8 kB view raw
1/* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License version 2 as 4 * published by the Free Software Foundation. 5 * 6 * Generic part shared by ipv4 and ipv6 backends. 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/* Return true if key asks for daddr/saddr and current 97 * state does have a valid address (BEET, TUNNEL). 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 = &regs->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 = skb_sec_path(pkt->skb); 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");