linux/net/ipv6/xfrm6_protocol.c
<<
>>
Prefs
   1/* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
   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/xfrm4_protocol.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 <linux/icmpv6.h>
  21#include <net/ipv6.h>
  22#include <net/protocol.h>
  23#include <net/xfrm.h>
  24
  25static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
  26static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
  27static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
  28static DEFINE_MUTEX(xfrm6_protocol_mutex);
  29
  30static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
  31{
  32        switch (protocol) {
  33        case IPPROTO_ESP:
  34                return &esp6_handlers;
  35        case IPPROTO_AH:
  36                return &ah6_handlers;
  37        case IPPROTO_COMP:
  38                return &ipcomp6_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
  49int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
  50{
  51        int ret;
  52        struct xfrm6_protocol *handler;
  53        struct xfrm6_protocol __rcu **head = proto_handlers(protocol);
  54
  55        if (!head)
  56                return 0;
  57
  58        for_each_protocol_rcu(*proto_handlers(protocol), handler)
  59                if ((ret = handler->cb_handler(skb, err)) <= 0)
  60                        return ret;
  61
  62        return 0;
  63}
  64EXPORT_SYMBOL(xfrm6_rcv_cb);
  65
  66static int xfrm6_esp_rcv(struct sk_buff *skb)
  67{
  68        int ret;
  69        struct xfrm6_protocol *handler;
  70
  71        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
  72
  73        for_each_protocol_rcu(esp6_handlers, handler)
  74                if ((ret = handler->handler(skb)) != -EINVAL)
  75                        return ret;
  76
  77        icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
  78
  79        kfree_skb(skb);
  80        return 0;
  81}
  82
  83static void xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
  84                          u8 type, u8 code, int offset, __be32 info)
  85{
  86        struct xfrm6_protocol *handler;
  87
  88        for_each_protocol_rcu(esp6_handlers, handler)
  89                if (!handler->err_handler(skb, opt, type, code, offset, info))
  90                        break;
  91}
  92
  93static int xfrm6_ah_rcv(struct sk_buff *skb)
  94{
  95        int ret;
  96        struct xfrm6_protocol *handler;
  97
  98        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
  99
 100        for_each_protocol_rcu(ah6_handlers, handler)
 101                if ((ret = handler->handler(skb)) != -EINVAL)
 102                        return ret;
 103
 104        icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
 105
 106        kfree_skb(skb);
 107        return 0;
 108}
 109
 110static void xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 111                         u8 type, u8 code, int offset, __be32 info)
 112{
 113        struct xfrm6_protocol *handler;
 114
 115        for_each_protocol_rcu(ah6_handlers, handler)
 116                if (!handler->err_handler(skb, opt, type, code, offset, info))
 117                        break;
 118}
 119
 120static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
 121{
 122        int ret;
 123        struct xfrm6_protocol *handler;
 124
 125        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
 126
 127        for_each_protocol_rcu(ipcomp6_handlers, handler)
 128                if ((ret = handler->handler(skb)) != -EINVAL)
 129                        return ret;
 130
 131        icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
 132
 133        kfree_skb(skb);
 134        return 0;
 135}
 136
 137static void xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 138                             u8 type, u8 code, int offset, __be32 info)
 139{
 140        struct xfrm6_protocol *handler;
 141
 142        for_each_protocol_rcu(ipcomp6_handlers, handler)
 143                if (!handler->err_handler(skb, opt, type, code, offset, info))
 144                        break;
 145}
 146
 147static const struct inet6_protocol esp6_protocol = {
 148        .handler        =       xfrm6_esp_rcv,
 149        .err_handler    =       xfrm6_esp_err,
 150        .flags          =       INET6_PROTO_NOPOLICY,
 151};
 152
 153static const struct inet6_protocol ah6_protocol = {
 154        .handler        =       xfrm6_ah_rcv,
 155        .err_handler    =       xfrm6_ah_err,
 156        .flags          =       INET6_PROTO_NOPOLICY,
 157};
 158
 159static const struct inet6_protocol ipcomp6_protocol = {
 160        .handler        =       xfrm6_ipcomp_rcv,
 161        .err_handler    =       xfrm6_ipcomp_err,
 162        .flags          =       INET6_PROTO_NOPOLICY,
 163};
 164
 165static struct xfrm_input_afinfo xfrm6_input_afinfo = {
 166        .family         =       AF_INET6,
 167        .owner          =       THIS_MODULE,
 168        .callback       =       xfrm6_rcv_cb,
 169};
 170
 171static inline const struct inet6_protocol *netproto(unsigned char protocol)
 172{
 173        switch (protocol) {
 174        case IPPROTO_ESP:
 175                return &esp6_protocol;
 176        case IPPROTO_AH:
 177                return &ah6_protocol;
 178        case IPPROTO_COMP:
 179                return &ipcomp6_protocol;
 180        }
 181
 182        return NULL;
 183}
 184
 185int xfrm6_protocol_register(struct xfrm6_protocol *handler,
 186                            unsigned char protocol)
 187{
 188        struct xfrm6_protocol __rcu **pprev;
 189        struct xfrm6_protocol *t;
 190        bool add_netproto = false;
 191        int ret = -EEXIST;
 192        int priority = handler->priority;
 193
 194        if (!proto_handlers(protocol) || !netproto(protocol))
 195                return -EINVAL;
 196
 197        mutex_lock(&xfrm6_protocol_mutex);
 198
 199        if (!rcu_dereference_protected(*proto_handlers(protocol),
 200                                       lockdep_is_held(&xfrm6_protocol_mutex)))
 201                add_netproto = true;
 202
 203        for (pprev = proto_handlers(protocol);
 204             (t = rcu_dereference_protected(*pprev,
 205                        lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
 206             pprev = &t->next) {
 207                if (t->priority < priority)
 208                        break;
 209                if (t->priority == priority)
 210                        goto err;
 211        }
 212
 213        handler->next = *pprev;
 214        rcu_assign_pointer(*pprev, handler);
 215
 216        ret = 0;
 217
 218err:
 219        mutex_unlock(&xfrm6_protocol_mutex);
 220
 221        if (add_netproto) {
 222                if (inet6_add_protocol(netproto(protocol), protocol)) {
 223                        pr_err("%s: can't add protocol\n", __func__);
 224                        ret = -EAGAIN;
 225                }
 226        }
 227
 228        return ret;
 229}
 230EXPORT_SYMBOL(xfrm6_protocol_register);
 231
 232int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
 233                              unsigned char protocol)
 234{
 235        struct xfrm6_protocol __rcu **pprev;
 236        struct xfrm6_protocol *t;
 237        int ret = -ENOENT;
 238
 239        if (!proto_handlers(protocol) || !netproto(protocol))
 240                return -EINVAL;
 241
 242        mutex_lock(&xfrm6_protocol_mutex);
 243
 244        for (pprev = proto_handlers(protocol);
 245             (t = rcu_dereference_protected(*pprev,
 246                        lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
 247             pprev = &t->next) {
 248                if (t == handler) {
 249                        *pprev = handler->next;
 250                        ret = 0;
 251                        break;
 252                }
 253        }
 254
 255        if (!rcu_dereference_protected(*proto_handlers(protocol),
 256                                       lockdep_is_held(&xfrm6_protocol_mutex))) {
 257                if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
 258                        pr_err("%s: can't remove protocol\n", __func__);
 259                        ret = -EAGAIN;
 260                }
 261        }
 262
 263        mutex_unlock(&xfrm6_protocol_mutex);
 264
 265        synchronize_net();
 266
 267        return ret;
 268}
 269EXPORT_SYMBOL(xfrm6_protocol_deregister);
 270
 271int __init xfrm6_protocol_init(void)
 272{
 273        return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
 274}
 275
 276void xfrm6_protocol_fini(void)
 277{
 278        xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
 279}
 280