Commit | Line | Data |
---|---|---|
33f11d16 TH |
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 | ||
16 | static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p) | |
17 | { | |
351596aa TH |
18 | struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); |
19 | ||
90bfe662 | 20 | if (p->locator_match.v64) |
33f11d16 TH |
21 | return p->csum_diff; |
22 | else | |
351596aa | 23 | return compute_csum_diff8((__be32 *)&iaddr->loc, |
33f11d16 TH |
24 | (__be32 *)&p->locator); |
25 | } | |
26 | ||
90bfe662 TH |
27 | static 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 { | |
0b797c85 TH |
37 | diff = compute_csum_diff8((__be32 *)&p->locator, |
38 | (__be32 *)iaddr); | |
90bfe662 TH |
39 | } |
40 | ||
41 | fval = (__force __wsum)(ila_csum_neutral_set(iaddr->ident) ? | |
0b797c85 | 42 | CSUM_NEUTRAL_FLAG : ~CSUM_NEUTRAL_FLAG); |
90bfe662 TH |
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 | ||
56 | static void ila_csum_adjust_transport(struct sk_buff *skb, | |
57 | struct ila_params *p) | |
33f11d16 TH |
58 | { |
59 | __wsum diff; | |
60 | struct ipv6hdr *ip6h = ipv6_hdr(skb); | |
351596aa | 61 | struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); |
33f11d16 TH |
62 | size_t nhoff = sizeof(struct ipv6hdr); |
63 | ||
33f11d16 TH |
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 */ | |
351596aa | 103 | iaddr->loc = p->locator; |
33f11d16 TH |
104 | } |
105 | ||
707a2ca4 TH |
106 | void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, |
107 | bool set_csum_neutral) | |
90bfe662 TH |
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 | */ | |
707a2ca4 TH |
118 | if (!set_csum_neutral) |
119 | ila_csum_do_neutral(iaddr, p); | |
90bfe662 TH |
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 | ||
137 | void 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( | |
0b797c85 TH |
143 | (__be32 *)&p->locator, |
144 | (__be32 *)&p->locator_match); | |
90bfe662 TH |
145 | } |
146 | ||
33f11d16 TH |
147 | static int __init ila_init(void) |
148 | { | |
149 | int ret; | |
150 | ||
151 | ret = ila_lwt_init(); | |
152 | ||
153 | if (ret) | |
154 | goto fail_lwt; | |
155 | ||
7f00feaf TH |
156 | ret = ila_xlat_init(); |
157 | if (ret) | |
158 | goto fail_xlat; | |
159 | ||
160 | return 0; | |
161 | fail_xlat: | |
162 | ila_lwt_fini(); | |
33f11d16 TH |
163 | fail_lwt: |
164 | return ret; | |
165 | } | |
166 | ||
167 | static void __exit ila_fini(void) | |
168 | { | |
7f00feaf | 169 | ila_xlat_fini(); |
33f11d16 TH |
170 | ila_lwt_fini(); |
171 | } | |
172 | ||
173 | module_init(ila_init); | |
174 | module_exit(ila_fini); | |
175 | MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>"); | |
176 | MODULE_LICENSE("GPL"); |