ia64/xen-unstable

view tools/vnet/vnetd/vcache.c @ 3498:1d24a5b0b338

bitkeeper revision 1.1159.223.25 (41f2cb9aEKMZkZbvqBE0eXhpljlV4Q)

Description: fix path to python
There is no python2 in debian. Instead, use python.

From: Adam Heath <doogie@brainfood.com>
Signed-off-by: ian.pratt@cl.cam.ac.uk
author iap10@labyrinth.cl.cam.ac.uk
date Sat Jan 22 21:54:34 2005 +0000 (2005-01-22)
parents f65b65977b19
children b5019559f1ca 736089c11af9
line source
1 /*
2 * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>.
3 *
4 * This library is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2.1 of the
7 * License, or (at your option) any later version. This library is
8 * distributed in the hope that it will be useful, but WITHOUT ANY
9 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE.
11 * See the GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <time.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <string.h>
30 #include "allocate.h"
31 #include "hash_table.h"
32 #include "sys_net.h"
33 #include "sys_string.h"
34 #include "connection.h"
35 #include "marshal.h"
36 #include "timer.h"
38 #undef offsetof
39 #include "vnetd.h"
40 #include "vcache.h"
42 #define MODULE_NAME "VARP"
43 #define DEBUG 1
44 #undef DEBUG
45 #include "debug.h"
47 static VarpCache *vcache = NULL;
49 void IPMessageQueue_init(IPMessageQueue *queue, int maxlen){
50 queue->msg = NULL;
51 queue->len = 0;
52 queue->maxlen = maxlen;
53 }
55 void IPMessageQueue_clear(IPMessageQueue *queue){
56 queue->msg = NULL;
57 queue->len = 0;
58 }
60 void IPMessageQueue_truncate(IPMessageQueue *queue, int n){
61 IPMessage **p = &queue->msg;
62 int i;
63 for(i = 1; *p; p = &(*p)->next, i++){
64 if(i == n){
65 *p = NULL;
66 break;
67 }
68 }
69 }
71 void IPMessageQueue_add(IPMessageQueue *queue, IPMessage *msg){
72 msg->next = queue->msg;
73 queue->msg = msg;
74 queue->len++;
75 if(queue->len >= queue->maxlen){
76 IPMessageQueue_truncate(queue, queue->maxlen);
77 }
78 }
80 IPMessage * IPMessageQueue_pop(IPMessageQueue *queue){
81 IPMessage *msg = NULL;
82 if(queue->len > 0){
83 queue->len--;
84 msg = queue->msg;
85 queue->msg = msg->next;
86 msg->next = NULL;
87 }
88 return msg;
89 }
91 void VarpCache_sweep(VarpCache *z, int all);
93 /** Send a varp protocol message.
94 *
95 * @param opcode varp opcode (host order)
96 * @param vnet vnet id (in network order)
97 * @param vmac vmac (in network order)
98 * @return 0 on success, error code otherwise
99 */
100 int varp_send(Conn *conn, uint16_t opcode, uint32_t vnet, Vmac *vmac, uint32_t addr){
101 int err = 0;
102 int varp_n = sizeof(VarpHdr);
103 VarpHdr varph = {};
105 varph.id = htons(VARP_ID);
106 varph.opcode = htons(opcode);
107 varph.vnet = vnet;
108 varph.vmac = *vmac;
109 varph.addr = addr;
111 if(0){
112 struct sockaddr_in self;
113 socklen_t self_n;
114 getsockname(conn->sock, (struct sockaddr *)&self, &self_n);
115 dprintf("> sockname addr=%s port=%d\n",
116 inet_ntoa(self.sin_addr), ntohs(self.sin_port));
117 }
118 dprintf("> addr=%s opcode=%d\n",
119 inet_ntoa(conn->addr.sin_addr), opcode);
120 dprintf("> vnet=%d vmac=" MACFMT " addr=" IPFMT "\n",
121 ntohl(vnet), MAC6TUPLE(vmac->mac), NIPQUAD(addr));
122 err = marshal_bytes(conn->out, &varph, varp_n);
123 marshal_flush(conn->out);
124 dprintf("< err=%d\n", err);
125 return err;
126 }
128 /* Test some flags.
129 *
130 * @param z varp entry
131 * @param flags to test
132 * @return nonzero if flags set
133 */
134 int VCEntry_get_flags(VCEntry *z, int flags){
135 return z->flags & flags;
136 }
138 /** Set some flags.
139 *
140 * @param z varp entry
141 * @param flags to set
142 * @param set set flags on if nonzero, off if zero
143 * @return new flags value
144 */
145 int VCEntry_set_flags(VCEntry *z, int flags, int set){
146 if(set){
147 z->flags |= flags;
148 } else {
149 z->flags &= ~flags;
150 }
151 return z->flags;
152 }
154 /** Print a varp entry.
155 *
156 * @param ventry varp entry
157 */
158 void VCEntry_print(VCEntry *ventry){
159 if(ventry){
160 char *c, *d;
161 switch(ventry->state){
162 case VCACHE_STATE_INCOMPLETE: c = "INC"; break;
163 case VCACHE_STATE_REACHABLE: c = "RCH"; break;
164 case VCACHE_STATE_FAILED: c = "FLD"; break;
165 default: c = "UNK"; break;
166 }
167 d = (VCEntry_get_flags(ventry, VCACHE_FLAG_PROBING) ? "P" : " ");
169 printf("VENTRY(%p %s %s vnet=%d vmac=" MACFMT " addr=" IPFMT " time=%g)\n",
170 ventry,
171 c, d,
172 ntohl(ventry->key.vnet),
173 MAC6TUPLE(ventry->key.vmac.mac),
174 NIPQUAD(ventry->addr),
175 ventry->timestamp);
176 } else {
177 printf("VENTRY: Null!\n");
178 }
179 }
181 int VCEntry_schedule(VCEntry *ventry);
182 void VCEntry_solicit(VCEntry *ventry);
184 /** Function called when a varp entry timer goes off.
185 * If the entry is still incomplete, carries on probing.
186 * Otherwise stops probing.
187 *
188 * @param arg ventry
189 */
190 static void ventry_timer_fn(Timer *timer){
191 VCEntry *ventry = timer->data;
192 int probing = 0, scheduled = 0;
194 //dprintf(">\n"); VCEntry_print(ventry);
195 if(ventry->state == VCACHE_STATE_REACHABLE){
196 // Do nothing.
197 } else {
198 // Probe if haven't run out of tries, otherwise fail.
199 if(ventry->probes < VCACHE_PROBE_MAX){
200 //probing = 1;
201 ventry->probes++;
202 scheduled = VCEntry_schedule(ventry);
203 //VCEntry_solicit(ventry);
204 probing = scheduled;
205 } else {
206 ventry->state = VCACHE_STATE_FAILED;
207 IPMessageQueue_clear(&ventry->queue);
208 }
209 }
210 if(!probing){
211 VCEntry_set_flags(ventry,
212 (VCACHE_FLAG_PROBING
213 | VCACHE_FLAG_REMOTE_PROBE
214 | VCACHE_FLAG_LOCAL_PROBE),
215 0);
216 }
217 VCEntry_set_flags(ventry, VCACHE_FLAG_PROBING, probing);
218 //dprintf("<\n");
219 }
221 /** Schedule the varp entry timer.
222 *
223 * @param ventry varp entry
224 */
225 int VCEntry_schedule(VCEntry *ventry){
226 int scheduled = 0;
227 if(ventry->probes == 1){
228 scheduled = 1;
229 Timer_set(VCACHE_LOCAL_DELAY, ventry_timer_fn, ventry);
230 } else {
231 VCEntry_solicit(ventry);
232 }
233 return scheduled;
234 }
236 /** Create a varp entry. Initializes the internal state.
237 *
238 * @param vnet vnet id
239 * @param vmac virtual MAC address (copied)
240 * @return ventry or null
241 */
242 VCEntry * VCEntry_new(uint32_t vnet, Vmac *vmac){
243 VCEntry *z = ALLOCATE(VCEntry);
244 z->state = VCACHE_STATE_INCOMPLETE;
245 z->timestamp = time_now();
246 z->key.vnet = vnet;
247 z->key.vmac = *vmac;
248 return z;
249 }
251 /** Hash function for keys in the varp cache.
252 * Hashes the vnet id and mac.
253 *
254 * @param k key (VCKey)
255 * @return hashcode
256 */
257 Hashcode vcache_key_hash_fn(void *k){
258 VCKey *key = k;
259 Hashcode h;
260 h = hash_2ul(key->vnet,
261 (key->vmac.mac[0] << 24) |
262 (key->vmac.mac[1] << 16) |
263 (key->vmac.mac[2] << 8) |
264 (key->vmac.mac[3] ));
265 h = hash_hul(h,
266 (key->vmac.mac[4] << 8) |
267 (key->vmac.mac[5] ));
268 return h;
269 }
271 /** Test equality for keys in the varp cache.
272 * Compares vnet and mac.
273 *
274 * @param k1 key to compare (VCKey)
275 * @param k2 key to compare (VCKey)
276 * @return 1 if equal, 0 otherwise
277 */
278 int vcache_key_equal_fn(void *k1, void *k2){
279 VCKey *key1 = k1;
280 VCKey *key2 = k2;
281 return (key1->vnet == key2->vnet)
282 && (memcmp(key1->vmac.mac, key2->vmac.mac, ETH_ALEN) == 0);
283 }
285 void VarpCache_schedule(VarpCache *z);
287 /** Function called when the varp table timer goes off.
288 * Sweeps old varp cache entries and reschedules itself.
289 *
290 * @param arg varp table
291 */
292 static void vcache_timer_fn(Timer *timer){
293 VarpCache *z = timer->data;
294 //dprintf("> z=%p\n", z);
295 if(z){
296 VarpCache_sweep(z, 0);
297 VarpCache_schedule(z);
298 }
299 //dprintf("<\n");
300 }
302 /** Schedule the varp table timer.
303 *
304 * @param z varp table
305 */
306 void VarpCache_schedule(VarpCache *z){
307 Timer_set(VCACHE_ENTRY_TTL, vcache_timer_fn, z);
308 }
310 /** Print a varp table.
311 *
312 * @param z table
313 */
314 void VarpCache_print(VarpCache *z){
315 HashTable_for_decl(entry);
316 VCEntry *ventry;
318 dprintf(">\n");
319 HashTable_for_each(entry, vcache->table){
320 ventry = entry->value;
321 VCEntry_print(ventry);
322 }
323 dprintf("<\n");
324 }
326 /** Print the varp cache.
327 */
328 void vcache_print(void){
329 VarpCache_print(vcache);
330 }
332 /** Create a varp table.
333 *
334 * @return new table or null
335 */
336 VarpCache * VarpCache_new(void){
337 VarpCache *z = NULL;
339 z = ALLOCATE(VarpCache);
340 z->table = HashTable_new(VCACHE_BUCKETS);
341 z->table->key_equal_fn = vcache_key_equal_fn;
342 z->table->key_hash_fn = vcache_key_hash_fn;
343 VarpCache_schedule(z);
344 return z;
345 }
347 /** Add a new entry to the varp table.
348 *
349 * @param z table
350 * @param vnet vnet id
351 * @param vmac virtual MAC address (copied)
352 * @return new entry or null
353 */
354 VCEntry * VarpCache_add(VarpCache *z, uint32_t vnet, Vmac *vmac){
355 VCEntry *ventry;
356 HTEntry *entry;
358 ventry = VCEntry_new(vnet, vmac);
359 //dprintf("> "); VCEntry_print(ventry);
360 entry = HashTable_add(z->table, ventry, ventry);
361 return ventry;
362 }
364 /** Remove an entry from the varp table.
365 *
366 * @param z table
367 * @param ventry entry to remove
368 * @return removed count
369 */
370 int VarpCache_remove(VarpCache *z, VCEntry *ventry){
371 return HashTable_remove(z->table, ventry);
372 }
374 /** Lookup an entry in the varp table.
375 *
376 * @param z table
377 * @param vnet vnet id
378 * @param vmac virtual MAC addres
379 * @return entry found or null
380 */
381 VCEntry * VarpCache_lookup(VarpCache *z, uint32_t vnet, Vmac *vmac){
382 VCKey key = { .vnet = vnet, .vmac = *vmac };
383 VCEntry *ventry;
384 ventry = HashTable_get(z->table, &key);
385 return ventry;
386 }
388 void VCEntry_solicit(VCEntry *ventry){
389 dprintf(">\n");
390 if(VCEntry_get_flags(ventry, VCACHE_FLAG_LOCAL_PROBE)){
391 dprintf("> local probe\n");
392 varp_send(vnetd->bcast_conn, VARP_OP_REQUEST, ventry->key.vnet, &ventry->key.vmac, ventry->addr);
393 }
394 if(VCEntry_get_flags(ventry, VCACHE_FLAG_REMOTE_PROBE)){
395 ConnList *l;
396 dprintf("> remote probe\n");
397 for(l = vnetd->connections; l; l = l->next){
398 varp_send(l->conn, VARP_OP_REQUEST, ventry->key.vnet, &ventry->key.vmac, ventry->addr);
399 }
401 }
402 dprintf("<\n");
403 }
405 int VCEntry_resolve(VCEntry *ventry, IPMessage *msg, int flags){
406 int err = 0;
408 dprintf("> "); //VCEntry_print(ventry);
409 ventry->state = VCACHE_STATE_INCOMPLETE;
410 VCEntry_set_flags(ventry, flags, 1);
411 IPMessageQueue_add(&ventry->queue, msg);
412 if(!VCEntry_get_flags(ventry, VCACHE_FLAG_PROBING)){
413 VCEntry_set_flags(ventry, VCACHE_FLAG_PROBING, 1);
414 ventry->probes = 1;
415 VCEntry_schedule(ventry);
416 //VCEntry_solicit(ventry);
417 }
418 dprintf("< err=%d\n", err);
419 return err;
420 }
422 /** Update a ventry. Sets the address and state to those given
423 * and sets the timestamp to 'now'.
424 *
425 * @param ventry varp entry
426 * @param addr care-of address
427 * @param state state
428 * @return 0 on success, error code otherwise
429 */
430 int VCEntry_update(VCEntry *ventry, IPMessage *msg, VarpHdr *varph, int state){
431 int err = 0;
432 double now = time_now();
434 if(VCEntry_get_flags(ventry, VCACHE_FLAG_PERMANENT)) goto exit;
435 ventry->addr = varph->addr;
436 ventry->timestamp = now;
437 ventry->state = state;
438 if(ventry->state == VCACHE_STATE_REACHABLE){
439 // Process the output queue.
440 IPMessage *msg;
441 while((msg = IPMessageQueue_pop(&ventry->queue))){
442 dprintf("> announce\n");
443 varp_send(msg->conn, VARP_OP_ANNOUNCE, ventry->key.vnet, &ventry->key.vmac, ventry->addr);
444 }
445 }
446 exit:
447 return err;
448 }
450 /** Update the ventry corresponding to the given varp header.
451 *
452 * @param z table
453 * @param varph varp header
454 * @param state state
455 * @return 0 on success, -ENOENT if no entry found
456 */
457 int VarpCache_update(VarpCache *z, IPMessage *msg, VarpHdr *varph, int state){
458 int err = 0;
459 VCEntry *ventry;
461 dprintf(">\n");
462 ventry = VarpCache_lookup(z, varph->vnet, &varph->vmac);
463 if(ventry){
464 err = VCEntry_update(ventry, msg, varph, state);
465 } else {
466 err = -ENOENT;
467 }
468 dprintf("< err=%d\n", err);
469 return err;
470 }
473 /** Put old varp entries into the incomplete state.
474 * Permanent entries are not changed.
475 * If 'all' is non-zero, all non-permanent entries
476 * are put into the incomplete state, regardless of age.
477 *
478 * @param z table
479 * @param all reset all entries if non-zero
480 */
481 void VarpCache_sweep(VarpCache *z, int all){
482 HashTable_for_decl(entry);
483 VCEntry *ventry;
484 double now = time_now();
485 double old = now - VCACHE_ENTRY_TTL;
487 dprintf(">\n");
488 HashTable_for_each(entry, vcache->table){
489 ventry = entry->value;
490 if(!VCEntry_get_flags(ventry, VCACHE_FLAG_PERMANENT) &&
491 (all || (ventry->timestamp < old))){
492 ventry->state = VCACHE_STATE_INCOMPLETE;
493 }
494 }
495 dprintf("<\n");
496 }
498 /** Forward a varp message.
499 * If local forwards it to remote vnetds.
500 * If not local forwards it to local net.
501 *
502 * @param varph varp message to forward
503 * @param local whether it's local or not
504 */
505 void vcache_forward_varp(VarpHdr *varph, int local){
506 uint16_t opcode = ntohs(varph->opcode);
507 if(local){
508 ConnList *l;
509 for(l = vnetd->connections; l; l = l->next){
510 varp_send(l->conn, opcode, varph->vnet, &varph->vmac, varph->addr);
511 }
512 } else {
513 varp_send(vnetd->bcast_conn, opcode, varph->vnet, &varph->vmac, varph->addr);
514 }
515 }
517 /** Handle a varp request.
518 *
519 * @param msg incoming message
520 * @param varph varp message
521 * @return 0 if ok, -ENOENT if no matching vif, or error code
522 */
523 #if 1
524 int vcache_handle_request(IPMessage *msg, VarpHdr *varph, int local){
525 dprintf("> local=%d\n", local);
526 vcache_forward_varp(varph, local);
527 dprintf("<\n");
528 return 0;
529 }
531 #else
532 int vcache_handle_request(IPMessage *msg, VarpHdr *varph, int local){
533 int err = -ENOENT;
534 uint32_t vnet;
535 Vmac *vmac;
536 VCEntry *ventry = NULL;
537 int reply = 0;
539 dprintf(">\n");
540 vnet = htonl(varph->vnet);
541 vmac = &varph->vmac;
542 ventry = VarpCache_lookup(vcache, vnet, vmac);
543 if(!ventry){
544 ventry = VarpCache_add(vcache, vnet, vmac);
545 }
546 if(local){
547 // Request coming from the local subnet (on our udp port).
548 if(ventry->state == VCACHE_STATE_REACHABLE){
549 if(local){
550 // Have an entry, and it's non-local - reply (locally).
551 // Potential out-of-date cache problem.
552 // Should query remotely instead of replying.
553 varp_send(conn, VARP_OP_ANNOUNCE, ventry);
554 }
555 } else {
556 // Incomplete entry. Resolve.
557 VCEntry_resolve(ventry, msg, VCACHE_FLAG_REMOTE_PROBE);
558 }
559 } else {
560 // Non-local request (on one of our tcp connetions).
561 if(ventry->state == VCACHE_STATE_REACHABLE){
562 if(local){
563 // Have an entry and it's local - reply (remotely).
564 // Potential out-of-date cache problem.
565 // Should query locally instead of replying.
566 varp_send(msg->conn, VARP_OP_ANNOUNCE, ventry);
567 } else {
568 // Have a non-local entry - do nothing and assume someone else
569 // will reply.
570 }
571 } else {
572 // Incomplete entry. Resolve.
573 VCEntry_resolve(ventry, msg, VCACHE_FLAG_LOCAL_PROBE);
574 }
575 }
576 exit:
577 dprintf("< err=%d\n", err);
578 return err;
579 }
580 #endif
582 /** Handle a varp announce message.
583 * Update the matching ventry if we have one.
584 *
585 * @param msg incoming message
586 * @param varp message
587 * @return 0 if OK, -ENOENT if no matching entry
588 */
589 int vcache_handle_announce(IPMessage *msg, VarpHdr *varph, int local){
590 int err = 0;
592 vcache_forward_varp(varph, local);
593 err = VarpCache_update(vcache, msg, varph, VCACHE_STATE_REACHABLE);
594 return err;
595 }
597 /** Handle an incoming varp message.
598 *
599 * @param msg incoming message
600 * @return 0 if OK, error code otherwise
601 */
602 int vcache_handle_message(IPMessage *msg, int local){
603 int err = -EINVAL;
604 VnetMsg *vmsg = msg->data;
605 VarpHdr *varph = &vmsg->varp.varph;
607 dprintf(">\n");
608 if(1){
609 dprintf("> src=%s:%d\n", inet_ntoa(msg->saddr.sin_addr), ntohs(msg->saddr.sin_port));
610 dprintf("> dst=%s:%d\n", inet_ntoa(msg->daddr.sin_addr), ntohs(msg->daddr.sin_port));
611 dprintf("> opcode=%d vnet=%u vmac=" MACFMT "\n",
612 ntohs(varph->opcode), ntohl(varph->vnet), MAC6TUPLE(varph->vmac.mac));
613 }
614 switch(ntohs(varph->opcode)){
615 case VARP_OP_REQUEST:
616 err = vcache_handle_request(msg, varph, local);
617 break;
618 case VARP_OP_ANNOUNCE:
619 err = vcache_handle_announce(msg, varph, local);
620 break;
621 default:
622 break;
623 }
624 dprintf("< err=%d\n", err);
625 return err;
626 }
628 /** Initialize the varp cache.
629 *
630 * @return 0 on success, error code otherwise
631 */
632 int vcache_init(void){
633 int err = 0;
635 if(!vcache){
636 vcache = VarpCache_new();
637 }
638 return err;
639 }