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_synproxy.c (10156B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/types.h>
      3#include <net/ip.h>
      4#include <net/tcp.h>
      5#include <net/netlink.h>
      6#include <net/netfilter/nf_tables.h>
      7#include <net/netfilter/nf_conntrack.h>
      8#include <net/netfilter/nf_conntrack_synproxy.h>
      9#include <net/netfilter/nf_synproxy.h>
     10#include <linux/netfilter/nf_tables.h>
     11#include <linux/netfilter/nf_synproxy.h>
     12
     13struct nft_synproxy {
     14	struct nf_synproxy_info	info;
     15};
     16
     17static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = {
     18	[NFTA_SYNPROXY_MSS]		= { .type = NLA_U16 },
     19	[NFTA_SYNPROXY_WSCALE]		= { .type = NLA_U8 },
     20	[NFTA_SYNPROXY_FLAGS]		= { .type = NLA_U32 },
     21};
     22
     23static void nft_synproxy_tcp_options(struct synproxy_options *opts,
     24				     const struct tcphdr *tcp,
     25				     struct synproxy_net *snet,
     26				     struct nf_synproxy_info *info,
     27				     const struct nft_synproxy *priv)
     28{
     29	this_cpu_inc(snet->stats->syn_received);
     30	if (tcp->ece && tcp->cwr)
     31		opts->options |= NF_SYNPROXY_OPT_ECN;
     32
     33	opts->options &= priv->info.options;
     34	opts->mss_encode = opts->mss_option;
     35	opts->mss_option = info->mss;
     36	if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
     37		synproxy_init_timestamp_cookie(info, opts);
     38	else
     39		opts->options &= ~(NF_SYNPROXY_OPT_WSCALE |
     40				   NF_SYNPROXY_OPT_SACK_PERM |
     41				   NF_SYNPROXY_OPT_ECN);
     42}
     43
     44static void nft_synproxy_eval_v4(const struct nft_synproxy *priv,
     45				 struct nft_regs *regs,
     46				 const struct nft_pktinfo *pkt,
     47				 const struct tcphdr *tcp,
     48				 struct tcphdr *_tcph,
     49				 struct synproxy_options *opts)
     50{
     51	struct nf_synproxy_info info = priv->info;
     52	struct net *net = nft_net(pkt);
     53	struct synproxy_net *snet = synproxy_pernet(net);
     54	struct sk_buff *skb = pkt->skb;
     55
     56	if (tcp->syn) {
     57		/* Initial SYN from client */
     58		nft_synproxy_tcp_options(opts, tcp, snet, &info, priv);
     59		synproxy_send_client_synack(net, skb, tcp, opts);
     60		consume_skb(skb);
     61		regs->verdict.code = NF_STOLEN;
     62	} else if (tcp->ack) {
     63		/* ACK from client */
     64		if (synproxy_recv_client_ack(net, skb, tcp, opts,
     65					     ntohl(tcp->seq))) {
     66			consume_skb(skb);
     67			regs->verdict.code = NF_STOLEN;
     68		} else {
     69			regs->verdict.code = NF_DROP;
     70		}
     71	}
     72}
     73
     74#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
     75static void nft_synproxy_eval_v6(const struct nft_synproxy *priv,
     76				 struct nft_regs *regs,
     77				 const struct nft_pktinfo *pkt,
     78				 const struct tcphdr *tcp,
     79				 struct tcphdr *_tcph,
     80				 struct synproxy_options *opts)
     81{
     82	struct nf_synproxy_info info = priv->info;
     83	struct net *net = nft_net(pkt);
     84	struct synproxy_net *snet = synproxy_pernet(net);
     85	struct sk_buff *skb = pkt->skb;
     86
     87	if (tcp->syn) {
     88		/* Initial SYN from client */
     89		nft_synproxy_tcp_options(opts, tcp, snet, &info, priv);
     90		synproxy_send_client_synack_ipv6(net, skb, tcp, opts);
     91		consume_skb(skb);
     92		regs->verdict.code = NF_STOLEN;
     93	} else if (tcp->ack) {
     94		/* ACK from client */
     95		if (synproxy_recv_client_ack_ipv6(net, skb, tcp, opts,
     96						  ntohl(tcp->seq))) {
     97			consume_skb(skb);
     98			regs->verdict.code = NF_STOLEN;
     99		} else {
    100			regs->verdict.code = NF_DROP;
    101		}
    102	}
    103}
    104#endif /* CONFIG_NF_TABLES_IPV6*/
    105
    106static void nft_synproxy_do_eval(const struct nft_synproxy *priv,
    107				 struct nft_regs *regs,
    108				 const struct nft_pktinfo *pkt)
    109{
    110	struct synproxy_options opts = {};
    111	struct sk_buff *skb = pkt->skb;
    112	int thoff = nft_thoff(pkt);
    113	const struct tcphdr *tcp;
    114	struct tcphdr _tcph;
    115
    116	if (pkt->tprot != IPPROTO_TCP) {
    117		regs->verdict.code = NFT_BREAK;
    118		return;
    119	}
    120
    121	if (nf_ip_checksum(skb, nft_hook(pkt), thoff, IPPROTO_TCP)) {
    122		regs->verdict.code = NF_DROP;
    123		return;
    124	}
    125
    126	tcp = skb_header_pointer(skb, thoff,
    127				 sizeof(struct tcphdr),
    128				 &_tcph);
    129	if (!tcp) {
    130		regs->verdict.code = NF_DROP;
    131		return;
    132	}
    133
    134	if (!synproxy_parse_options(skb, thoff, tcp, &opts)) {
    135		regs->verdict.code = NF_DROP;
    136		return;
    137	}
    138
    139	switch (skb->protocol) {
    140	case htons(ETH_P_IP):
    141		nft_synproxy_eval_v4(priv, regs, pkt, tcp, &_tcph, &opts);
    142		return;
    143#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
    144	case htons(ETH_P_IPV6):
    145		nft_synproxy_eval_v6(priv, regs, pkt, tcp, &_tcph, &opts);
    146		return;
    147#endif
    148	}
    149	regs->verdict.code = NFT_BREAK;
    150}
    151
    152static int nft_synproxy_do_init(const struct nft_ctx *ctx,
    153				const struct nlattr * const tb[],
    154				struct nft_synproxy *priv)
    155{
    156	struct synproxy_net *snet = synproxy_pernet(ctx->net);
    157	u32 flags;
    158	int err;
    159
    160	if (tb[NFTA_SYNPROXY_MSS])
    161		priv->info.mss = ntohs(nla_get_be16(tb[NFTA_SYNPROXY_MSS]));
    162	if (tb[NFTA_SYNPROXY_WSCALE])
    163		priv->info.wscale = nla_get_u8(tb[NFTA_SYNPROXY_WSCALE]);
    164	if (tb[NFTA_SYNPROXY_FLAGS]) {
    165		flags = ntohl(nla_get_be32(tb[NFTA_SYNPROXY_FLAGS]));
    166		if (flags & ~NF_SYNPROXY_OPT_MASK)
    167			return -EOPNOTSUPP;
    168		priv->info.options = flags;
    169	}
    170
    171	err = nf_ct_netns_get(ctx->net, ctx->family);
    172	if (err)
    173		return err;
    174
    175	switch (ctx->family) {
    176	case NFPROTO_IPV4:
    177		err = nf_synproxy_ipv4_init(snet, ctx->net);
    178		if (err)
    179			goto nf_ct_failure;
    180		break;
    181#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
    182	case NFPROTO_IPV6:
    183		err = nf_synproxy_ipv6_init(snet, ctx->net);
    184		if (err)
    185			goto nf_ct_failure;
    186		break;
    187#endif
    188	case NFPROTO_INET:
    189	case NFPROTO_BRIDGE:
    190		err = nf_synproxy_ipv4_init(snet, ctx->net);
    191		if (err)
    192			goto nf_ct_failure;
    193		err = nf_synproxy_ipv6_init(snet, ctx->net);
    194		if (err) {
    195			nf_synproxy_ipv4_fini(snet, ctx->net);
    196			goto nf_ct_failure;
    197		}
    198		break;
    199	}
    200
    201	return 0;
    202
    203nf_ct_failure:
    204	nf_ct_netns_put(ctx->net, ctx->family);
    205	return err;
    206}
    207
    208static void nft_synproxy_do_destroy(const struct nft_ctx *ctx)
    209{
    210	struct synproxy_net *snet = synproxy_pernet(ctx->net);
    211
    212	switch (ctx->family) {
    213	case NFPROTO_IPV4:
    214		nf_synproxy_ipv4_fini(snet, ctx->net);
    215		break;
    216#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
    217	case NFPROTO_IPV6:
    218		nf_synproxy_ipv6_fini(snet, ctx->net);
    219		break;
    220#endif
    221	case NFPROTO_INET:
    222	case NFPROTO_BRIDGE:
    223		nf_synproxy_ipv4_fini(snet, ctx->net);
    224		nf_synproxy_ipv6_fini(snet, ctx->net);
    225		break;
    226	}
    227	nf_ct_netns_put(ctx->net, ctx->family);
    228}
    229
    230static int nft_synproxy_do_dump(struct sk_buff *skb, struct nft_synproxy *priv)
    231{
    232	if (nla_put_be16(skb, NFTA_SYNPROXY_MSS, htons(priv->info.mss)) ||
    233	    nla_put_u8(skb, NFTA_SYNPROXY_WSCALE, priv->info.wscale) ||
    234	    nla_put_be32(skb, NFTA_SYNPROXY_FLAGS, htonl(priv->info.options)))
    235		goto nla_put_failure;
    236
    237	return 0;
    238
    239nla_put_failure:
    240	return -1;
    241}
    242
    243static void nft_synproxy_eval(const struct nft_expr *expr,
    244			      struct nft_regs *regs,
    245			      const struct nft_pktinfo *pkt)
    246{
    247	const struct nft_synproxy *priv = nft_expr_priv(expr);
    248
    249	nft_synproxy_do_eval(priv, regs, pkt);
    250}
    251
    252static int nft_synproxy_validate(const struct nft_ctx *ctx,
    253				 const struct nft_expr *expr,
    254				 const struct nft_data **data)
    255{
    256	return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) |
    257						    (1 << NF_INET_FORWARD));
    258}
    259
    260static int nft_synproxy_init(const struct nft_ctx *ctx,
    261			     const struct nft_expr *expr,
    262			     const struct nlattr * const tb[])
    263{
    264	struct nft_synproxy *priv = nft_expr_priv(expr);
    265
    266	return nft_synproxy_do_init(ctx, tb, priv);
    267}
    268
    269static void nft_synproxy_destroy(const struct nft_ctx *ctx,
    270				 const struct nft_expr *expr)
    271{
    272	nft_synproxy_do_destroy(ctx);
    273}
    274
    275static int nft_synproxy_dump(struct sk_buff *skb, const struct nft_expr *expr)
    276{
    277	struct nft_synproxy *priv = nft_expr_priv(expr);
    278
    279	return nft_synproxy_do_dump(skb, priv);
    280}
    281
    282static struct nft_expr_type nft_synproxy_type;
    283static const struct nft_expr_ops nft_synproxy_ops = {
    284	.eval		= nft_synproxy_eval,
    285	.size		= NFT_EXPR_SIZE(sizeof(struct nft_synproxy)),
    286	.init		= nft_synproxy_init,
    287	.destroy	= nft_synproxy_destroy,
    288	.dump		= nft_synproxy_dump,
    289	.type		= &nft_synproxy_type,
    290	.validate	= nft_synproxy_validate,
    291	.reduce		= NFT_REDUCE_READONLY,
    292};
    293
    294static struct nft_expr_type nft_synproxy_type __read_mostly = {
    295	.ops		= &nft_synproxy_ops,
    296	.name		= "synproxy",
    297	.owner		= THIS_MODULE,
    298	.policy		= nft_synproxy_policy,
    299	.maxattr	= NFTA_SYNPROXY_MAX,
    300};
    301
    302static int nft_synproxy_obj_init(const struct nft_ctx *ctx,
    303				 const struct nlattr * const tb[],
    304				 struct nft_object *obj)
    305{
    306	struct nft_synproxy *priv = nft_obj_data(obj);
    307
    308	return nft_synproxy_do_init(ctx, tb, priv);
    309}
    310
    311static void nft_synproxy_obj_destroy(const struct nft_ctx *ctx,
    312				     struct nft_object *obj)
    313{
    314	nft_synproxy_do_destroy(ctx);
    315}
    316
    317static int nft_synproxy_obj_dump(struct sk_buff *skb,
    318				 struct nft_object *obj, bool reset)
    319{
    320	struct nft_synproxy *priv = nft_obj_data(obj);
    321
    322	return nft_synproxy_do_dump(skb, priv);
    323}
    324
    325static void nft_synproxy_obj_eval(struct nft_object *obj,
    326				  struct nft_regs *regs,
    327				  const struct nft_pktinfo *pkt)
    328{
    329	const struct nft_synproxy *priv = nft_obj_data(obj);
    330
    331	nft_synproxy_do_eval(priv, regs, pkt);
    332}
    333
    334static void nft_synproxy_obj_update(struct nft_object *obj,
    335				    struct nft_object *newobj)
    336{
    337	struct nft_synproxy *newpriv = nft_obj_data(newobj);
    338	struct nft_synproxy *priv = nft_obj_data(obj);
    339
    340	priv->info = newpriv->info;
    341}
    342
    343static struct nft_object_type nft_synproxy_obj_type;
    344static const struct nft_object_ops nft_synproxy_obj_ops = {
    345	.type		= &nft_synproxy_obj_type,
    346	.size		= sizeof(struct nft_synproxy),
    347	.init		= nft_synproxy_obj_init,
    348	.destroy	= nft_synproxy_obj_destroy,
    349	.dump		= nft_synproxy_obj_dump,
    350	.eval		= nft_synproxy_obj_eval,
    351	.update		= nft_synproxy_obj_update,
    352};
    353
    354static struct nft_object_type nft_synproxy_obj_type __read_mostly = {
    355	.type		= NFT_OBJECT_SYNPROXY,
    356	.ops		= &nft_synproxy_obj_ops,
    357	.maxattr	= NFTA_SYNPROXY_MAX,
    358	.policy		= nft_synproxy_policy,
    359	.owner		= THIS_MODULE,
    360};
    361
    362static int __init nft_synproxy_module_init(void)
    363{
    364	int err;
    365
    366	err = nft_register_obj(&nft_synproxy_obj_type);
    367	if (err < 0)
    368		return err;
    369
    370	err = nft_register_expr(&nft_synproxy_type);
    371	if (err < 0)
    372		goto err;
    373
    374	return 0;
    375
    376err:
    377	nft_unregister_obj(&nft_synproxy_obj_type);
    378	return err;
    379}
    380
    381static void __exit nft_synproxy_module_exit(void)
    382{
    383	nft_unregister_expr(&nft_synproxy_type);
    384	nft_unregister_obj(&nft_synproxy_obj_type);
    385}
    386
    387module_init(nft_synproxy_module_init);
    388module_exit(nft_synproxy_module_exit);
    389
    390MODULE_LICENSE("GPL");
    391MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>");
    392MODULE_ALIAS_NFT_EXPR("synproxy");
    393MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_SYNPROXY);
    394MODULE_DESCRIPTION("nftables SYNPROXY expression support");