Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * ip_vs_proto.c: transport protocol load balancing support for IPVS | |
3 | * | |
4 | * Version: $Id: ip_vs_proto.c,v 1.2 2003/04/18 09:03:16 wensong Exp $ | |
5 | * | |
6 | * Authors: Wensong Zhang <wensong@linuxvirtualserver.org> | |
7 | * Julian Anastasov <ja@ssi.bg> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * as published by the Free Software Foundation; either version | |
12 | * 2 of the License, or (at your option) any later version. | |
13 | * | |
14 | * Changes: | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/kernel.h> | |
20 | #include <linux/skbuff.h> | |
21 | #include <linux/in.h> | |
22 | #include <linux/ip.h> | |
23 | #include <net/protocol.h> | |
24 | #include <net/tcp.h> | |
25 | #include <net/udp.h> | |
26 | #include <asm/system.h> | |
27 | #include <linux/stat.h> | |
28 | #include <linux/proc_fs.h> | |
29 | ||
30 | #include <net/ip_vs.h> | |
31 | ||
32 | ||
33 | /* | |
34 | * IPVS protocols can only be registered/unregistered when the ipvs | |
35 | * module is loaded/unloaded, so no lock is needed in accessing the | |
36 | * ipvs protocol table. | |
37 | */ | |
38 | ||
39 | #define IP_VS_PROTO_TAB_SIZE 32 /* must be power of 2 */ | |
40 | #define IP_VS_PROTO_HASH(proto) ((proto) & (IP_VS_PROTO_TAB_SIZE-1)) | |
41 | ||
42 | static struct ip_vs_protocol *ip_vs_proto_table[IP_VS_PROTO_TAB_SIZE]; | |
43 | ||
44 | ||
45 | /* | |
46 | * register an ipvs protocol | |
47 | */ | |
d535a916 | 48 | static int __used register_ip_vs_protocol(struct ip_vs_protocol *pp) |
1da177e4 LT |
49 | { |
50 | unsigned hash = IP_VS_PROTO_HASH(pp->protocol); | |
51 | ||
52 | pp->next = ip_vs_proto_table[hash]; | |
53 | ip_vs_proto_table[hash] = pp; | |
54 | ||
55 | if (pp->init != NULL) | |
56 | pp->init(pp); | |
57 | ||
58 | return 0; | |
59 | } | |
60 | ||
61 | ||
62 | /* | |
63 | * unregister an ipvs protocol | |
64 | */ | |
65 | static int unregister_ip_vs_protocol(struct ip_vs_protocol *pp) | |
66 | { | |
67 | struct ip_vs_protocol **pp_p; | |
68 | unsigned hash = IP_VS_PROTO_HASH(pp->protocol); | |
69 | ||
70 | pp_p = &ip_vs_proto_table[hash]; | |
71 | for (; *pp_p; pp_p = &(*pp_p)->next) { | |
72 | if (*pp_p == pp) { | |
73 | *pp_p = pp->next; | |
74 | if (pp->exit != NULL) | |
75 | pp->exit(pp); | |
76 | return 0; | |
77 | } | |
78 | } | |
79 | ||
80 | return -ESRCH; | |
81 | } | |
82 | ||
83 | ||
84 | /* | |
85 | * get ip_vs_protocol object by its proto. | |
86 | */ | |
87 | struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto) | |
88 | { | |
89 | struct ip_vs_protocol *pp; | |
90 | unsigned hash = IP_VS_PROTO_HASH(proto); | |
91 | ||
92 | for (pp = ip_vs_proto_table[hash]; pp; pp = pp->next) { | |
93 | if (pp->protocol == proto) | |
94 | return pp; | |
95 | } | |
96 | ||
97 | return NULL; | |
98 | } | |
99 | ||
100 | ||
101 | /* | |
102 | * Propagate event for state change to all protocols | |
103 | */ | |
104 | void ip_vs_protocol_timeout_change(int flags) | |
105 | { | |
106 | struct ip_vs_protocol *pp; | |
107 | int i; | |
108 | ||
109 | for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { | |
110 | for (pp = ip_vs_proto_table[i]; pp; pp = pp->next) { | |
111 | if (pp->timeout_change) | |
112 | pp->timeout_change(pp, flags); | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | ||
118 | int * | |
119 | ip_vs_create_timeout_table(int *table, int size) | |
120 | { | |
8b2ed4bb | 121 | return kmemdup(table, size, GFP_ATOMIC); |
1da177e4 LT |
122 | } |
123 | ||
124 | ||
125 | /* | |
126 | * Set timeout value for state specified by name | |
127 | */ | |
128 | int | |
129 | ip_vs_set_state_timeout(int *table, int num, char **names, char *name, int to) | |
130 | { | |
131 | int i; | |
132 | ||
133 | if (!table || !name || !to) | |
134 | return -EINVAL; | |
135 | ||
136 | for (i = 0; i < num; i++) { | |
137 | if (strcmp(names[i], name)) | |
138 | continue; | |
139 | table[i] = to * HZ; | |
140 | return 0; | |
141 | } | |
142 | return -ENOENT; | |
143 | } | |
144 | ||
145 | ||
146 | const char * ip_vs_state_name(__u16 proto, int state) | |
147 | { | |
148 | struct ip_vs_protocol *pp = ip_vs_proto_get(proto); | |
149 | ||
150 | if (pp == NULL || pp->state_name == NULL) | |
151 | return "ERR!"; | |
152 | return pp->state_name(state); | |
153 | } | |
154 | ||
155 | ||
156 | void | |
157 | ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp, | |
158 | const struct sk_buff *skb, | |
159 | int offset, | |
160 | const char *msg) | |
161 | { | |
162 | char buf[128]; | |
163 | struct iphdr _iph, *ih; | |
164 | ||
165 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | |
166 | if (ih == NULL) | |
167 | sprintf(buf, "%s TRUNCATED", pp->name); | |
168 | else if (ih->frag_off & __constant_htons(IP_OFFSET)) | |
169 | sprintf(buf, "%s %u.%u.%u.%u->%u.%u.%u.%u frag", | |
170 | pp->name, NIPQUAD(ih->saddr), | |
171 | NIPQUAD(ih->daddr)); | |
172 | else { | |
014d730d | 173 | __be16 _ports[2], *pptr |
1da177e4 LT |
174 | ; |
175 | pptr = skb_header_pointer(skb, offset + ih->ihl*4, | |
176 | sizeof(_ports), _ports); | |
177 | if (pptr == NULL) | |
178 | sprintf(buf, "%s TRUNCATED %u.%u.%u.%u->%u.%u.%u.%u", | |
179 | pp->name, | |
180 | NIPQUAD(ih->saddr), | |
181 | NIPQUAD(ih->daddr)); | |
182 | else | |
183 | sprintf(buf, "%s %u.%u.%u.%u:%u->%u.%u.%u.%u:%u", | |
184 | pp->name, | |
185 | NIPQUAD(ih->saddr), | |
186 | ntohs(pptr[0]), | |
187 | NIPQUAD(ih->daddr), | |
188 | ntohs(pptr[1])); | |
189 | } | |
190 | ||
191 | printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); | |
192 | } | |
193 | ||
194 | ||
195 | int ip_vs_protocol_init(void) | |
196 | { | |
197 | char protocols[64]; | |
198 | #define REGISTER_PROTOCOL(p) \ | |
199 | do { \ | |
200 | register_ip_vs_protocol(p); \ | |
201 | strcat(protocols, ", "); \ | |
202 | strcat(protocols, (p)->name); \ | |
203 | } while (0) | |
204 | ||
205 | protocols[0] = '\0'; | |
206 | protocols[2] = '\0'; | |
207 | #ifdef CONFIG_IP_VS_PROTO_TCP | |
208 | REGISTER_PROTOCOL(&ip_vs_protocol_tcp); | |
209 | #endif | |
210 | #ifdef CONFIG_IP_VS_PROTO_UDP | |
211 | REGISTER_PROTOCOL(&ip_vs_protocol_udp); | |
212 | #endif | |
1da177e4 LT |
213 | #ifdef CONFIG_IP_VS_PROTO_AH |
214 | REGISTER_PROTOCOL(&ip_vs_protocol_ah); | |
215 | #endif | |
216 | #ifdef CONFIG_IP_VS_PROTO_ESP | |
217 | REGISTER_PROTOCOL(&ip_vs_protocol_esp); | |
218 | #endif | |
219 | IP_VS_INFO("Registered protocols (%s)\n", &protocols[2]); | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | ||
225 | void ip_vs_protocol_cleanup(void) | |
226 | { | |
227 | struct ip_vs_protocol *pp; | |
228 | int i; | |
229 | ||
230 | /* unregister all the ipvs protocols */ | |
231 | for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { | |
232 | while ((pp = ip_vs_proto_table[i]) != NULL) | |
233 | unregister_ip_vs_protocol(pp); | |
234 | } | |
235 | } |