linux/net/ipv4/gre_demux.c
<<
>>
Prefs
   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");
 222