1 #include <linux/errno.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>
10 #include <net/ip6_fib.h>
11 #include <net/lwtunnel.h>
12 #include <net/protocol.h>
13 #include <uapi/linux/ila.h>
16 void ila_init_saved_csum(struct ila_params
*p
)
18 if (!p
->locator_match
.v64
)
21 p
->csum_diff
= compute_csum_diff8(
22 (__be32
*)&p
->locator
,
23 (__be32
*)&p
->locator_match
);
26 static __wsum
get_csum_diff_iaddr(struct ila_addr
*iaddr
, struct ila_params
*p
)
28 if (p
->locator_match
.v64
)
31 return compute_csum_diff8((__be32
*)&p
->locator
,
32 (__be32
*)&iaddr
->loc
);
35 static __wsum
get_csum_diff(struct ipv6hdr
*ip6h
, struct ila_params
*p
)
37 return get_csum_diff_iaddr(ila_a2i(&ip6h
->daddr
), p
);
40 static void ila_csum_do_neutral_fmt(struct ila_addr
*iaddr
,
43 __sum16
*adjust
= (__force __sum16
*)&iaddr
->ident
.v16
[3];
46 diff
= get_csum_diff_iaddr(iaddr
, p
);
48 fval
= (__force __wsum
)(ila_csum_neutral_set(iaddr
->ident
) ?
49 CSUM_NEUTRAL_FLAG
: ~CSUM_NEUTRAL_FLAG
);
51 diff
= csum_add(diff
, fval
);
53 *adjust
= ~csum_fold(csum_add(diff
, csum_unfold(*adjust
)));
55 /* Flip the csum-neutral bit. Either we are doing a SIR->ILA
56 * translation with ILA_CSUM_NEUTRAL_MAP as the csum_method
57 * and the C-bit is not set, or we are doing an ILA-SIR
58 * tranlsation and the C-bit is set.
60 iaddr
->ident
.csum_neutral
^= 1;
63 static void ila_csum_do_neutral_nofmt(struct ila_addr
*iaddr
,
66 __sum16
*adjust
= (__force __sum16
*)&iaddr
->ident
.v16
[3];
69 diff
= get_csum_diff_iaddr(iaddr
, p
);
71 *adjust
= ~csum_fold(csum_add(diff
, csum_unfold(*adjust
)));
74 static void ila_csum_adjust_transport(struct sk_buff
*skb
,
77 size_t nhoff
= sizeof(struct ipv6hdr
);
78 struct ipv6hdr
*ip6h
= ipv6_hdr(skb
);
81 switch (ip6h
->nexthdr
) {
83 if (likely(pskb_may_pull(skb
, nhoff
+ sizeof(struct tcphdr
)))) {
84 struct tcphdr
*th
= (struct tcphdr
*)
85 (skb_network_header(skb
) + nhoff
);
87 diff
= get_csum_diff(ip6h
, p
);
88 inet_proto_csum_replace_by_diff(&th
->check
, skb
,
93 if (likely(pskb_may_pull(skb
, nhoff
+ sizeof(struct udphdr
)))) {
94 struct udphdr
*uh
= (struct udphdr
*)
95 (skb_network_header(skb
) + nhoff
);
97 if (uh
->check
|| skb
->ip_summed
== CHECKSUM_PARTIAL
) {
98 diff
= get_csum_diff(ip6h
, p
);
99 inet_proto_csum_replace_by_diff(&uh
->check
, skb
,
102 uh
->check
= CSUM_MANGLED_0
;
107 if (likely(pskb_may_pull(skb
,
108 nhoff
+ sizeof(struct icmp6hdr
)))) {
109 struct icmp6hdr
*ih
= (struct icmp6hdr
*)
110 (skb_network_header(skb
) + nhoff
);
112 diff
= get_csum_diff(ip6h
, p
);
113 inet_proto_csum_replace_by_diff(&ih
->icmp6_cksum
, skb
,
120 void ila_update_ipv6_locator(struct sk_buff
*skb
, struct ila_params
*p
,
123 struct ipv6hdr
*ip6h
= ipv6_hdr(skb
);
124 struct ila_addr
*iaddr
= ila_a2i(&ip6h
->daddr
);
126 switch (p
->csum_mode
) {
127 case ILA_CSUM_ADJUST_TRANSPORT
:
128 ila_csum_adjust_transport(skb
, p
);
130 case ILA_CSUM_NEUTRAL_MAP
:
132 if (WARN_ON(ila_csum_neutral_set(iaddr
->ident
))) {
133 /* Checksum flag should never be
134 * set in a formatted SIR address.
138 } else if (!ila_csum_neutral_set(iaddr
->ident
)) {
139 /* ILA to SIR translation and C-bit isn't
144 ila_csum_do_neutral_fmt(iaddr
, p
);
146 case ILA_CSUM_NEUTRAL_MAP_AUTO
:
147 ila_csum_do_neutral_nofmt(iaddr
, p
);
149 case ILA_CSUM_NO_ACTION
:
153 /* Now change destination address */
154 iaddr
->loc
= p
->locator
;
157 static int __init
ila_init(void)
161 ret
= ila_lwt_init();
166 ret
= ila_xlat_init();
177 static void __exit
ila_fini(void)
183 module_init(ila_init
);
184 module_exit(ila_fini
);
185 MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
186 MODULE_LICENSE("GPL");