linux/net/ipv6/ila/ila_common.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 __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
  17{
  18        struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
  19
  20        if (p->locator_match.v64)
  21                return p->csum_diff;
  22        else
  23                return compute_csum_diff8((__be32 *)&iaddr->loc,
  24                                          (__be32 *)&p->locator);
  25}
  26
  27static void ila_csum_do_neutral(struct ila_addr *iaddr,
  28                                struct ila_params *p)
  29{
  30        __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
  31        __wsum diff, fval;
  32
  33        /* Check if checksum adjust value has been cached */
  34        if (p->locator_match.v64) {
  35                diff = p->csum_diff;
  36        } else {
  37                diff = compute_csum_diff8((__be32 *)&p->locator,
  38                                          (__be32 *)iaddr);
  39        }
  40
  41        fval = (__force __wsum)(ila_csum_neutral_set(iaddr->ident) ?
  42                        CSUM_NEUTRAL_FLAG : ~CSUM_NEUTRAL_FLAG);
  43
  44        diff = csum_add(diff, fval);
  45
  46        *adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust)));
  47
  48        /* Flip the csum-neutral bit. Either we are doing a SIR->ILA
  49         * translation with ILA_CSUM_NEUTRAL_MAP as the csum_method
  50         * and the C-bit is not set, or we are doing an ILA-SIR
  51         * tranlsation and the C-bit is set.
  52         */
  53        iaddr->ident.csum_neutral ^= 1;
  54}
  55
  56static void ila_csum_adjust_transport(struct sk_buff *skb,
  57                                      struct ila_params *p)
  58{
  59        __wsum diff;
  60        struct ipv6hdr *ip6h = ipv6_hdr(skb);
  61        struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
  62        size_t nhoff = sizeof(struct ipv6hdr);
  63
  64        switch (ip6h->nexthdr) {
  65        case NEXTHDR_TCP:
  66                if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
  67                        struct tcphdr *th = (struct tcphdr *)
  68                                        (skb_network_header(skb) + nhoff);
  69
  70                        diff = get_csum_diff(ip6h, p);
  71                        inet_proto_csum_replace_by_diff(&th->check, skb,
  72                                                        diff, true);
  73                }
  74                break;
  75        case NEXTHDR_UDP:
  76                if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
  77                        struct udphdr *uh = (struct udphdr *)
  78                                        (skb_network_header(skb) + nhoff);
  79
  80                        if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
  81                                diff = get_csum_diff(ip6h, p);
  82                                inet_proto_csum_replace_by_diff(&uh->check, skb,
  83                                                                diff, true);
  84                                if (!uh->check)
  85                                        uh->check = CSUM_MANGLED_0;
  86                        }
  87                }
  88                break;
  89        case NEXTHDR_ICMP:
  90                if (likely(pskb_may_pull(skb,
  91                                         nhoff + sizeof(struct icmp6hdr)))) {
  92                        struct icmp6hdr *ih = (struct icmp6hdr *)
  93                                        (skb_network_header(skb) + nhoff);
  94
  95                        diff = get_csum_diff(ip6h, p);
  96                        inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
  97                                                        diff, true);
  98                }
  99                break;
 100        }
 101
 102        /* Now change destination address */
 103        iaddr->loc = p->locator;
 104}
 105
 106void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p,
 107                             bool set_csum_neutral)
 108{
 109        struct ipv6hdr *ip6h = ipv6_hdr(skb);
 110        struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
 111
 112        /* First deal with the transport checksum */
 113        if (ila_csum_neutral_set(iaddr->ident)) {
 114                /* C-bit is set in the locator indicating that this
 115                 * is a locator being translated to a SIR address.
 116                 * Perform (receiver) checksum-neutral translation.
 117                 */
 118                if (!set_csum_neutral)
 119                        ila_csum_do_neutral(iaddr, p);
 120        } else {
 121                switch (p->csum_mode) {
 122                case ILA_CSUM_ADJUST_TRANSPORT:
 123                        ila_csum_adjust_transport(skb, p);
 124                        break;
 125                case ILA_CSUM_NEUTRAL_MAP:
 126                        ila_csum_do_neutral(iaddr, p);
 127                        break;
 128                case ILA_CSUM_NO_ACTION:
 129                        break;
 130                }
 131        }
 132
 133        /* Now change destination address */
 134        iaddr->loc = p->locator;
 135}
 136
 137void ila_init_saved_csum(struct ila_params *p)
 138{
 139        if (!p->locator_match.v64)
 140                return;
 141
 142        p->csum_diff = compute_csum_diff8(
 143                                (__be32 *)&p->locator,
 144                                (__be32 *)&p->locator_match);
 145}
 146
 147static int __init ila_init(void)
 148{
 149        int ret;
 150
 151        ret = ila_lwt_init();
 152
 153        if (ret)
 154                goto fail_lwt;
 155
 156        ret = ila_xlat_init();
 157        if (ret)
 158                goto fail_xlat;
 159
 160        return 0;
 161fail_xlat:
 162        ila_lwt_fini();
 163fail_lwt:
 164        return ret;
 165}
 166
 167static void __exit ila_fini(void)
 168{
 169        ila_xlat_fini();
 170        ila_lwt_fini();
 171}
 172
 173module_init(ila_init);
 174module_exit(ila_fini);
 175MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
 176MODULE_LICENSE("GPL");
 177