varp.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,537 行 · 第 1/3 页
C
1,537 行
return err;}/** Send a varp request for the vnet and destination mac of a packet. * Assumes the ventry is locked. * * @param skb packet * @param vnet vnet (in network order) * @return 0 on success, error code otherwise */int varp_solicit(VnetId *vnet, Vmac *vmac){ return varp_send(VARP_OP_REQUEST, NULL, NULL, vnet, vmac);}/* Test some flags. * * @param ventry varp entry * @param flags to test * @return nonzero if flags set */int VarpEntry_get_flags(VarpEntry *ventry, int flags){ return ventry->flags & flags;}/** Set some flags. * * @param ventry varp entry * @param flags to set * @param set set flags on if nonzero, off if zero * @return new flags value */int VarpEntry_set_flags(VarpEntry *ventry, int flags, int set){ if(set){ ventry->flags |= flags; } else { ventry->flags &= ~flags; } return ventry->flags;}/** Print a varp entry. * * @param ventry varp entry */void VarpEntry_print(VarpEntry *ventry, IOStream *io){ IOStream_print(io, "(ventry \n"); if(ventry){ unsigned long now = jiffies; char *state, *flags; char vnetbuf[VNET_ID_BUF]; char addrbuf[VARP_ADDR_BUF]; switch(ventry->state){ case VARP_STATE_INCOMPLETE: state = "incomplete"; break; case VARP_STATE_REACHABLE: state = "reachable"; break; case VARP_STATE_FAILED: state = "failed"; break; default: state = "unknown"; break; } flags = (VarpEntry_get_flags(ventry, VARP_FLAG_PROBING) ? "P" : "-"); IOStream_print(io, " (ref %d)\n", atomic_read(&ventry->refcount)); IOStream_print(io, " (state %s)\n", state); IOStream_print(io, " (flags %s)\n", flags); IOStream_print(io, " (addr %s)\n", VarpAddr_ntoa(&ventry->addr, addrbuf)); IOStream_print(io, " (queue %d)\n", skb_queue_len(&ventry->queue)); IOStream_print(io, " (age %lu)\n", now - ventry->timestamp); IOStream_print(io, " (vmac " MACFMT ")\n", MAC6TUPLE(ventry->key.vmac.mac)); IOStream_print(io, " (vnet %s)\n", VnetId_ntoa(&ventry->key.vnet, vnetbuf)); } IOStream_print(io, ")\n");}/** Free a varp entry. * * @param ventry varp entry */static void VarpEntry_free(VarpEntry *ventry){ if(!ventry) return; deallocate(ventry);}/** Increment reference count. * * @param ventry varp entry (may be null) */void VarpEntry_incref(VarpEntry *ventry){ if(!ventry) return; atomic_inc(&ventry->refcount);}/** Decrement reference count, freeing if zero. * * @param ventry varp entry (may be null) */void VarpEntry_decref(VarpEntry *ventry){ if(!ventry) return; if(atomic_dec_and_test(&ventry->refcount)){ VarpEntry_free(ventry); }}/** Call the error handler. * * @param ventry varp entry */void VarpEntry_error(VarpEntry *ventry){ struct sk_buff *skb; skb = skb_peek(&ventry->queue); if(!skb) return; if(ventry->error) ventry->error(ventry, skb); skb_queue_purge(&ventry->queue);}/** Schedule the varp entry timer. * Must increment the reference count before doing * this the first time, so the ventry won't be freed * before the timer goes off. * * @param ventry varp entry */void VarpEntry_schedule(VarpEntry *ventry){ timer_set(&ventry->timer, VARP_PROBE_INTERVAL);}/** Function called when a varp entry timer goes off. * If the entry is still incomplete, carries on probing. * Otherwise stops probing. * * @param arg ventry */static void varp_timer_fn(unsigned long arg){ unsigned long flags; VarpEntry *ventry = (VarpEntry *)arg; struct sk_buff *skb = NULL; int probing = 0; dprintf(">\n"); VarpEntry_lock(ventry, flags); if(!atomic_read(&ventry->deleted)){ switch(ventry->state){ case VARP_STATE_REACHABLE: case VARP_STATE_FAILED: break; case VARP_STATE_INCOMPLETE: // Probe if haven't run out of tries, otherwise fail. if(atomic_read(&ventry->probes) < VARP_PROBE_MAX){ unsigned long qflags; VnetId vnet; Vmac vmac; probing = 1; spin_lock_irqsave(&ventry->queue.lock, qflags); skb = skb_peek(&ventry->queue); if(skb){ vmac = *(Vmac*)eth_hdr(skb)->h_dest; } spin_unlock_irqrestore(&ventry->queue.lock, qflags); if(skb){ dprintf("> skbs in queue - solicit\n"); vnet = ventry->key.vnet; atomic_inc(&ventry->probes); VarpEntry_unlock(ventry, flags); varp_solicit(&vnet, &vmac); VarpEntry_lock(ventry, flags); } else { dprintf("> empty queue.\n"); } VarpEntry_schedule(ventry); } else { VarpEntry_error(ventry); ventry->state = VARP_STATE_FAILED; } break; } } VarpEntry_set_flags(ventry, VARP_FLAG_PROBING, probing); VarpEntry_unlock(ventry, flags); if(!probing) VarpEntry_decref(ventry); dprintf("<\n");}/** Default error function for varp entries. * * @param ventry varp entry * @param skb packet dropped because of error */static void varp_error_fn(VarpEntry *ventry, struct sk_buff *skb){}/** Create a varp entry. Initializes the internal state. * * @param vnet vnet id * @param vmac virtual MAC address (copied) * @return ventry or null */VarpEntry * VarpEntry_new(VnetId *vnet, Vmac *vmac){ VarpEntry *ventry = ALLOCATE(VarpEntry); if(ventry){ unsigned long now = jiffies; atomic_set(&ventry->refcount, 1); atomic_set(&ventry->probes, 0); atomic_set(&ventry->deleted, 0); ventry->lock = RW_LOCK_UNLOCKED; ventry->state = VARP_STATE_INCOMPLETE; ventry->queue_max = VARP_QUEUE_MAX; skb_queue_head_init(&ventry->queue); timer_init(&ventry->timer, varp_timer_fn, ventry); ventry->timestamp = now; ventry->error = varp_error_fn; ventry->key.vnet = *vnet; ventry->key.vmac = *vmac; } return ventry;}/** Hash function for keys in the varp cache. * Hashes the vnet id and mac. * * @param k key (VarpKey) * @return hashcode */static Hashcode varp_key_hash_fn(void *k){ return hash_hvoid(0, k, sizeof(VarpKey));}/** Test equality for keys in the varp cache. * Compares vnet and mac. * * @param k1 key to compare (VarpKey) * @param k2 key to compare (VarpKey) * @return 1 if equal, 0 otherwise */static int varp_key_equal_fn(void *k1, void *k2){ return memcmp(k1, k2, sizeof(VarpKey)) == 0;}/** Free an entry in the varp cache. * * @param table containing table * @param entry entry to free */static void varp_entry_free_fn(HashTable *table, HTEntry *entry){ VarpEntry *ventry; if(!entry) return; ventry = entry->value; if(ventry) VarpEntry_decref(ventry); HTEntry_free(entry);}/** Free the whole varp cache. * Dangerous. * * @param vtable varp cache */void VarpTable_free(VarpTable *vtable){ unsigned long vtflags; if(!vtable) return; VarpTable_write_lock(vtable, vtflags); timer_cancel(&vtable->timer); vtable->timer.data = 0; if(vtable->table){ HashTable *table = vtable->table; HashTable_for_decl(entry); vtable->table = NULL; HashTable_for_each(entry, table){ VarpEntry *ventry = entry->value; unsigned long flags; VarpEntry_lock(ventry, flags); atomic_set(&ventry->deleted, 1); if(VarpEntry_get_flags(ventry, VARP_FLAG_PROBING)){ timer_cancel(&ventry->timer); ventry->timer.data = 0; VarpEntry_decref(ventry); } VarpEntry_unlock(ventry, flags); } HashTable_free(table); } VarpTable_write_unlock(vtable, vtflags); deallocate(vtable);}/** Schedule the varp table timer. * * @param vtable varp table */void VarpTable_schedule(VarpTable *vtable){ timer_set(&vtable->timer, vtable->entry_ttl);}/** Function called when the varp table timer goes off. * Sweeps old varp cache entries and reschedules itself. * * @param arg varp table */static void varp_table_timer_fn(unsigned long arg){ VarpTable *vtable = (VarpTable *)arg; if(vtable){ VarpTable_sweep(vtable); VarpTable_schedule(vtable); }}/** Print a varp table. * * @param vtable table */void VarpTable_print(VarpTable *vtable, IOStream *io){ HashTable_for_decl(entry); VarpEntry *ventry; unsigned long vtflags, flags; VarpTable_read_lock(vtable, vtflags); HashTable_for_each(entry, vtable->table){ ventry = entry->value; VarpEntry_lock(ventry, flags); VarpEntry_print(ventry, io); VarpEntry_unlock(ventry, flags); } VarpTable_read_unlock(vtable, vtflags);}/** Create a varp table. * * @return new table or null */VarpTable * VarpTable_new(void){ int err = -ENOMEM; VarpTable *vtable = NULL; vtable = ALLOCATE(VarpTable); if(!vtable) goto exit; vtable->table = HashTable_new(VARP_TABLE_BUCKETS); if(!vtable->table) goto exit; vtable->table->key_size = sizeof(VarpKey); vtable->table->key_equal_fn = varp_key_equal_fn; vtable->table->key_hash_fn = varp_key_hash_fn; vtable->table->entry_free_fn = varp_entry_free_fn; vtable->entry_ttl = VARP_ENTRY_TTL; vtable->probe_max = VARP_PROBE_MAX; vtable->probe_interval = VARP_PROBE_INTERVAL; vtable->queue_max = VARP_QUEUE_MAX; init_MUTEX(&vtable->mutex); vtable->lock = RW_LOCK_UNLOCKED; timer_init(&vtable->timer, varp_table_timer_fn, vtable); err = 0; exit: if(err){ VarpTable_free(vtable); vtable = NULL; } return vtable;}/** Add a new entry to the varp table. * * @param vtable table * @param vnet vnet id * @param vmac virtual MAC address (copied) * @return new entry or null */VarpEntry * VarpTable_add(VarpTable *vtable, VnetId *vnet, Vmac *vmac){ int err = 0; VarpKey key = { .vnet = *vnet, .vmac = *vmac}; VarpEntry *ventry = NULL; HTEntry *entry = NULL; unsigned long vtflags; VarpTable_write_lock(vtable, vtflags); ventry = HashTable_get(vtable->table, &key); if(ventry){ VarpEntry_incref(ventry); goto exit; } err = -ENOMEM; ventry = VarpEntry_new(vnet, vmac); if(!ventry) goto exit; entry = HashTable_add(vtable->table, ventry, ventry); if(!entry){ VarpEntry_decref(ventry); ventry = NULL; goto exit; } err = 0; VarpEntry_incref(ventry); exit: VarpTable_write_unlock(vtable, vtflags); return ventry;}/** Remove an entry from the varp table. * * @param vtable table * @param ventry entry to remove * @return removed count */int VarpTable_remove(VarpTable *vtable, VarpEntry *ventry){ //TODO: Could send a varp announce with null addr for the entry // vnet and vmac to notify others, so they will resolve the addr // instead of sending traffic to us. atomic_set(&ventry->deleted, 1); skb_queue_purge(&ventry->queue); return HashTable_remove(vtable->table, ventry);}/** Remove all entries using a vnet. * Caller must hold the table lock. * * @param vtable table * @param vnet vnet * @return removed count */int VarpTable_remove_vnet(VarpTable *vtable, VnetId *vnet){ int count = 0; HashTable_for_decl(entry); HashTable_for_each(entry, vtable->table){ VarpEntry *ventry = entry->value; if(VnetId_eq(&ventry->key.vnet, vnet)){ count += VarpTable_remove(vtable, ventry); } } return count;}/** Remove all entries using a vnet from the varp table. * * @param vnet vnet * @return removed count */int varp_remove_vnet(VnetId *vnet){ int count = 0; unsigned long vtflags; VarpTable_write_lock(varp_table, vtflags); count = VarpTable_remove_vnet(varp_table, vnet); VarpTable_write_unlock(varp_table, vtflags); return count;}/** Lookup an entry in the varp table. * * @param vtable table * @param vnet vnet id * @param vmac virtual MAC address * @param create create a new entry if needed if true * @return entry found or null */VarpEntry * VarpTable_lookup(VarpTable *vtable, VnetId *vnet, Vmac *vmac, int create){ VarpKey key = { .vnet = *vnet, .vmac = *vmac }; VarpEntry *ventry = NULL; unsigned long vtflags; VarpTable_read_lock(vtable, vtflags); ventry = HashTable_get(vtable->table, &key); if(ventry) VarpEntry_incref(ventry); VarpTable_read_unlock(vtable, vtflags); if(!ventry && create){ ventry = VarpTable_add(vtable, vnet, vmac); } return ventry;}/** Handle output for a reachable ventry. * Send the skb using the tunnel to the care-of address. * Assumes the ventry lock is held. * * @param ventry varp entry * @param skb skb to send * @return 0 on success, error code otherwise */int VarpEntry_send(VarpEntry *ventry, struct sk_buff *skb){ int err = 0; unsigned long flags = 0; VarpAddr addr; VnetId vnet; dprintf("> skb=%p\n", skb); vnet = ventry->key.vnet; addr = ventry->addr; VarpEntry_unlock(ventry, flags); err = vnet_tunnel_send(&vnet, &addr, skb); VarpEntry_lock(ventry, flags); dprintf("< err=%d\n", err); return err;}/** Handle output for a non-reachable ventry. Send messages to complete it. * If the entry is still incomplete, queue the skb, otherwise * send it. If the queue is full, dequeue and free an old skb to * make room for the new one. * Assumes the ventry lock is held. * * @param ventry varp entry * @param skb skb to send * @return 0 on success, error code otherwise */int VarpEntry_resolve(VarpEntry *ventry, struct sk_buff *skb){ int err = 0; unsigned long flags = 0; VnetId vnet; Vmac vmac; dprintf("> skb=%p\n", skb); ventry->state = VARP_STATE_INCOMPLETE; atomic_set(&ventry->probes, 1);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?