linux/net/ipv4/xfrm4_protocol.c
<<
>>
Prefs
   1/* xfrm4_protocol.c - Generic xfrm protocol multiplexer.
   2 *
   3 * Copyright (C) 2013 secunet Security Networks AG
   4 *
   5 * Author:
   6 * Steffen Klassert <steffen.klassert@secunet.com>
   7 *
   8 * Based on:
   9 * net/ipv4/tunnel4.c
  10 *
  11 *      This program is free software; you can redistribute it and/or
  12 *      modify it under the terms of the GNU General Public License
  13 *      as published by the Free Software Foundation; either version
  14 *      2 of the License, or (at your option) any later version.
  15 */
  16
  17#include <linux/init.h>
  18#include <linux/mutex.h>
  19#include <linux/skbuff.h>
  20#include <net/icmp.h>
  21#include <net/ip.h>
  22#include <net/protocol.h>
  23#include <net/xfrm.h>
  24
  25static struct xfrm4_protocol __rcu *esp4_handlers __read_mostly;
  26static struct xfrm4_protocol __rcu *ah4_handlers __read_mostly;
  27static struct xfrm4_protocol __rcu *ipcomp4_handlers __read_mostly;
  28static DEFINE_MUTEX(xfrm4_protocol_mutex);
  29
  30static inline struct xfrm4_protocol __rcu **proto_handlers(u8 protocol)
  31{
  32        switch (protocol) {
  33        case IPPROTO_ESP:
  34                return &esp4_handlers;
  35        case IPPROTO_AH:
  36                return &ah4_handlers;
  37        case IPPROTO_COMP:
  38                return &ipcomp4_handlers;
  39        }
  40
  41        return NULL;
  42}
  43
  44#define for_each_protocol_rcu(head, handler)            \
  45        for (handler = rcu_dereference(head);           \
  46             handler != NULL;                           \
  47             handler = rcu_dereference(handler->next))  \
  48
  49static int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
  50{
  51        int ret;
  52        struct xfrm4_protocol *handler;
  53        struct xfrm4_protocol __rcu **head = proto_handlers(protocol);
  54
  55        if (!head)
  56                return 0;
  57
  58        for_each_protocol_rcu(*head, handler)
  59                if ((ret = handler->cb_handler(skb, err)) <= 0)
  60                        return ret;
  61
  62        return 0;
  63}
  64
  65int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
  66                    int encap_type)
  67{
  68        int ret;
  69        struct xfrm4_protocol *handler;
  70        struct xfrm4_protocol __rcu **head = proto_handlers(nexthdr);
  71
  72        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
  73        XFRM_SPI_SKB_CB(skb)->family = AF_INET;
  74        XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
  75
  76        if (!head)
  77                goto out;
  78
  79        if (!skb_dst(skb)) {
  80                const struct iphdr *iph = ip_hdr(skb);
  81
  82                if (ip_route_input_noref(skb, iph->daddr, iph->saddr,
  83                                         iph->tos, skb->dev))
  84                        goto drop;
  85        }
  86
  87        for_each_protocol_rcu(*head, handler)
  88                if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL)
  89                        return ret;
  90
  91out:
  92        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
  93
  94drop:
  95        kfree_skb(skb);
  96        return 0;
  97}
  98EXPORT_SYMBOL(xfrm4_rcv_encap);
  99
 100static int xfrm4_esp_rcv(struct sk_buff *skb)
 101{
 102        int ret;
 103        struct xfrm4_protocol *handler;
 104
 105        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
 106
 107        for_each_protocol_rcu(esp4_handlers, handler)
 108                if ((ret = handler->handler(skb)) != -EINVAL)
 109                        return ret;
 110
 111        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 112
 113        kfree_skb(skb);
 114        return 0;
 115}
 116
 117static int xfrm4_esp_err(struct sk_buff *skb, u32 info)
 118{
 119        struct xfrm4_protocol *handler;
 120
 121        for_each_protocol_rcu(esp4_handlers, handler)
 122                if (!handler->err_handler(skb, info))
 123                        return 0;
 124
 125        return -ENOENT;
 126}
 127
 128static int xfrm4_ah_rcv(struct sk_buff *skb)
 129{
 130        int ret;
 131        struct xfrm4_protocol *handler;
 132
 133        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
 134
 135        for_each_protocol_rcu(ah4_handlers, handler)
 136                if ((ret = handler->handler(skb)) != -EINVAL)
 137                        return ret;
 138
 139        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 140
 141        kfree_skb(skb);
 142        return 0;
 143}
 144
 145static int xfrm4_ah_err(struct sk_buff *skb, u32 info)
 146{
 147        struct xfrm4_protocol *handler;
 148
 149        for_each_protocol_rcu(ah4_handlers, handler)
 150                if (!handler->err_handler(skb, info))
 151                        return 0;
 152
 153        return -ENOENT;
 154}
 155
 156static int xfrm4_ipcomp_rcv(struct sk_buff *skb)
 157{
 158        int ret;
 159        struct xfrm4_protocol *handler;
 160
 161        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
 162
 163        for_each_protocol_rcu(ipcomp4_handlers, handler)
 164                if ((ret = handler->handler(skb)) != -EINVAL)
 165                        return ret;
 166
 167        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 168
 169        kfree_skb(skb);
 170        return 0;
 171}
 172
 173static int xfrm4_ipcomp_err(struct sk_buff *skb, u32 info)
 174{
 175        struct xfrm4_protocol *handler;
 176
 177        for_each_protocol_rcu(ipcomp4_handlers, handler)
 178                if (!handler->err_handler(skb, info))
 179                        return 0;
 180
 181        return -ENOENT;
 182}
 183
 184static const struct net_protocol esp4_protocol = {
 185        .handler        =       xfrm4_esp_rcv,
 186        .err_handler    =       xfrm4_esp_err,
 187        .no_policy      =       1,
 188        .netns_ok       =       1,
 189};
 190
 191static const struct net_protocol ah4_protocol = {
 192        .handler        =       xfrm4_ah_rcv,
 193        .err_handler    =       xfrm4_ah_err,
 194        .no_policy      =       1,
 195        .netns_ok       =       1,
 196};
 197
 198static const struct net_protocol ipcomp4_protocol = {
 199        .handler        =       xfrm4_ipcomp_rcv,
 200        .err_handler    =       xfrm4_ipcomp_err,
 201        .no_policy      =       1,
 202        .netns_ok       =       1,
 203};
 204
 205static const struct xfrm_input_afinfo xfrm4_input_afinfo = {
 206        .family         =       AF_INET,
 207        .callback       =       xfrm4_rcv_cb,
 208};
 209
 210static inline const struct net_protocol *netproto(unsigned char protocol)
 211{
 212        switch (protocol) {
 213        case IPPROTO_ESP:
 214                return &esp4_protocol;
 215        case IPPROTO_AH:
 216                return &ah4_protocol;
 217        case IPPROTO_COMP:
 218                return &ipcomp4_protocol;
 219        }
 220
 221        return NULL;
 222}
 223
 224int xfrm4_protocol_register(struct xfrm4_protocol *handler,
 225                            unsigned char protocol)
 226{
 227        struct xfrm4_protocol __rcu **pprev;
 228        struct xfrm4_protocol *t;
 229        bool add_netproto = false;
 230        int ret = -EEXIST;
 231        int priority = handler->priority;
 232
 233        if (!proto_handlers(protocol) || !netproto(protocol))
 234                return -EINVAL;
 235
 236        mutex_lock(&xfrm4_protocol_mutex);
 237
 238        if (!rcu_dereference_protected(*proto_handlers(protocol),
 239                                       lockdep_is_held(&xfrm4_protocol_mutex)))
 240                add_netproto = true;
 241
 242        for (pprev = proto_handlers(protocol);
 243             (t = rcu_dereference_protected(*pprev,
 244                        lockdep_is_held(&xfrm4_protocol_mutex))) != NULL;
 245             pprev = &t->next) {
 246                if (t->priority < priority)
 247                        break;
 248                if (t->priority == priority)
 249                        goto err;
 250        }
 251
 252        handler->next = *pprev;
 253        rcu_assign_pointer(*pprev, handler);
 254
 255        ret = 0;
 256
 257err:
 258        mutex_unlock(&xfrm4_protocol_mutex);
 259
 260        if (add_netproto) {
 261                if (inet_add_protocol(netproto(protocol), protocol)) {
 262                        pr_err("%s: can't add protocol\n", __func__);
 263                        ret = -EAGAIN;
 264                }
 265        }
 266
 267        return ret;
 268}
 269EXPORT_SYMBOL(xfrm4_protocol_register);
 270
 271int xfrm4_protocol_deregister(struct xfrm4_protocol *handler,
 272                              unsigned char protocol)
 273{
 274        struct xfrm4_protocol __rcu **pprev;
 275        struct xfrm4_protocol *t;
 276        int ret = -ENOENT;
 277
 278        if (!proto_handlers(protocol) || !netproto(protocol))
 279                return -EINVAL;
 280
 281        mutex_lock(&xfrm4_protocol_mutex);
 282
 283        for (pprev = proto_handlers(protocol);
 284             (t = rcu_dereference_protected(*pprev,
 285                        lockdep_is_held(&xfrm4_protocol_mutex))) != NULL;
 286             pprev = &t->next) {
 287                if (t == handler) {
 288                        *pprev = handler->next;
 289                        ret = 0;
 290                        break;
 291                }
 292        }
 293
 294        if (!rcu_dereference_protected(*proto_handlers(protocol),
 295                                       lockdep_is_held(&xfrm4_protocol_mutex))) {
 296                if (inet_del_protocol(netproto(protocol), protocol) < 0) {
 297                        pr_err("%s: can't remove protocol\n", __func__);
 298                        ret = -EAGAIN;
 299                }
 300        }
 301
 302        mutex_unlock(&xfrm4_protocol_mutex);
 303
 304        synchronize_net();
 305
 306        return ret;
 307}
 308EXPORT_SYMBOL(xfrm4_protocol_deregister);
 309
 310void __init xfrm4_protocol_init(void)
 311{
 312        xfrm_input_register_afinfo(&xfrm4_input_afinfo);
 313}
 314EXPORT_SYMBOL(xfrm4_protocol_init);
 315