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 + -
显示快捷键?