cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nft_xfrm.c (7371B)


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