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

xt_dccp.c (4401B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * iptables module for DCCP protocol header matching
      4 *
      5 * (C) 2005 by Harald Welte <laforge@netfilter.org>
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/skbuff.h>
     10#include <linux/slab.h>
     11#include <linux/spinlock.h>
     12#include <net/ip.h>
     13#include <linux/dccp.h>
     14
     15#include <linux/netfilter/x_tables.h>
     16#include <linux/netfilter/xt_dccp.h>
     17
     18#include <linux/netfilter_ipv4/ip_tables.h>
     19#include <linux/netfilter_ipv6/ip6_tables.h>
     20
     21MODULE_LICENSE("GPL");
     22MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
     23MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
     24MODULE_ALIAS("ipt_dccp");
     25MODULE_ALIAS("ip6t_dccp");
     26
     27#define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
     28				  || (!!((invflag) & (option)) ^ (cond)))
     29
     30static unsigned char *dccp_optbuf;
     31static DEFINE_SPINLOCK(dccp_buflock);
     32
     33static inline bool
     34dccp_find_option(u_int8_t option,
     35		 const struct sk_buff *skb,
     36		 unsigned int protoff,
     37		 const struct dccp_hdr *dh,
     38		 bool *hotdrop)
     39{
     40	/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
     41	const unsigned char *op;
     42	unsigned int optoff = __dccp_hdr_len(dh);
     43	unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
     44	unsigned int i;
     45
     46	if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
     47		goto invalid;
     48
     49	if (!optlen)
     50		return false;
     51
     52	spin_lock_bh(&dccp_buflock);
     53	op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
     54	if (op == NULL) {
     55		/* If we don't have the whole header, drop packet. */
     56		goto partial;
     57	}
     58
     59	for (i = 0; i < optlen; ) {
     60		if (op[i] == option) {
     61			spin_unlock_bh(&dccp_buflock);
     62			return true;
     63		}
     64
     65		if (op[i] < 2)
     66			i++;
     67		else
     68			i += op[i+1]?:1;
     69	}
     70
     71	spin_unlock_bh(&dccp_buflock);
     72	return false;
     73
     74partial:
     75	spin_unlock_bh(&dccp_buflock);
     76invalid:
     77	*hotdrop = true;
     78	return false;
     79}
     80
     81
     82static inline bool
     83match_types(const struct dccp_hdr *dh, u_int16_t typemask)
     84{
     85	return typemask & (1 << dh->dccph_type);
     86}
     87
     88static inline bool
     89match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
     90	     const struct dccp_hdr *dh, bool *hotdrop)
     91{
     92	return dccp_find_option(option, skb, protoff, dh, hotdrop);
     93}
     94
     95static bool
     96dccp_mt(const struct sk_buff *skb, struct xt_action_param *par)
     97{
     98	const struct xt_dccp_info *info = par->matchinfo;
     99	const struct dccp_hdr *dh;
    100	struct dccp_hdr _dh;
    101
    102	if (par->fragoff != 0)
    103		return false;
    104
    105	dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh);
    106	if (dh == NULL) {
    107		par->hotdrop = true;
    108		return false;
    109	}
    110
    111	return  DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
    112			&& ntohs(dh->dccph_sport) <= info->spts[1],
    113			XT_DCCP_SRC_PORTS, info->flags, info->invflags)
    114		&& DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
    115			&& ntohs(dh->dccph_dport) <= info->dpts[1],
    116			XT_DCCP_DEST_PORTS, info->flags, info->invflags)
    117		&& DCCHECK(match_types(dh, info->typemask),
    118			   XT_DCCP_TYPE, info->flags, info->invflags)
    119		&& DCCHECK(match_option(info->option, skb, par->thoff, dh,
    120					&par->hotdrop),
    121			   XT_DCCP_OPTION, info->flags, info->invflags);
    122}
    123
    124static int dccp_mt_check(const struct xt_mtchk_param *par)
    125{
    126	const struct xt_dccp_info *info = par->matchinfo;
    127
    128	if (info->flags & ~XT_DCCP_VALID_FLAGS)
    129		return -EINVAL;
    130	if (info->invflags & ~XT_DCCP_VALID_FLAGS)
    131		return -EINVAL;
    132	if (info->invflags & ~info->flags)
    133		return -EINVAL;
    134	return 0;
    135}
    136
    137static struct xt_match dccp_mt_reg[] __read_mostly = {
    138	{
    139		.name 		= "dccp",
    140		.family		= NFPROTO_IPV4,
    141		.checkentry	= dccp_mt_check,
    142		.match		= dccp_mt,
    143		.matchsize	= sizeof(struct xt_dccp_info),
    144		.proto		= IPPROTO_DCCP,
    145		.me 		= THIS_MODULE,
    146	},
    147	{
    148		.name 		= "dccp",
    149		.family		= NFPROTO_IPV6,
    150		.checkentry	= dccp_mt_check,
    151		.match		= dccp_mt,
    152		.matchsize	= sizeof(struct xt_dccp_info),
    153		.proto		= IPPROTO_DCCP,
    154		.me 		= THIS_MODULE,
    155	},
    156};
    157
    158static int __init dccp_mt_init(void)
    159{
    160	int ret;
    161
    162	/* doff is 8 bits, so the maximum option size is (4*256).  Don't put
    163	 * this in BSS since DaveM is worried about locked TLB's for kernel
    164	 * BSS. */
    165	dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
    166	if (!dccp_optbuf)
    167		return -ENOMEM;
    168	ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
    169	if (ret)
    170		goto out_kfree;
    171	return ret;
    172
    173out_kfree:
    174	kfree(dccp_optbuf);
    175	return ret;
    176}
    177
    178static void __exit dccp_mt_exit(void)
    179{
    180	xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
    181	kfree(dccp_optbuf);
    182}
    183
    184module_init(dccp_mt_init);
    185module_exit(dccp_mt_exit);