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

gre_demux.c (4960B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	GRE over IPv4 demultiplexer driver
      4 *
      5 *	Authors: Dmitry Kozlov (xeb@mail.ru)
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/module.h>
     11#include <linux/if.h>
     12#include <linux/icmp.h>
     13#include <linux/kernel.h>
     14#include <linux/kmod.h>
     15#include <linux/skbuff.h>
     16#include <linux/in.h>
     17#include <linux/ip.h>
     18#include <linux/netdevice.h>
     19#include <linux/if_tunnel.h>
     20#include <linux/spinlock.h>
     21#include <net/protocol.h>
     22#include <net/gre.h>
     23#include <net/erspan.h>
     24
     25#include <net/icmp.h>
     26#include <net/route.h>
     27#include <net/xfrm.h>
     28
     29static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
     30
     31int gre_add_protocol(const struct gre_protocol *proto, u8 version)
     32{
     33	if (version >= GREPROTO_MAX)
     34		return -EINVAL;
     35
     36	return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
     37		0 : -EBUSY;
     38}
     39EXPORT_SYMBOL_GPL(gre_add_protocol);
     40
     41int gre_del_protocol(const struct gre_protocol *proto, u8 version)
     42{
     43	int ret;
     44
     45	if (version >= GREPROTO_MAX)
     46		return -EINVAL;
     47
     48	ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
     49		0 : -EBUSY;
     50
     51	if (ret)
     52		return ret;
     53
     54	synchronize_rcu();
     55	return 0;
     56}
     57EXPORT_SYMBOL_GPL(gre_del_protocol);
     58
     59/* Fills in tpi and returns header length to be pulled.
     60 * Note that caller must use pskb_may_pull() before pulling GRE header.
     61 */
     62int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
     63		     bool *csum_err, __be16 proto, int nhs)
     64{
     65	const struct gre_base_hdr *greh;
     66	__be32 *options;
     67	int hdr_len;
     68
     69	if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr))))
     70		return -EINVAL;
     71
     72	greh = (struct gre_base_hdr *)(skb->data + nhs);
     73	if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
     74		return -EINVAL;
     75
     76	tpi->flags = gre_flags_to_tnl_flags(greh->flags);
     77	hdr_len = gre_calc_hlen(tpi->flags);
     78
     79	if (!pskb_may_pull(skb, nhs + hdr_len))
     80		return -EINVAL;
     81
     82	greh = (struct gre_base_hdr *)(skb->data + nhs);
     83	tpi->proto = greh->protocol;
     84
     85	options = (__be32 *)(greh + 1);
     86	if (greh->flags & GRE_CSUM) {
     87		if (!skb_checksum_simple_validate(skb)) {
     88			skb_checksum_try_convert(skb, IPPROTO_GRE,
     89						 null_compute_pseudo);
     90		} else if (csum_err) {
     91			*csum_err = true;
     92			return -EINVAL;
     93		}
     94
     95		options++;
     96	}
     97
     98	if (greh->flags & GRE_KEY) {
     99		tpi->key = *options;
    100		options++;
    101	} else {
    102		tpi->key = 0;
    103	}
    104	if (unlikely(greh->flags & GRE_SEQ)) {
    105		tpi->seq = *options;
    106		options++;
    107	} else {
    108		tpi->seq = 0;
    109	}
    110	/* WCCP version 1 and 2 protocol decoding.
    111	 * - Change protocol to IPv4/IPv6
    112	 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
    113	 */
    114	if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
    115		u8 _val, *val;
    116
    117		val = skb_header_pointer(skb, nhs + hdr_len,
    118					 sizeof(_val), &_val);
    119		if (!val)
    120			return -EINVAL;
    121		tpi->proto = proto;
    122		if ((*val & 0xF0) != 0x40)
    123			hdr_len += 4;
    124	}
    125	tpi->hdr_len = hdr_len;
    126
    127	/* ERSPAN ver 1 and 2 protocol sets GRE key field
    128	 * to 0 and sets the configured key in the
    129	 * inner erspan header field
    130	 */
    131	if ((greh->protocol == htons(ETH_P_ERSPAN) && hdr_len != 4) ||
    132	    greh->protocol == htons(ETH_P_ERSPAN2)) {
    133		struct erspan_base_hdr *ershdr;
    134
    135		if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr)))
    136			return -EINVAL;
    137
    138		ershdr = (struct erspan_base_hdr *)(skb->data + nhs + hdr_len);
    139		tpi->key = cpu_to_be32(get_session_id(ershdr));
    140	}
    141
    142	return hdr_len;
    143}
    144EXPORT_SYMBOL(gre_parse_header);
    145
    146static int gre_rcv(struct sk_buff *skb)
    147{
    148	const struct gre_protocol *proto;
    149	u8 ver;
    150	int ret;
    151
    152	if (!pskb_may_pull(skb, 12))
    153		goto drop;
    154
    155	ver = skb->data[1]&0x7f;
    156	if (ver >= GREPROTO_MAX)
    157		goto drop;
    158
    159	rcu_read_lock();
    160	proto = rcu_dereference(gre_proto[ver]);
    161	if (!proto || !proto->handler)
    162		goto drop_unlock;
    163	ret = proto->handler(skb);
    164	rcu_read_unlock();
    165	return ret;
    166
    167drop_unlock:
    168	rcu_read_unlock();
    169drop:
    170	kfree_skb(skb);
    171	return NET_RX_DROP;
    172}
    173
    174static int gre_err(struct sk_buff *skb, u32 info)
    175{
    176	const struct gre_protocol *proto;
    177	const struct iphdr *iph = (const struct iphdr *)skb->data;
    178	u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
    179	int err = 0;
    180
    181	if (ver >= GREPROTO_MAX)
    182		return -EINVAL;
    183
    184	rcu_read_lock();
    185	proto = rcu_dereference(gre_proto[ver]);
    186	if (proto && proto->err_handler)
    187		proto->err_handler(skb, info);
    188	else
    189		err = -EPROTONOSUPPORT;
    190	rcu_read_unlock();
    191
    192	return err;
    193}
    194
    195static const struct net_protocol net_gre_protocol = {
    196	.handler     = gre_rcv,
    197	.err_handler = gre_err,
    198};
    199
    200static int __init gre_init(void)
    201{
    202	pr_info("GRE over IPv4 demultiplexor driver\n");
    203
    204	if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
    205		pr_err("can't add protocol\n");
    206		return -EAGAIN;
    207	}
    208	return 0;
    209}
    210
    211static void __exit gre_exit(void)
    212{
    213	inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
    214}
    215
    216module_init(gre_init);
    217module_exit(gre_exit);
    218
    219MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
    220MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
    221MODULE_LICENSE("GPL");