]>
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> | |
22 | #include <linux/in.h> | |
23 | #include <linux/ip.h> | |
24 | #include <net/protocol.h> | |
25 | #include <net/tcp.h> | |
26 | #include <net/udp.h> | |
27 | #include <asm/system.h> | |
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 LT |
50 | { |
51 | unsigned hash = IP_VS_PROTO_HASH(pp->protocol); | |
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 | ||
62 | ||
63 | /* | |
64 | * unregister an ipvs protocol | |
65 | */ | |
66 | static int unregister_ip_vs_protocol(struct ip_vs_protocol *pp) | |
67 | { | |
68 | struct ip_vs_protocol **pp_p; | |
69 | unsigned hash = IP_VS_PROTO_HASH(pp->protocol); | |
70 | ||
71 | pp_p = &ip_vs_proto_table[hash]; | |
72 | for (; *pp_p; pp_p = &(*pp_p)->next) { | |
73 | if (*pp_p == pp) { | |
74 | *pp_p = pp->next; | |
75 | if (pp->exit != NULL) | |
76 | pp->exit(pp); | |
77 | return 0; | |
78 | } | |
79 | } | |
80 | ||
81 | return -ESRCH; | |
82 | } | |
83 | ||
84 | ||
85 | /* | |
86 | * get ip_vs_protocol object by its proto. | |
87 | */ | |
88 | struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto) | |
89 | { | |
90 | struct ip_vs_protocol *pp; | |
91 | unsigned hash = IP_VS_PROTO_HASH(proto); | |
92 | ||
93 | for (pp = ip_vs_proto_table[hash]; pp; pp = pp->next) { | |
94 | if (pp->protocol == proto) | |
95 | return pp; | |
96 | } | |
97 | ||
98 | return NULL; | |
99 | } | |
100 | ||
101 | ||
102 | /* | |
103 | * Propagate event for state change to all protocols | |
104 | */ | |
105 | void ip_vs_protocol_timeout_change(int flags) | |
106 | { | |
107 | struct ip_vs_protocol *pp; | |
108 | int i; | |
109 | ||
110 | for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { | |
111 | for (pp = ip_vs_proto_table[i]; pp; pp = pp->next) { | |
112 | if (pp->timeout_change) | |
113 | pp->timeout_change(pp, flags); | |
114 | } | |
115 | } | |
116 | } | |
117 | ||
118 | ||
119 | int * | |
120 | ip_vs_create_timeout_table(int *table, int size) | |
121 | { | |
8b2ed4bb | 122 | return kmemdup(table, size, GFP_ATOMIC); |
1da177e4 LT |
123 | } |
124 | ||
125 | ||
126 | /* | |
127 | * Set timeout value for state specified by name | |
128 | */ | |
129 | int | |
36cbd3dc JE |
130 | ip_vs_set_state_timeout(int *table, int num, const char *const *names, |
131 | const char *name, int to) | |
1da177e4 LT |
132 | { |
133 | int i; | |
134 | ||
135 | if (!table || !name || !to) | |
136 | return -EINVAL; | |
137 | ||
138 | for (i = 0; i < num; i++) { | |
139 | if (strcmp(names[i], name)) | |
140 | continue; | |
141 | table[i] = to * HZ; | |
142 | return 0; | |
143 | } | |
144 | return -ENOENT; | |
145 | } | |
146 | ||
147 | ||
148 | const char * ip_vs_state_name(__u16 proto, int state) | |
149 | { | |
150 | struct ip_vs_protocol *pp = ip_vs_proto_get(proto); | |
151 | ||
152 | if (pp == NULL || pp->state_name == NULL) | |
2ad17def | 153 | return (IPPROTO_IP == proto) ? "NONE" : "ERR!"; |
1da177e4 LT |
154 | return pp->state_name(state); |
155 | } | |
156 | ||
157 | ||
77eb8516 | 158 | static void |
3b047d9d JV |
159 | ip_vs_tcpudp_debug_packet_v4(struct ip_vs_protocol *pp, |
160 | const struct sk_buff *skb, | |
161 | int offset, | |
162 | const char *msg) | |
1da177e4 LT |
163 | { |
164 | char buf[128]; | |
165 | struct iphdr _iph, *ih; | |
166 | ||
167 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | |
168 | if (ih == NULL) | |
169 | sprintf(buf, "%s TRUNCATED", pp->name); | |
5661df7b | 170 | else if (ih->frag_off & htons(IP_OFFSET)) |
14d5e834 HH |
171 | sprintf(buf, "%s %pI4->%pI4 frag", |
172 | pp->name, &ih->saddr, &ih->daddr); | |
1da177e4 | 173 | else { |
014d730d | 174 | __be16 _ports[2], *pptr |
1da177e4 LT |
175 | ; |
176 | pptr = skb_header_pointer(skb, offset + ih->ihl*4, | |
177 | sizeof(_ports), _ports); | |
178 | if (pptr == NULL) | |
14d5e834 HH |
179 | sprintf(buf, "%s TRUNCATED %pI4->%pI4", |
180 | pp->name, &ih->saddr, &ih->daddr); | |
1da177e4 | 181 | else |
14d5e834 | 182 | sprintf(buf, "%s %pI4:%u->%pI4:%u", |
1da177e4 | 183 | pp->name, |
14d5e834 HH |
184 | &ih->saddr, ntohs(pptr[0]), |
185 | &ih->daddr, ntohs(pptr[1])); | |
1da177e4 LT |
186 | } |
187 | ||
9aada7ac | 188 | pr_debug("%s: %s\n", msg, buf); |
1da177e4 LT |
189 | } |
190 | ||
3b047d9d | 191 | #ifdef CONFIG_IP_VS_IPV6 |
77eb8516 | 192 | static void |
3b047d9d JV |
193 | ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp, |
194 | const struct sk_buff *skb, | |
195 | int offset, | |
196 | const char *msg) | |
197 | { | |
198 | char buf[192]; | |
199 | struct ipv6hdr _iph, *ih; | |
200 | ||
201 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | |
202 | if (ih == NULL) | |
203 | sprintf(buf, "%s TRUNCATED", pp->name); | |
204 | else if (ih->nexthdr == IPPROTO_FRAGMENT) | |
5b095d98 | 205 | sprintf(buf, "%s %pI6->%pI6 frag", |
38ff4fa4 | 206 | pp->name, &ih->saddr, &ih->daddr); |
3b047d9d JV |
207 | else { |
208 | __be16 _ports[2], *pptr; | |
209 | ||
210 | pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr), | |
211 | sizeof(_ports), _ports); | |
212 | if (pptr == NULL) | |
5b095d98 | 213 | sprintf(buf, "%s TRUNCATED %pI6->%pI6", |
38ff4fa4 | 214 | pp->name, &ih->saddr, &ih->daddr); |
3b047d9d | 215 | else |
5b095d98 | 216 | sprintf(buf, "%s %pI6:%u->%pI6:%u", |
3b047d9d | 217 | pp->name, |
38ff4fa4 HH |
218 | &ih->saddr, ntohs(pptr[0]), |
219 | &ih->daddr, ntohs(pptr[1])); | |
3b047d9d JV |
220 | } |
221 | ||
9aada7ac | 222 | pr_debug("%s: %s\n", msg, buf); |
3b047d9d JV |
223 | } |
224 | #endif | |
225 | ||
226 | ||
227 | void | |
228 | ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp, | |
229 | const struct sk_buff *skb, | |
230 | int offset, | |
231 | const char *msg) | |
232 | { | |
233 | #ifdef CONFIG_IP_VS_IPV6 | |
d286600e | 234 | if (skb->protocol == htons(ETH_P_IPV6)) |
3b047d9d JV |
235 | ip_vs_tcpudp_debug_packet_v6(pp, skb, offset, msg); |
236 | else | |
237 | #endif | |
238 | ip_vs_tcpudp_debug_packet_v4(pp, skb, offset, msg); | |
239 | } | |
240 | ||
1da177e4 | 241 | |
048cf48b | 242 | int __init ip_vs_protocol_init(void) |
1da177e4 LT |
243 | { |
244 | char protocols[64]; | |
245 | #define REGISTER_PROTOCOL(p) \ | |
246 | do { \ | |
247 | register_ip_vs_protocol(p); \ | |
248 | strcat(protocols, ", "); \ | |
249 | strcat(protocols, (p)->name); \ | |
250 | } while (0) | |
251 | ||
252 | protocols[0] = '\0'; | |
253 | protocols[2] = '\0'; | |
254 | #ifdef CONFIG_IP_VS_PROTO_TCP | |
255 | REGISTER_PROTOCOL(&ip_vs_protocol_tcp); | |
256 | #endif | |
257 | #ifdef CONFIG_IP_VS_PROTO_UDP | |
258 | REGISTER_PROTOCOL(&ip_vs_protocol_udp); | |
259 | #endif | |
1da177e4 LT |
260 | #ifdef CONFIG_IP_VS_PROTO_AH |
261 | REGISTER_PROTOCOL(&ip_vs_protocol_ah); | |
262 | #endif | |
263 | #ifdef CONFIG_IP_VS_PROTO_ESP | |
264 | REGISTER_PROTOCOL(&ip_vs_protocol_esp); | |
265 | #endif | |
1e3e238e | 266 | pr_info("Registered protocols (%s)\n", &protocols[2]); |
1da177e4 LT |
267 | |
268 | return 0; | |
269 | } | |
270 | ||
271 | ||
272 | void ip_vs_protocol_cleanup(void) | |
273 | { | |
274 | struct ip_vs_protocol *pp; | |
275 | int i; | |
276 | ||
277 | /* unregister all the ipvs protocols */ | |
278 | for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { | |
279 | while ((pp = ip_vs_proto_table[i]) != NULL) | |
280 | unregister_ip_vs_protocol(pp); | |
281 | } | |
282 | } |