Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.16-rc2 430 lines 10 kB view raw
1/* 2 * ip_vs_proto_udp.c: UDP load balancing support for IPVS 3 * 4 * Version: $Id: ip_vs_proto_udp.c,v 1.3 2002/11/30 01:50:35 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/in.h> 19#include <linux/ip.h> 20#include <linux/kernel.h> 21#include <linux/netfilter_ipv4.h> 22#include <linux/udp.h> 23 24#include <net/ip_vs.h> 25 26 27static struct ip_vs_conn * 28udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, 29 const struct iphdr *iph, unsigned int proto_off, int inverse) 30{ 31 struct ip_vs_conn *cp; 32 __u16 _ports[2], *pptr; 33 34 pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports); 35 if (pptr == NULL) 36 return NULL; 37 38 if (likely(!inverse)) { 39 cp = ip_vs_conn_in_get(iph->protocol, 40 iph->saddr, pptr[0], 41 iph->daddr, pptr[1]); 42 } else { 43 cp = ip_vs_conn_in_get(iph->protocol, 44 iph->daddr, pptr[1], 45 iph->saddr, pptr[0]); 46 } 47 48 return cp; 49} 50 51 52static struct ip_vs_conn * 53udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, 54 const struct iphdr *iph, unsigned int proto_off, int inverse) 55{ 56 struct ip_vs_conn *cp; 57 __u16 _ports[2], *pptr; 58 59 pptr = skb_header_pointer(skb, skb->nh.iph->ihl*4, 60 sizeof(_ports), _ports); 61 if (pptr == NULL) 62 return NULL; 63 64 if (likely(!inverse)) { 65 cp = ip_vs_conn_out_get(iph->protocol, 66 iph->saddr, pptr[0], 67 iph->daddr, pptr[1]); 68 } else { 69 cp = ip_vs_conn_out_get(iph->protocol, 70 iph->daddr, pptr[1], 71 iph->saddr, pptr[0]); 72 } 73 74 return cp; 75} 76 77 78static int 79udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp, 80 int *verdict, struct ip_vs_conn **cpp) 81{ 82 struct ip_vs_service *svc; 83 struct udphdr _udph, *uh; 84 85 uh = skb_header_pointer(skb, skb->nh.iph->ihl*4, 86 sizeof(_udph), &_udph); 87 if (uh == NULL) { 88 *verdict = NF_DROP; 89 return 0; 90 } 91 92 if ((svc = ip_vs_service_get(skb->nfmark, skb->nh.iph->protocol, 93 skb->nh.iph->daddr, uh->dest))) { 94 if (ip_vs_todrop()) { 95 /* 96 * It seems that we are very loaded. 97 * We have to drop this packet :( 98 */ 99 ip_vs_service_put(svc); 100 *verdict = NF_DROP; 101 return 0; 102 } 103 104 /* 105 * Let the virtual server select a real server for the 106 * incoming connection, and create a connection entry. 107 */ 108 *cpp = ip_vs_schedule(svc, skb); 109 if (!*cpp) { 110 *verdict = ip_vs_leave(svc, skb, pp); 111 return 0; 112 } 113 ip_vs_service_put(svc); 114 } 115 return 1; 116} 117 118 119static inline void 120udp_fast_csum_update(struct udphdr *uhdr, u32 oldip, u32 newip, 121 u16 oldport, u16 newport) 122{ 123 uhdr->check = 124 ip_vs_check_diff(~oldip, newip, 125 ip_vs_check_diff(oldport ^ 0xFFFF, 126 newport, uhdr->check)); 127 if (!uhdr->check) 128 uhdr->check = 0xFFFF; 129} 130 131static int 132udp_snat_handler(struct sk_buff **pskb, 133 struct ip_vs_protocol *pp, struct ip_vs_conn *cp) 134{ 135 struct udphdr *udph; 136 unsigned int udphoff = (*pskb)->nh.iph->ihl * 4; 137 138 /* csum_check requires unshared skb */ 139 if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph))) 140 return 0; 141 142 if (unlikely(cp->app != NULL)) { 143 /* Some checks before mangling */ 144 if (pp->csum_check && !pp->csum_check(*pskb, pp)) 145 return 0; 146 147 /* 148 * Call application helper if needed 149 */ 150 if (!ip_vs_app_pkt_out(cp, pskb)) 151 return 0; 152 } 153 154 udph = (void *)(*pskb)->nh.iph + udphoff; 155 udph->source = cp->vport; 156 157 /* 158 * Adjust UDP checksums 159 */ 160 if (!cp->app && (udph->check != 0)) { 161 /* Only port and addr are changed, do fast csum update */ 162 udp_fast_csum_update(udph, cp->daddr, cp->vaddr, 163 cp->dport, cp->vport); 164 if ((*pskb)->ip_summed == CHECKSUM_HW) 165 (*pskb)->ip_summed = CHECKSUM_NONE; 166 } else { 167 /* full checksum calculation */ 168 udph->check = 0; 169 (*pskb)->csum = skb_checksum(*pskb, udphoff, 170 (*pskb)->len - udphoff, 0); 171 udph->check = csum_tcpudp_magic(cp->vaddr, cp->caddr, 172 (*pskb)->len - udphoff, 173 cp->protocol, 174 (*pskb)->csum); 175 if (udph->check == 0) 176 udph->check = 0xFFFF; 177 IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", 178 pp->name, udph->check, 179 (char*)&(udph->check) - (char*)udph); 180 } 181 return 1; 182} 183 184 185static int 186udp_dnat_handler(struct sk_buff **pskb, 187 struct ip_vs_protocol *pp, struct ip_vs_conn *cp) 188{ 189 struct udphdr *udph; 190 unsigned int udphoff = (*pskb)->nh.iph->ihl * 4; 191 192 /* csum_check requires unshared skb */ 193 if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph))) 194 return 0; 195 196 if (unlikely(cp->app != NULL)) { 197 /* Some checks before mangling */ 198 if (pp->csum_check && !pp->csum_check(*pskb, pp)) 199 return 0; 200 201 /* 202 * Attempt ip_vs_app call. 203 * It will fix ip_vs_conn 204 */ 205 if (!ip_vs_app_pkt_in(cp, pskb)) 206 return 0; 207 } 208 209 udph = (void *)(*pskb)->nh.iph + udphoff; 210 udph->dest = cp->dport; 211 212 /* 213 * Adjust UDP checksums 214 */ 215 if (!cp->app && (udph->check != 0)) { 216 /* Only port and addr are changed, do fast csum update */ 217 udp_fast_csum_update(udph, cp->vaddr, cp->daddr, 218 cp->vport, cp->dport); 219 if ((*pskb)->ip_summed == CHECKSUM_HW) 220 (*pskb)->ip_summed = CHECKSUM_NONE; 221 } else { 222 /* full checksum calculation */ 223 udph->check = 0; 224 (*pskb)->csum = skb_checksum(*pskb, udphoff, 225 (*pskb)->len - udphoff, 0); 226 udph->check = csum_tcpudp_magic(cp->caddr, cp->daddr, 227 (*pskb)->len - udphoff, 228 cp->protocol, 229 (*pskb)->csum); 230 if (udph->check == 0) 231 udph->check = 0xFFFF; 232 (*pskb)->ip_summed = CHECKSUM_UNNECESSARY; 233 } 234 return 1; 235} 236 237 238static int 239udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp) 240{ 241 struct udphdr _udph, *uh; 242 unsigned int udphoff = skb->nh.iph->ihl*4; 243 244 uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); 245 if (uh == NULL) 246 return 0; 247 248 if (uh->check != 0) { 249 switch (skb->ip_summed) { 250 case CHECKSUM_NONE: 251 skb->csum = skb_checksum(skb, udphoff, 252 skb->len - udphoff, 0); 253 case CHECKSUM_HW: 254 if (csum_tcpudp_magic(skb->nh.iph->saddr, 255 skb->nh.iph->daddr, 256 skb->len - udphoff, 257 skb->nh.iph->protocol, 258 skb->csum)) { 259 IP_VS_DBG_RL_PKT(0, pp, skb, 0, 260 "Failed checksum for"); 261 return 0; 262 } 263 break; 264 default: 265 /* CHECKSUM_UNNECESSARY */ 266 break; 267 } 268 } 269 return 1; 270} 271 272 273/* 274 * Note: the caller guarantees that only one of register_app, 275 * unregister_app or app_conn_bind is called each time. 276 */ 277 278#define UDP_APP_TAB_BITS 4 279#define UDP_APP_TAB_SIZE (1 << UDP_APP_TAB_BITS) 280#define UDP_APP_TAB_MASK (UDP_APP_TAB_SIZE - 1) 281 282static struct list_head udp_apps[UDP_APP_TAB_SIZE]; 283static DEFINE_SPINLOCK(udp_app_lock); 284 285static inline __u16 udp_app_hashkey(__u16 port) 286{ 287 return ((port >> UDP_APP_TAB_BITS) ^ port) & UDP_APP_TAB_MASK; 288} 289 290 291static int udp_register_app(struct ip_vs_app *inc) 292{ 293 struct ip_vs_app *i; 294 __u16 hash, port = inc->port; 295 int ret = 0; 296 297 hash = udp_app_hashkey(port); 298 299 300 spin_lock_bh(&udp_app_lock); 301 list_for_each_entry(i, &udp_apps[hash], p_list) { 302 if (i->port == port) { 303 ret = -EEXIST; 304 goto out; 305 } 306 } 307 list_add(&inc->p_list, &udp_apps[hash]); 308 atomic_inc(&ip_vs_protocol_udp.appcnt); 309 310 out: 311 spin_unlock_bh(&udp_app_lock); 312 return ret; 313} 314 315 316static void 317udp_unregister_app(struct ip_vs_app *inc) 318{ 319 spin_lock_bh(&udp_app_lock); 320 atomic_dec(&ip_vs_protocol_udp.appcnt); 321 list_del(&inc->p_list); 322 spin_unlock_bh(&udp_app_lock); 323} 324 325 326static int udp_app_conn_bind(struct ip_vs_conn *cp) 327{ 328 int hash; 329 struct ip_vs_app *inc; 330 int result = 0; 331 332 /* Default binding: bind app only for NAT */ 333 if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) 334 return 0; 335 336 /* Lookup application incarnations and bind the right one */ 337 hash = udp_app_hashkey(cp->vport); 338 339 spin_lock(&udp_app_lock); 340 list_for_each_entry(inc, &udp_apps[hash], p_list) { 341 if (inc->port == cp->vport) { 342 if (unlikely(!ip_vs_app_inc_get(inc))) 343 break; 344 spin_unlock(&udp_app_lock); 345 346 IP_VS_DBG(9, "%s: Binding conn %u.%u.%u.%u:%u->" 347 "%u.%u.%u.%u:%u to app %s on port %u\n", 348 __FUNCTION__, 349 NIPQUAD(cp->caddr), ntohs(cp->cport), 350 NIPQUAD(cp->vaddr), ntohs(cp->vport), 351 inc->name, ntohs(inc->port)); 352 cp->app = inc; 353 if (inc->init_conn) 354 result = inc->init_conn(inc, cp); 355 goto out; 356 } 357 } 358 spin_unlock(&udp_app_lock); 359 360 out: 361 return result; 362} 363 364 365static int udp_timeouts[IP_VS_UDP_S_LAST+1] = { 366 [IP_VS_UDP_S_NORMAL] = 5*60*HZ, 367 [IP_VS_UDP_S_LAST] = 2*HZ, 368}; 369 370static char * udp_state_name_table[IP_VS_UDP_S_LAST+1] = { 371 [IP_VS_UDP_S_NORMAL] = "UDP", 372 [IP_VS_UDP_S_LAST] = "BUG!", 373}; 374 375 376static int 377udp_set_state_timeout(struct ip_vs_protocol *pp, char *sname, int to) 378{ 379 return ip_vs_set_state_timeout(pp->timeout_table, IP_VS_UDP_S_LAST, 380 udp_state_name_table, sname, to); 381} 382 383static const char * udp_state_name(int state) 384{ 385 if (state >= IP_VS_UDP_S_LAST) 386 return "ERR!"; 387 return udp_state_name_table[state] ? udp_state_name_table[state] : "?"; 388} 389 390static int 391udp_state_transition(struct ip_vs_conn *cp, int direction, 392 const struct sk_buff *skb, 393 struct ip_vs_protocol *pp) 394{ 395 cp->timeout = pp->timeout_table[IP_VS_UDP_S_NORMAL]; 396 return 1; 397} 398 399static void udp_init(struct ip_vs_protocol *pp) 400{ 401 IP_VS_INIT_HASH_TABLE(udp_apps); 402 pp->timeout_table = udp_timeouts; 403} 404 405static void udp_exit(struct ip_vs_protocol *pp) 406{ 407} 408 409 410struct ip_vs_protocol ip_vs_protocol_udp = { 411 .name = "UDP", 412 .protocol = IPPROTO_UDP, 413 .dont_defrag = 0, 414 .init = udp_init, 415 .exit = udp_exit, 416 .conn_schedule = udp_conn_schedule, 417 .conn_in_get = udp_conn_in_get, 418 .conn_out_get = udp_conn_out_get, 419 .snat_handler = udp_snat_handler, 420 .dnat_handler = udp_dnat_handler, 421 .csum_check = udp_csum_check, 422 .state_transition = udp_state_transition, 423 .state_name = udp_state_name, 424 .register_app = udp_register_app, 425 .unregister_app = udp_unregister_app, 426 .app_conn_bind = udp_app_conn_bind, 427 .debug_packet = ip_vs_tcpudp_debug_packet, 428 .timeout_change = NULL, 429 .set_state_timeout = udp_set_state_timeout, 430};