linux/net/ipv6/xfrm6_protocol.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
   3 *
   4 * Copyright (C) 2013 secunet Security Networks AG
   5 *
   6 * Author:
   7 * Steffen Klassert <steffen.klassert@secunet.com>
   8 *
   9 * Based on:
  10 * net/ipv4/xfrm4_protocol.c
  11 */
  12
  13#include <linux/init.h>
  14#include <linux/mutex.h>
  15#include <linux/skbuff.h>
  16#include <linux/icmpv6.h>
  17#include <net/ipv6.h>
  18#include <net/protocol.h>
  19#include <net/xfrm.h>
  20
  21static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
  22static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
  23static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
  24static DEFINE_MUTEX(xfrm6_protocol_mutex);
  25
  26static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
  27{
  28        switch (protocol) {
  29        case IPPROTO_ESP:
  30                return &esp6_handlers;
  31        case IPPROTO_AH:
  32                return &ah6_handlers;
  33        case IPPROTO_COMP:
  34                return &ipcomp6_handlers;
  35        }
  36
  37        return NULL;
  38}
  39
  40#define for_each_protocol_rcu(head, handler)            \
  41        for (handler = rcu_dereference(head);           \
  42             handler != NULL;                           \
  43             handler = rcu_dereference(handler->next))  \
  44
  45static int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
  46{
  47        int ret;
  48        struct xfrm6_protocol *handler;
  49        struct xfrm6_protocol __rcu **head = proto_handlers(protocol);
  50
  51        if (!head)
  52                return 0;
  53
  54        for_each_protocol_rcu(*proto_handlers(protocol), handler)
  55                if ((ret = handler->cb_handler(skb, err)) <= 0)
  56                        return ret;
  57
  58        return 0;
  59}
  60
  61static int xfrm6_esp_rcv(struct sk_buff *skb)
  62{
  63        int ret;
  64        struct xfrm6_protocol *handler;
  65
  66        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
  67
  68        for_each_protocol_rcu(esp6_handlers, handler)
  69                if ((ret = handler->handler(skb)) != -EINVAL)
  70                        return ret;
  71
  72        icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
  73
  74        kfree_skb(skb);
  75        return 0;
  76}
  77
  78static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
  79                          u8 type, u8 code, int offset, __be32 info)
  80{
  81        struct xfrm6_protocol *handler;
  82
  83        for_each_protocol_rcu(esp6_handlers, handler)
  84                if (!handler->err_handler(skb, opt, type, code, offset, info))
  85                        return 0;
  86
  87        return -ENOENT;
  88}
  89
  90static int xfrm6_ah_rcv(struct sk_buff *skb)
  91{
  92        int ret;
  93        struct xfrm6_protocol *handler;
  94
  95        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
  96
  97        for_each_protocol_rcu(ah6_handlers, handler)
  98                if ((ret = handler->handler(skb)) != -EINVAL)
  99                        return ret;
 100
 101        icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
 102
 103        kfree_skb(skb);
 104        return 0;
 105}
 106
 107static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 108                         u8 type, u8 code, int offset, __be32 info)
 109{
 110        struct xfrm6_protocol *handler;
 111
 112        for_each_protocol_rcu(ah6_handlers, handler)
 113                if (!handler->err_handler(skb, opt, type, code, offset, info))
 114                        return 0;
 115
 116        return -ENOENT;
 117}
 118
 119static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
 120{
 121        int ret;
 122        struct xfrm6_protocol *handler;
 123
 124        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
 125
 126        for_each_protocol_rcu(ipcomp6_handlers, handler)
 127                if ((ret = handler->handler(skb)) != -EINVAL)
 128                        return ret;
 129
 130        icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
 131
 132        kfree_skb(skb);
 133        return 0;
 134}
 135
 136static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 137                             u8 type, u8 code, int offset, __be32 info)
 138{
 139        struct xfrm6_protocol *handler;
 140
 141        for_each_protocol_rcu(ipcomp6_handlers, handler)
 142                if (!handler->err_handler(skb, opt, type, code, offset, info))
 143                        return 0;
 144
 145        return -ENOENT;
 146}
 147
 148static const struct inet6_protocol esp6_protocol = {
 149        .handler        =       xfrm6_esp_rcv,
 150        .err_handler    =       xfrm6_esp_err,
 151        .flags          =       INET6_PROTO_NOPOLICY,
 152};
 153
 154static const struct inet6_protocol ah6_protocol = {
 155        .handler        =       xfrm6_ah_rcv,
 156        .err_handler    =       xfrm6_ah_err,
 157        .flags          =       INET6_PROTO_NOPOLICY,
 158};
 159
 160static const struct inet6_protocol ipcomp6_protocol = {
 161        .handler        =       xfrm6_ipcomp_rcv,
 162        .err_handler    =       xfrm6_ipcomp_err,
 163        .flags          =       INET6_PROTO_NOPOLICY,
 164};
 165
 166static const struct xfrm_input_afinfo xfrm6_input_afinfo = {
 167        .family         =       AF_INET6,
 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