root/net/ipv4/gre_demux.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gre_add_protocol
  2. gre_del_protocol
  3. gre_parse_header
  4. gre_rcv
  5. gre_err
  6. gre_init
  7. gre_exit

   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 
  29 static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
  30 
  31 int 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 }
  39 EXPORT_SYMBOL_GPL(gre_add_protocol);
  40 
  41 int 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 }
  57 EXPORT_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  */
  62 int 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) ||
 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 }
 144 EXPORT_SYMBOL(gre_parse_header);
 145 
 146 static 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 
 167 drop_unlock:
 168         rcu_read_unlock();
 169 drop:
 170         kfree_skb(skb);
 171         return NET_RX_DROP;
 172 }
 173 
 174 static 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 
 195 static const struct net_protocol net_gre_protocol = {
 196         .handler     = gre_rcv,
 197         .err_handler = gre_err,
 198         .netns_ok    = 1,
 199 };
 200 
 201 static int __init gre_init(void)
 202 {
 203         pr_info("GRE over IPv4 demultiplexor driver\n");
 204 
 205         if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
 206                 pr_err("can't add protocol\n");
 207                 return -EAGAIN;
 208         }
 209         return 0;
 210 }
 211 
 212 static void __exit gre_exit(void)
 213 {
 214         inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
 215 }
 216 
 217 module_init(gre_init);
 218 module_exit(gre_exit);
 219 
 220 MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
 221 MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
 222 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */