diff options
author | Tom Herbert <tom@herbertland.com> | 2015-12-15 18:41:35 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-12-15 23:25:20 -0500 |
commit | 33f11d16142b06588eedfc1dd8cf93790979a712 (patch) | |
tree | bdcd45dcbb8d4c951e26ecd9926ac9f13cef061a /net/ipv6/ila | |
parent | 3026043dc4035a6f6dff68065c9df245ead06344 (diff) |
ila: Create net/ipv6/ila directory
Create ila directory in preparation for supporting other hooks in the
kernel than LWT for doing ILA. This includes:
- Moving ila.c to ila/ila_lwt.c
- Splitting out some common functions into ila_common.c
Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/ila')
-rw-r--r-- | net/ipv6/ila/Makefile | 7 | ||||
-rw-r--r-- | net/ipv6/ila/ila.h | 46 | ||||
-rw-r--r-- | net/ipv6/ila/ila_common.c | 95 | ||||
-rw-r--r-- | net/ipv6/ila/ila_lwt.c | 152 |
4 files changed, 300 insertions, 0 deletions
diff --git a/net/ipv6/ila/Makefile b/net/ipv6/ila/Makefile new file mode 100644 index 000000000000..31d136be2f21 --- /dev/null +++ b/net/ipv6/ila/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for ILA module | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_IPV6_ILA) += ila.o | ||
6 | |||
7 | ila-objs := ila_common.o ila_lwt.o | ||
diff --git a/net/ipv6/ila/ila.h b/net/ipv6/ila/ila.h new file mode 100644 index 000000000000..b94081ff2f8a --- /dev/null +++ b/net/ipv6/ila/ila.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015 Tom Herbert <tom@herbertland.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation; either version 2 of | ||
7 | * the License, or (at your option) any later version. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #ifndef __ILA_H | ||
12 | #define __ILA_H | ||
13 | |||
14 | #include <linux/errno.h> | ||
15 | #include <linux/ip.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/socket.h> | ||
19 | #include <linux/skbuff.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <net/checksum.h> | ||
22 | #include <net/ip.h> | ||
23 | #include <net/protocol.h> | ||
24 | #include <uapi/linux/ila.h> | ||
25 | |||
26 | struct ila_params { | ||
27 | __be64 locator; | ||
28 | __be64 locator_match; | ||
29 | __wsum csum_diff; | ||
30 | }; | ||
31 | |||
32 | static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to) | ||
33 | { | ||
34 | __be32 diff[] = { | ||
35 | ~from[0], ~from[1], to[0], to[1], | ||
36 | }; | ||
37 | |||
38 | return csum_partial(diff, sizeof(diff), 0); | ||
39 | } | ||
40 | |||
41 | void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p); | ||
42 | |||
43 | int ila_lwt_init(void); | ||
44 | void ila_lwt_fini(void); | ||
45 | |||
46 | #endif /* __ILA_H */ | ||
diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c new file mode 100644 index 000000000000..64e1904991ac --- /dev/null +++ b/net/ipv6/ila/ila_common.c | |||
@@ -0,0 +1,95 @@ | |||
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 | { | ||
18 | if (*(__be64 *)&ip6h->daddr == p->locator_match) | ||
19 | return p->csum_diff; | ||
20 | else | ||
21 | return compute_csum_diff8((__be32 *)&ip6h->daddr, | ||
22 | (__be32 *)&p->locator); | ||
23 | } | ||
24 | |||
25 | void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) | ||
26 | { | ||
27 | __wsum diff; | ||
28 | struct ipv6hdr *ip6h = ipv6_hdr(skb); | ||
29 | size_t nhoff = sizeof(struct ipv6hdr); | ||
30 | |||
31 | /* First update checksum */ | ||
32 | switch (ip6h->nexthdr) { | ||
33 | case NEXTHDR_TCP: | ||
34 | if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) { | ||
35 | struct tcphdr *th = (struct tcphdr *) | ||
36 | (skb_network_header(skb) + nhoff); | ||
37 | |||
38 | diff = get_csum_diff(ip6h, p); | ||
39 | inet_proto_csum_replace_by_diff(&th->check, skb, | ||
40 | diff, true); | ||
41 | } | ||
42 | break; | ||
43 | case NEXTHDR_UDP: | ||
44 | if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) { | ||
45 | struct udphdr *uh = (struct udphdr *) | ||
46 | (skb_network_header(skb) + nhoff); | ||
47 | |||
48 | if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { | ||
49 | diff = get_csum_diff(ip6h, p); | ||
50 | inet_proto_csum_replace_by_diff(&uh->check, skb, | ||
51 | diff, true); | ||
52 | if (!uh->check) | ||
53 | uh->check = CSUM_MANGLED_0; | ||
54 | } | ||
55 | } | ||
56 | break; | ||
57 | case NEXTHDR_ICMP: | ||
58 | if (likely(pskb_may_pull(skb, | ||
59 | nhoff + sizeof(struct icmp6hdr)))) { | ||
60 | struct icmp6hdr *ih = (struct icmp6hdr *) | ||
61 | (skb_network_header(skb) + nhoff); | ||
62 | |||
63 | diff = get_csum_diff(ip6h, p); | ||
64 | inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb, | ||
65 | diff, true); | ||
66 | } | ||
67 | break; | ||
68 | } | ||
69 | |||
70 | /* Now change destination address */ | ||
71 | *(__be64 *)&ip6h->daddr = p->locator; | ||
72 | } | ||
73 | |||
74 | static int __init ila_init(void) | ||
75 | { | ||
76 | int ret; | ||
77 | |||
78 | ret = ila_lwt_init(); | ||
79 | |||
80 | if (ret) | ||
81 | goto fail_lwt; | ||
82 | |||
83 | fail_lwt: | ||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | static void __exit ila_fini(void) | ||
88 | { | ||
89 | ila_lwt_fini(); | ||
90 | } | ||
91 | |||
92 | module_init(ila_init); | ||
93 | module_exit(ila_fini); | ||
94 | MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>"); | ||
95 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c new file mode 100644 index 000000000000..2ae3c4fd8aab --- /dev/null +++ b/net/ipv6/ila/ila_lwt.c | |||
@@ -0,0 +1,152 @@ | |||
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 inline struct ila_params *ila_params_lwtunnel( | ||
17 | struct lwtunnel_state *lwstate) | ||
18 | { | ||
19 | return (struct ila_params *)lwstate->data; | ||
20 | } | ||
21 | |||
22 | static 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 | update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); | ||
30 | |||
31 | return dst->lwtstate->orig_output(net, sk, skb); | ||
32 | |||
33 | drop: | ||
34 | kfree_skb(skb); | ||
35 | return -EINVAL; | ||
36 | } | ||
37 | |||
38 | static 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 | update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); | ||
46 | |||
47 | return dst->lwtstate->orig_input(skb); | ||
48 | |||
49 | drop: | ||
50 | kfree_skb(skb); | ||
51 | return -EINVAL; | ||
52 | } | ||
53 | |||
54 | static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { | ||
55 | [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, | ||
56 | }; | ||
57 | |||
58 | static int ila_build_state(struct net_device *dev, struct nlattr *nla, | ||
59 | unsigned int family, const void *cfg, | ||
60 | struct lwtunnel_state **ts) | ||
61 | { | ||
62 | struct ila_params *p; | ||
63 | struct nlattr *tb[ILA_ATTR_MAX + 1]; | ||
64 | size_t encap_len = sizeof(*p); | ||
65 | struct lwtunnel_state *newts; | ||
66 | const struct fib6_config *cfg6 = cfg; | ||
67 | int ret; | ||
68 | |||
69 | if (family != AF_INET6) | ||
70 | return -EINVAL; | ||
71 | |||
72 | ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, | ||
73 | ila_nl_policy); | ||
74 | if (ret < 0) | ||
75 | return ret; | ||
76 | |||
77 | if (!tb[ILA_ATTR_LOCATOR]) | ||
78 | return -EINVAL; | ||
79 | |||
80 | newts = lwtunnel_state_alloc(encap_len); | ||
81 | if (!newts) | ||
82 | return -ENOMEM; | ||
83 | |||
84 | newts->len = encap_len; | ||
85 | p = ila_params_lwtunnel(newts); | ||
86 | |||
87 | p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); | ||
88 | |||
89 | if (cfg6->fc_dst_len > sizeof(__be64)) { | ||
90 | /* Precompute checksum difference for translation since we | ||
91 | * know both the old locator and the new one. | ||
92 | */ | ||
93 | p->locator_match = *(__be64 *)&cfg6->fc_dst; | ||
94 | p->csum_diff = compute_csum_diff8( | ||
95 | (__be32 *)&p->locator_match, (__be32 *)&p->locator); | ||
96 | } | ||
97 | |||
98 | newts->type = LWTUNNEL_ENCAP_ILA; | ||
99 | newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | | ||
100 | LWTUNNEL_STATE_INPUT_REDIRECT; | ||
101 | |||
102 | *ts = newts; | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int ila_fill_encap_info(struct sk_buff *skb, | ||
108 | struct lwtunnel_state *lwtstate) | ||
109 | { | ||
110 | struct ila_params *p = ila_params_lwtunnel(lwtstate); | ||
111 | |||
112 | if (nla_put_u64(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator)) | ||
113 | goto nla_put_failure; | ||
114 | |||
115 | return 0; | ||
116 | |||
117 | nla_put_failure: | ||
118 | return -EMSGSIZE; | ||
119 | } | ||
120 | |||
121 | static int ila_encap_nlsize(struct lwtunnel_state *lwtstate) | ||
122 | { | ||
123 | /* No encapsulation overhead */ | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) | ||
128 | { | ||
129 | struct ila_params *a_p = ila_params_lwtunnel(a); | ||
130 | struct ila_params *b_p = ila_params_lwtunnel(b); | ||
131 | |||
132 | return (a_p->locator != b_p->locator); | ||
133 | } | ||
134 | |||
135 | static const struct lwtunnel_encap_ops ila_encap_ops = { | ||
136 | .build_state = ila_build_state, | ||
137 | .output = ila_output, | ||
138 | .input = ila_input, | ||
139 | .fill_encap = ila_fill_encap_info, | ||
140 | .get_encap_size = ila_encap_nlsize, | ||
141 | .cmp_encap = ila_encap_cmp, | ||
142 | }; | ||
143 | |||
144 | int ila_lwt_init(void) | ||
145 | { | ||
146 | return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA); | ||
147 | } | ||
148 | |||
149 | void ila_lwt_fini(void) | ||
150 | { | ||
151 | lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA); | ||
152 | } | ||