linux/net/ipv6/ila/ila_lwt.c
<<
>>
Prefs
   1#include <linux/errno.h>
   2#include <linux/ip.h>
   3#include <linux/kernel.h>
   4#include <linux/module.h>
   5#include <linux/skbuff.h>
   6#include <linux/socket.h>
   7#include <linux/types.h>
   8#include <net/checksum.h>
   9#include <net/ip.h>
  10#include <net/ip6_fib.h>
  11#include <net/lwtunnel.h>
  12#include <net/protocol.h>
  13#include <uapi/linux/ila.h>
  14#include "ila.h"
  15
  16static inline struct ila_params *ila_params_lwtunnel(
  17        struct lwtunnel_state *lwstate)
  18{
  19        return (struct ila_params *)lwstate->data;
  20}
  21
  22static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
  23{
  24        struct dst_entry *dst = skb_dst(skb);
  25
  26        if (skb->protocol != htons(ETH_P_IPV6))
  27                goto drop;
  28
  29        ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate), true);
  30
  31        return dst->lwtstate->orig_output(net, sk, skb);
  32
  33drop:
  34        kfree_skb(skb);
  35        return -EINVAL;
  36}
  37
  38static int ila_input(struct sk_buff *skb)
  39{
  40        struct dst_entry *dst = skb_dst(skb);
  41
  42        if (skb->protocol != htons(ETH_P_IPV6))
  43                goto drop;
  44
  45        ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate), false);
  46
  47        return dst->lwtstate->orig_input(skb);
  48
  49drop:
  50        kfree_skb(skb);
  51        return -EINVAL;
  52}
  53
  54static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
  55        [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
  56        [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, },
  57};
  58
  59static int ila_build_state(struct net_device *dev, struct nlattr *nla,
  60                           unsigned int family, const void *cfg,
  61                           struct lwtunnel_state **ts)
  62{
  63        struct ila_params *p;
  64        struct nlattr *tb[ILA_ATTR_MAX + 1];
  65        size_t encap_len = sizeof(*p);
  66        struct lwtunnel_state *newts;
  67        const struct fib6_config *cfg6 = cfg;
  68        struct ila_addr *iaddr;
  69        int ret;
  70
  71        if (family != AF_INET6)
  72                return -EINVAL;
  73
  74        if (cfg6->fc_dst_len < sizeof(struct ila_locator) + 1) {
  75                /* Need to have full locator and at least type field
  76                 * included in destination
  77                 */
  78                return -EINVAL;
  79        }
  80
  81        iaddr = (struct ila_addr *)&cfg6->fc_dst;
  82
  83        if (!ila_addr_is_ila(iaddr) || ila_csum_neutral_set(iaddr->ident)) {
  84                /* Don't allow translation for a non-ILA address or checksum
  85                 * neutral flag to be set.
  86                 */
  87                return -EINVAL;
  88        }
  89
  90        ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla,
  91                               ila_nl_policy);
  92        if (ret < 0)
  93                return ret;
  94
  95        if (!tb[ILA_ATTR_LOCATOR])
  96                return -EINVAL;
  97
  98        newts = lwtunnel_state_alloc(encap_len);
  99        if (!newts)
 100                return -ENOMEM;
 101
 102        newts->len = encap_len;
 103        p = ila_params_lwtunnel(newts);
 104
 105        p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
 106
 107        /* Precompute checksum difference for translation since we
 108         * know both the old locator and the new one.
 109         */
 110        p->locator_match = iaddr->loc;
 111        p->csum_diff = compute_csum_diff8(
 112                (__be32 *)&p->locator_match, (__be32 *)&p->locator);
 113
 114        if (tb[ILA_ATTR_CSUM_MODE])
 115                p->csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]);
 116
 117        ila_init_saved_csum(p);
 118
 119        newts->type = LWTUNNEL_ENCAP_ILA;
 120        newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
 121                        LWTUNNEL_STATE_INPUT_REDIRECT;
 122
 123        *ts = newts;
 124
 125        return 0;
 126}
 127
 128static int ila_fill_encap_info(struct sk_buff *skb,
 129                               struct lwtunnel_state *lwtstate)
 130{
 131        struct ila_params *p = ila_params_lwtunnel(lwtstate);
 132
 133        if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator.v64,
 134                              ILA_ATTR_PAD))
 135                goto nla_put_failure;
 136        if (nla_put_u8(skb, ILA_ATTR_CSUM_MODE, (__force u8)p->csum_mode))
 137                goto nla_put_failure;
 138
 139        return 0;
 140
 141nla_put_failure:
 142        return -EMSGSIZE;
 143}
 144
 145static int ila_encap_nlsize(struct lwtunnel_state *lwtstate)
 146{
 147        return nla_total_size_64bit(sizeof(u64)) + /* ILA_ATTR_LOCATOR */
 148               nla_total_size(sizeof(u8)) +        /* ILA_ATTR_CSUM_MODE */
 149               0;
 150}
 151
 152static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
 153{
 154        struct ila_params *a_p = ila_params_lwtunnel(a);
 155        struct ila_params *b_p = ila_params_lwtunnel(b);
 156
 157        return (a_p->locator.v64 != b_p->locator.v64);
 158}
 159
 160static const struct lwtunnel_encap_ops ila_encap_ops = {
 161        .build_state = ila_build_state,
 162        .output = ila_output,
 163        .input = ila_input,
 164        .fill_encap = ila_fill_encap_info,
 165        .get_encap_size = ila_encap_nlsize,
 166        .cmp_encap = ila_encap_cmp,
 167};
 168
 169int ila_lwt_init(void)
 170{
 171        return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
 172}
 173
 174void ila_lwt_fini(void)
 175{
 176        lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
 177}
 178