Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * ip_vs_proto.c: transport protocol load balancing support for IPVS | |
3 | * | |
1da177e4 LT |
4 | * Authors: Wensong Zhang <wensong@linuxvirtualserver.org> |
5 | * Julian Anastasov <ja@ssi.bg> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation; either version | |
10 | * 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * Changes: | |
13 | * | |
14 | */ | |
15 | ||
9aada7ac HE |
16 | #define KMSG_COMPONENT "IPVS" |
17 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
18 | ||
1da177e4 LT |
19 | #include <linux/module.h> |
20 | #include <linux/kernel.h> | |
21 | #include <linux/skbuff.h> | |
5a0e3ad6 | 22 | #include <linux/gfp.h> |
1da177e4 LT |
23 | #include <linux/in.h> |
24 | #include <linux/ip.h> | |
25 | #include <net/protocol.h> | |
26 | #include <net/tcp.h> | |
27 | #include <net/udp.h> | |
1da177e4 LT |
28 | #include <linux/stat.h> |
29 | #include <linux/proc_fs.h> | |
30 | ||
31 | #include <net/ip_vs.h> | |
32 | ||
33 | ||
34 | /* | |
35 | * IPVS protocols can only be registered/unregistered when the ipvs | |
36 | * module is loaded/unloaded, so no lock is needed in accessing the | |
37 | * ipvs protocol table. | |
38 | */ | |
39 | ||
40 | #define IP_VS_PROTO_TAB_SIZE 32 /* must be power of 2 */ | |
41 | #define IP_VS_PROTO_HASH(proto) ((proto) & (IP_VS_PROTO_TAB_SIZE-1)) | |
42 | ||
43 | static struct ip_vs_protocol *ip_vs_proto_table[IP_VS_PROTO_TAB_SIZE]; | |
44 | ||
45 | ||
46 | /* | |
47 | * register an ipvs protocol | |
48 | */ | |
048cf48b | 49 | static int __used __init register_ip_vs_protocol(struct ip_vs_protocol *pp) |
1da177e4 | 50 | { |
95c96174 | 51 | unsigned int hash = IP_VS_PROTO_HASH(pp->protocol); |
1da177e4 LT |
52 | |
53 | pp->next = ip_vs_proto_table[hash]; | |
54 | ip_vs_proto_table[hash] = pp; | |
55 | ||
56 | if (pp->init != NULL) | |
57 | pp->init(pp); | |
58 | ||
59 | return 0; | |
60 | } | |
61 | ||
252c6410 HS |
62 | /* |
63 | * register an ipvs protocols netns related data | |
64 | */ | |
65 | static int | |
66 | register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp) | |
67 | { | |
68 | struct netns_ipvs *ipvs = net_ipvs(net); | |
95c96174 | 69 | unsigned int hash = IP_VS_PROTO_HASH(pp->protocol); |
252c6410 | 70 | struct ip_vs_proto_data *pd = |
9615e61e | 71 | kzalloc(sizeof(struct ip_vs_proto_data), GFP_KERNEL); |
252c6410 | 72 | |
0a9ee813 | 73 | if (!pd) |
252c6410 | 74 | return -ENOMEM; |
0a9ee813 | 75 | |
252c6410 HS |
76 | pd->pp = pp; /* For speed issues */ |
77 | pd->next = ipvs->proto_data_table[hash]; | |
78 | ipvs->proto_data_table[hash] = pd; | |
79 | atomic_set(&pd->appcnt, 0); /* Init app counter */ | |
80 | ||
582b8e3e HS |
81 | if (pp->init_netns != NULL) { |
82 | int ret = pp->init_netns(net, pd); | |
83 | if (ret) { | |
84 | /* unlink an free proto data */ | |
85 | ipvs->proto_data_table[hash] = pd->next; | |
86 | kfree(pd); | |
87 | return ret; | |
88 | } | |
89 | } | |
252c6410 HS |
90 | |
91 | return 0; | |
92 | } | |
1da177e4 LT |
93 | |
94 | /* | |
95 | * unregister an ipvs protocol | |
96 | */ | |
97 | static int unregister_ip_vs_protocol(struct ip_vs_protocol *pp) | |
98 | { | |
99 | struct ip_vs_protocol **pp_p; | |
95c96174 | 100 | unsigned int hash = IP_VS_PROTO_HASH(pp->protocol); |
1da177e4 LT |
101 | |
102 | pp_p = &ip_vs_proto_table[hash]; | |
103 | for (; *pp_p; pp_p = &(*pp_p)->next) { | |
104 | if (*pp_p == pp) { | |
105 | *pp_p = pp->next; | |
106 | if (pp->exit != NULL) | |
107 | pp->exit(pp); | |
108 | return 0; | |
109 | } | |
110 | } | |
111 | ||
112 | return -ESRCH; | |
113 | } | |
114 | ||
252c6410 HS |
115 | /* |
116 | * unregister an ipvs protocols netns data | |
117 | */ | |
118 | static int | |
119 | unregister_ip_vs_proto_netns(struct net *net, struct ip_vs_proto_data *pd) | |
120 | { | |
121 | struct netns_ipvs *ipvs = net_ipvs(net); | |
122 | struct ip_vs_proto_data **pd_p; | |
95c96174 | 123 | unsigned int hash = IP_VS_PROTO_HASH(pd->pp->protocol); |
252c6410 HS |
124 | |
125 | pd_p = &ipvs->proto_data_table[hash]; | |
126 | for (; *pd_p; pd_p = &(*pd_p)->next) { | |
127 | if (*pd_p == pd) { | |
128 | *pd_p = pd->next; | |
129 | if (pd->pp->exit_netns != NULL) | |
130 | pd->pp->exit_netns(net, pd); | |
131 | kfree(pd); | |
132 | return 0; | |
133 | } | |
134 | } | |
135 | ||
136 | return -ESRCH; | |
137 | } | |
1da177e4 LT |
138 | |
139 | /* | |
140 | * get ip_vs_protocol object by its proto. | |
141 | */ | |
142 | struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto) | |
143 | { | |
144 | struct ip_vs_protocol *pp; | |
95c96174 | 145 | unsigned int hash = IP_VS_PROTO_HASH(proto); |
1da177e4 LT |
146 | |
147 | for (pp = ip_vs_proto_table[hash]; pp; pp = pp->next) { | |
148 | if (pp->protocol == proto) | |
149 | return pp; | |
150 | } | |
151 | ||
152 | return NULL; | |
153 | } | |
9c3e1c39 | 154 | EXPORT_SYMBOL(ip_vs_proto_get); |
1da177e4 | 155 | |
252c6410 HS |
156 | /* |
157 | * get ip_vs_protocol object data by netns and proto | |
158 | */ | |
068d5220 | 159 | static struct ip_vs_proto_data * |
9330419d | 160 | __ipvs_proto_data_get(struct netns_ipvs *ipvs, unsigned short proto) |
252c6410 | 161 | { |
252c6410 | 162 | struct ip_vs_proto_data *pd; |
95c96174 | 163 | unsigned int hash = IP_VS_PROTO_HASH(proto); |
252c6410 HS |
164 | |
165 | for (pd = ipvs->proto_data_table[hash]; pd; pd = pd->next) { | |
166 | if (pd->pp->protocol == proto) | |
167 | return pd; | |
168 | } | |
169 | ||
170 | return NULL; | |
171 | } | |
9330419d HS |
172 | |
173 | struct ip_vs_proto_data * | |
174 | ip_vs_proto_data_get(struct net *net, unsigned short proto) | |
175 | { | |
176 | struct netns_ipvs *ipvs = net_ipvs(net); | |
177 | ||
178 | return __ipvs_proto_data_get(ipvs, proto); | |
179 | } | |
252c6410 | 180 | EXPORT_SYMBOL(ip_vs_proto_data_get); |
1da177e4 LT |
181 | |
182 | /* | |
183 | * Propagate event for state change to all protocols | |
184 | */ | |
9330419d | 185 | void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags) |
1da177e4 | 186 | { |
9330419d | 187 | struct ip_vs_proto_data *pd; |
1da177e4 LT |
188 | int i; |
189 | ||
190 | for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { | |
9330419d HS |
191 | for (pd = ipvs->proto_data_table[i]; pd; pd = pd->next) { |
192 | if (pd->pp->timeout_change) | |
193 | pd->pp->timeout_change(pd, flags); | |
1da177e4 LT |
194 | } |
195 | } | |
196 | } | |
197 | ||
198 | ||
199 | int * | |
200 | ip_vs_create_timeout_table(int *table, int size) | |
201 | { | |
41cff6d5 | 202 | return kmemdup(table, size, GFP_KERNEL); |
1da177e4 LT |
203 | } |
204 | ||
205 | ||
206 | /* | |
207 | * Set timeout value for state specified by name | |
208 | */ | |
209 | int | |
36cbd3dc JE |
210 | ip_vs_set_state_timeout(int *table, int num, const char *const *names, |
211 | const char *name, int to) | |
1da177e4 LT |
212 | { |
213 | int i; | |
214 | ||
215 | if (!table || !name || !to) | |
216 | return -EINVAL; | |
217 | ||
218 | for (i = 0; i < num; i++) { | |
219 | if (strcmp(names[i], name)) | |
220 | continue; | |
221 | table[i] = to * HZ; | |
222 | return 0; | |
223 | } | |
224 | return -ENOENT; | |
225 | } | |
226 | ||
227 | ||
228 | const char * ip_vs_state_name(__u16 proto, int state) | |
229 | { | |
230 | struct ip_vs_protocol *pp = ip_vs_proto_get(proto); | |
231 | ||
232 | if (pp == NULL || pp->state_name == NULL) | |
2ad17def | 233 | return (IPPROTO_IP == proto) ? "NONE" : "ERR!"; |
1da177e4 LT |
234 | return pp->state_name(state); |
235 | } | |
236 | ||
237 | ||
77eb8516 | 238 | static void |
3b047d9d JV |
239 | ip_vs_tcpudp_debug_packet_v4(struct ip_vs_protocol *pp, |
240 | const struct sk_buff *skb, | |
241 | int offset, | |
242 | const char *msg) | |
1da177e4 LT |
243 | { |
244 | char buf[128]; | |
245 | struct iphdr _iph, *ih; | |
246 | ||
247 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | |
248 | if (ih == NULL) | |
3d91c1a8 | 249 | sprintf(buf, "TRUNCATED"); |
5661df7b | 250 | else if (ih->frag_off & htons(IP_OFFSET)) |
3d91c1a8 | 251 | sprintf(buf, "%pI4->%pI4 frag", &ih->saddr, &ih->daddr); |
1da177e4 | 252 | else { |
0d79641a JA |
253 | __be16 _ports[2], *pptr; |
254 | ||
1da177e4 LT |
255 | pptr = skb_header_pointer(skb, offset + ih->ihl*4, |
256 | sizeof(_ports), _ports); | |
257 | if (pptr == NULL) | |
3d91c1a8 PM |
258 | sprintf(buf, "TRUNCATED %pI4->%pI4", |
259 | &ih->saddr, &ih->daddr); | |
1da177e4 | 260 | else |
3d91c1a8 | 261 | sprintf(buf, "%pI4:%u->%pI4:%u", |
14d5e834 HH |
262 | &ih->saddr, ntohs(pptr[0]), |
263 | &ih->daddr, ntohs(pptr[1])); | |
1da177e4 LT |
264 | } |
265 | ||
3d91c1a8 | 266 | pr_debug("%s: %s %s\n", msg, pp->name, buf); |
1da177e4 LT |
267 | } |
268 | ||
3b047d9d | 269 | #ifdef CONFIG_IP_VS_IPV6 |
77eb8516 | 270 | static void |
3b047d9d JV |
271 | ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp, |
272 | const struct sk_buff *skb, | |
273 | int offset, | |
274 | const char *msg) | |
275 | { | |
276 | char buf[192]; | |
277 | struct ipv6hdr _iph, *ih; | |
278 | ||
279 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | |
280 | if (ih == NULL) | |
3d91c1a8 | 281 | sprintf(buf, "TRUNCATED"); |
3b047d9d | 282 | else if (ih->nexthdr == IPPROTO_FRAGMENT) |
120b9c14 | 283 | sprintf(buf, "%pI6c->%pI6c frag", &ih->saddr, &ih->daddr); |
3b047d9d JV |
284 | else { |
285 | __be16 _ports[2], *pptr; | |
286 | ||
287 | pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr), | |
288 | sizeof(_ports), _ports); | |
289 | if (pptr == NULL) | |
120b9c14 | 290 | sprintf(buf, "TRUNCATED %pI6c->%pI6c", |
3d91c1a8 | 291 | &ih->saddr, &ih->daddr); |
3b047d9d | 292 | else |
120b9c14 | 293 | sprintf(buf, "%pI6c:%u->%pI6c:%u", |
38ff4fa4 HH |
294 | &ih->saddr, ntohs(pptr[0]), |
295 | &ih->daddr, ntohs(pptr[1])); | |
3b047d9d JV |
296 | } |
297 | ||
3d91c1a8 | 298 | pr_debug("%s: %s %s\n", msg, pp->name, buf); |
3b047d9d JV |
299 | } |
300 | #endif | |
301 | ||
302 | ||
303 | void | |
0d79641a | 304 | ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, |
3b047d9d JV |
305 | const struct sk_buff *skb, |
306 | int offset, | |
307 | const char *msg) | |
308 | { | |
309 | #ifdef CONFIG_IP_VS_IPV6 | |
0d79641a | 310 | if (af == AF_INET6) |
3b047d9d JV |
311 | ip_vs_tcpudp_debug_packet_v6(pp, skb, offset, msg); |
312 | else | |
313 | #endif | |
314 | ip_vs_tcpudp_debug_packet_v4(pp, skb, offset, msg); | |
315 | } | |
316 | ||
61b1ab45 HS |
317 | /* |
318 | * per network name-space init | |
319 | */ | |
503cf15a | 320 | int __net_init ip_vs_protocol_net_init(struct net *net) |
61b1ab45 | 321 | { |
7118c07a SL |
322 | int i, ret; |
323 | static struct ip_vs_protocol *protos[] = { | |
4a85b96c | 324 | #ifdef CONFIG_IP_VS_PROTO_TCP |
7118c07a | 325 | &ip_vs_protocol_tcp, |
78b16bde HS |
326 | #endif |
327 | #ifdef CONFIG_IP_VS_PROTO_UDP | |
7118c07a | 328 | &ip_vs_protocol_udp, |
9d934878 HS |
329 | #endif |
330 | #ifdef CONFIG_IP_VS_PROTO_SCTP | |
7118c07a | 331 | &ip_vs_protocol_sctp, |
88fe2d37 HS |
332 | #endif |
333 | #ifdef CONFIG_IP_VS_PROTO_AH | |
7118c07a | 334 | &ip_vs_protocol_ah, |
88fe2d37 HS |
335 | #endif |
336 | #ifdef CONFIG_IP_VS_PROTO_ESP | |
7118c07a | 337 | &ip_vs_protocol_esp, |
4a85b96c | 338 | #endif |
7118c07a SL |
339 | }; |
340 | ||
341 | for (i = 0; i < ARRAY_SIZE(protos); i++) { | |
342 | ret = register_ip_vs_proto_netns(net, protos[i]); | |
343 | if (ret < 0) | |
344 | goto cleanup; | |
345 | } | |
61b1ab45 | 346 | return 0; |
7118c07a SL |
347 | |
348 | cleanup: | |
349 | ip_vs_protocol_net_cleanup(net); | |
350 | return ret; | |
61b1ab45 HS |
351 | } |
352 | ||
503cf15a | 353 | void __net_exit ip_vs_protocol_net_cleanup(struct net *net) |
61b1ab45 | 354 | { |
4a85b96c HS |
355 | struct netns_ipvs *ipvs = net_ipvs(net); |
356 | struct ip_vs_proto_data *pd; | |
357 | int i; | |
358 | ||
359 | /* unregister all the ipvs proto data for this netns */ | |
360 | for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { | |
361 | while ((pd = ipvs->proto_data_table[i]) != NULL) | |
362 | unregister_ip_vs_proto_netns(net, pd); | |
363 | } | |
61b1ab45 HS |
364 | } |
365 | ||
048cf48b | 366 | int __init ip_vs_protocol_init(void) |
1da177e4 LT |
367 | { |
368 | char protocols[64]; | |
369 | #define REGISTER_PROTOCOL(p) \ | |
370 | do { \ | |
371 | register_ip_vs_protocol(p); \ | |
372 | strcat(protocols, ", "); \ | |
373 | strcat(protocols, (p)->name); \ | |
374 | } while (0) | |
375 | ||
376 | protocols[0] = '\0'; | |
377 | protocols[2] = '\0'; | |
378 | #ifdef CONFIG_IP_VS_PROTO_TCP | |
379 | REGISTER_PROTOCOL(&ip_vs_protocol_tcp); | |
380 | #endif | |
381 | #ifdef CONFIG_IP_VS_PROTO_UDP | |
382 | REGISTER_PROTOCOL(&ip_vs_protocol_udp); | |
383 | #endif | |
2906f66a VMR |
384 | #ifdef CONFIG_IP_VS_PROTO_SCTP |
385 | REGISTER_PROTOCOL(&ip_vs_protocol_sctp); | |
386 | #endif | |
1da177e4 LT |
387 | #ifdef CONFIG_IP_VS_PROTO_AH |
388 | REGISTER_PROTOCOL(&ip_vs_protocol_ah); | |
389 | #endif | |
390 | #ifdef CONFIG_IP_VS_PROTO_ESP | |
391 | REGISTER_PROTOCOL(&ip_vs_protocol_esp); | |
392 | #endif | |
1e3e238e | 393 | pr_info("Registered protocols (%s)\n", &protocols[2]); |
1da177e4 LT |
394 | |
395 | return 0; | |
396 | } | |
397 | ||
398 | ||
399 | void ip_vs_protocol_cleanup(void) | |
400 | { | |
401 | struct ip_vs_protocol *pp; | |
402 | int i; | |
403 | ||
404 | /* unregister all the ipvs protocols */ | |
405 | for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { | |
406 | while ((pp = ip_vs_proto_table[i]) != NULL) | |
407 | unregister_ip_vs_protocol(pp); | |
408 | } | |
409 | } |