1/* 2 * ip_vs_proto.c: transport protocol load balancing support for IPVS 3 * 4 * Version: $Id: ip_vs_proto.c,v 1.1.1.1 2007/08/03 18:53:52 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 42static struct ip_vs_protocol *ip_vs_proto_table[IP_VS_PROTO_TAB_SIZE]; 43 44 45/* 46 * register an ipvs protocol 47 */ 48static int register_ip_vs_protocol(struct ip_vs_protocol *pp) 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 */ 65static 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 */ 87struct 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 */ 104void 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 118int * 119ip_vs_create_timeout_table(int *table, int size) 120{ 121 return kmemdup(table, size, GFP_ATOMIC); 122} 123 124 125/* 126 * Set timeout value for state specified by name 127 */ 128int 129ip_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 146const 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 156void 157ip_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 { 173 __be16 _ports[2], *pptr 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 195int 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 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 225void 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} 236