varp.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,537 行 · 第 1/3 页

C
1,537
字号
    if(!VarpEntry_get_flags(ventry, VARP_FLAG_PROBING)){        VarpEntry_set_flags(ventry, VARP_FLAG_PROBING, 1);        VarpEntry_incref(ventry);        VarpEntry_schedule(ventry);    }    vnet = ventry->key.vnet;    vmac = *(Vmac*)eth_hdr(skb)->h_dest;    VarpEntry_unlock(ventry, flags);    varp_solicit(&vnet, &vmac);    VarpEntry_lock(ventry, flags);    if(ventry->state == VARP_STATE_INCOMPLETE){        while(skb_queue_len(&ventry->queue) >= ventry->queue_max){            struct sk_buff *oldskb;            oldskb = skb_dequeue(&ventry->queue);            //oldskb = ventry->queue.next;            //__skb_unlink(oldskb, &ventry->queue);            if(!oldskb) break;            dprintf("> dropping skb=%p\n", oldskb);            kfree_skb(oldskb);        }        skb_queue_tail(&ventry->queue, skb);    } else {        err = VarpEntry_send(ventry, skb);    }    dprintf("< err=%d\n", err);    return err;}/** Process the output queue for a ventry.  Sends the queued skbs if * the ventry is reachable, otherwise drops them. * * @param ventry varp entry */void VarpEntry_process_queue(VarpEntry *ventry){    struct sk_buff *skb;    for( ; ; ){        if(ventry->state != VARP_STATE_REACHABLE) break;        skb = skb_dequeue(&ventry->queue);        if(!skb) break;        VarpEntry_send(ventry, skb);    }    skb_queue_purge(&ventry->queue);}/** Multicast an skb on a vnet. * * @param vnet vnet id * @param skb skb to send * @return 0 on success, error code otherwise */static int varp_multicast(VnetId *vnet, struct sk_buff *skb){    VarpAddr addr = { .family = AF_INET };    addr.u.ip4.s_addr = varp_mcast_addr;    return vnet_tunnel_send(vnet, &addr, skb);}/** Handle output for a ventry. Resolves the ventry * if necessary. * * @param ventry varp entry * @param skb skb to send * @return 0 on success, error code otherwise */int VarpEntry_output(VarpEntry *ventry, struct sk_buff *skb){    int err = 0;    unsigned long flags;    VarpEntry_lock(ventry, flags);    switch(ventry->state){    case VARP_STATE_REACHABLE:        if(skb_queue_len(&ventry->queue) > 0){            VarpEntry_process_queue(ventry);        }        err = VarpEntry_send(ventry, skb);        break;    default:         if(0){            err = VarpEntry_resolve(ventry, skb);        } else {                 // Multicast the skb if the entry is not reachable.            VnetId vnet = ventry->key.vnet;            VarpEntry_unlock(ventry, flags);            err = varp_multicast(&vnet, skb);            VarpEntry_lock(ventry, flags);        }        break;    }    VarpEntry_unlock(ventry, flags);    return err;}/** Update a ventry. Sets the address and state to those given * and sets the timestamp to 'now'. * * @param ventry varp entry * @param addr care-of address * @param state state * @return 0 on success, error code otherwise */int VarpEntry_update(VarpEntry *ventry, VarpAddr *addr, int state, int vflags){    int err = 0;    unsigned long now = jiffies;    unsigned long flags;    VarpEntry_lock(ventry, flags);    //if(atomic_read(&ventry->deleted)) goto exit;    if(VarpEntry_get_flags(ventry, VARP_FLAG_PERMANENT)) goto exit;    ventry->addr = *addr;    ventry->timestamp = now;    ventry->state = state;    // Can't process the queue while atomic as it calls schedule(),    // and that's bad.    //if(0 && (vflags & VARP_UPDATE_QUEUE) && !in_atomic()){    //    VarpEntry_process_queue(ventry);    //}  exit:    VarpEntry_unlock(ventry, flags);    dprintf("< err=%d\n", err);    return err;}    /** Update the entry for a vnet. * * @param vtable varp table * @param vnet vnet id * @param vmac mac address * @param addr care-of-address * @param state state * @param flags update flags * @return 0 on success, error code otherwise */int VarpTable_update(VarpTable *vtable, VnetId *vnet, Vmac *vmac, VarpAddr *addr,                     int state, int flags){    int err = 0;    VarpEntry *ventry;#ifdef DEBUG    char vnetbuf[VNET_ID_BUF];    char addrbuf[VARP_ADDR_BUF];        dprintf("> vnet=%s mac=" MACFMT " addr=%s state=%d flags=%x\n",            VnetId_ntoa(vnet, vnetbuf),            MAC6TUPLE(vmac->mac),            VarpAddr_ntoa(addr, addrbuf),            state,            flags);#endif    ventry = VarpTable_lookup(vtable, vnet, vmac, (flags & VARP_UPDATE_CREATE));    if(!ventry){        err = -ENOENT;        goto exit;    }    err = VarpEntry_update(ventry, addr, state, flags);    VarpEntry_decref(ventry);  exit:    dprintf("< err=%d\n", err);    return err;}/** Update the entry for a vnet: make it reachable and create an entry * if needed. * * @param vnet vnet id * @param vmac mac address * @param addr care-of-address * @return 0 on success, error code otherwise */int varp_update(VnetId *vnet, unsigned char *vmac, VarpAddr *addr){    int err = 0;    if(!varp_table){        err = -ENOSYS;    } else {        err = VarpTable_update(varp_table, vnet, (Vmac*)vmac, addr,                               VARP_STATE_REACHABLE, VARP_UPDATE_CREATE);    }    return err;}static inline int VarpEntry_sweepable(VarpEntry *ventry){    return !VarpEntry_get_flags(ventry, (VARP_FLAG_PERMANENT | VARP_FLAG_PROBING));}static inline int VarpTable_old(VarpTable *vtable, VarpEntry *ventry, unsigned long now){    return now - ventry->timestamp > vtable->entry_ttl;}/** Sweep old varp entries. * Doesn't affect entries that are probing or permanent. * * @param vtable table */void VarpTable_sweep(VarpTable *vtable){    HashTable_for_decl(entry);    VarpEntry *ventry;    unsigned long now = jiffies;    unsigned long vtflags, flags;    int sweep, swept = 0;    if(!vtable) return;    VarpTable_write_lock(vtable, vtflags);    HashTable_for_each(entry, vtable->table){        ventry = entry->value;        VarpEntry_lock(ventry, flags);        sweep = VarpEntry_sweepable(ventry) && VarpTable_old(vtable, ventry, now);        if(sweep){            swept++;            iprintf("> Sweeping:\n");            VarpEntry_print(ventry, iostdout);            //VarpEntry_process_queue(ventry);            ventry->state = VARP_STATE_INCOMPLETE;        }        VarpEntry_unlock(ventry, flags);        if(sweep){            VarpTable_remove(vtable, ventry);        }    }    VarpTable_write_unlock(vtable, vtflags);    if(swept){        iprintf(">\n");        varp_print(iostdout);    }}/** Flush the varp table. * * @param vtable table */void VarpTable_flush(VarpTable *vtable){    HashTable_for_decl(entry);    VarpEntry *ventry;    unsigned long vtflags, flags;    int flush;    VarpTable_write_lock(vtable, vtflags);    HashTable_for_each(entry, vtable->table){        ventry = entry->value;        VarpEntry_lock(ventry, flags);        flush = (!VarpEntry_get_flags(ventry, VARP_FLAG_PERMANENT) &&                 !VarpEntry_get_flags(ventry, VARP_FLAG_PROBING));                        if(flush){            iprintf("> Flushing:\n");            VarpEntry_print(ventry, iostdout);        }        VarpEntry_unlock(ventry, flags);        if(flush){            VarpTable_remove(vtable, ventry);        }    }    VarpTable_write_unlock(vtable, vtflags);}/** Handle a varp request. Look for a vif with the requested  * vnet and vmac. If find one, reply with the vnet, vmac and our * address. Otherwise do nothing. * * @param skb incoming message * @param varph varp message * @return 0 if ok, -ENOENT if no matching vif, or error code */int varp_handle_request(struct sk_buff *skb, VarpHdr *varph){    int err = -ENOENT;    VnetId *vnet;    Vmac *vmac;    Vif *vif = NULL;    dprintf(">\n");    vnet = &varph->vnet;    vmac = &varph->vmac;    if(vif_lookup(vnet, vmac, &vif)) goto exit;    varp_send(VARP_OP_ANNOUNCE, skb->dev, skb, vnet, vmac);    vif_decref(vif);  exit:    dprintf("< err=%d\n", err);    return err;}/** Announce the vnet and vmac of a vif (gratuitous varp). * * @param dev device to send on (may be null) * @param vif vif * @return 0 on success, error code otherwise */int varp_announce_vif(struct net_device *dev, Vif *vif){    int err = 0;    dprintf(">\n");    if(!varp_table){        err = -ENOSYS;        goto exit;    }    err = varp_send(VARP_OP_ANNOUNCE, dev, NULL, &vif->vnet, &vif->vmac);  exit:    dprintf("< err=%d\n", err);    return err;}/** Handle a varp announce message. * Update the matching ventry if we have one. * * @param skb incoming message * @param varp message * @return 0 if OK, -ENOENT if no matching entry */int varp_handle_announce(struct sk_buff *skb, VarpHdr *varph){    int err = 0;    dprintf(">\n");    err = VarpTable_update(varp_table,                           &varph->vnet, &varph->vmac, &varph->addr,                           VARP_STATE_REACHABLE,                            (VARP_UPDATE_CREATE | VARP_UPDATE_QUEUE));    dprintf("< err=%d\n", err);    return err;}/** Handle an incoming varp message. * * @param skb incoming message * @return 0 if OK, error code otherwise */int varp_handle_message(struct sk_buff *skb){    // Assume nh, h set, skb->data points at udp hdr (h).    int err = -EINVAL;    VarpHdr *varph; // = (void*)(skb->h.uh + 1);    dprintf("> skb=%p saddr=" IPFMT " daddr=" IPFMT "\n",            skb,            NIPQUAD(skb->nh.iph->saddr),            NIPQUAD(skb->nh.iph->daddr));    if(!varp_table){        err = -ENOSYS;        return err;    }    if(MULTICAST(skb->nh.iph->daddr)){        if(skb->nh.iph->daddr != varp_mcast_addr){            // Ignore multicast packets not addressed to us.            err = 0;            dprintf("> Ignoring daddr=" IPFMT " mcaddr=" IPFMT "\n",                    NIPQUAD(skb->nh.iph->daddr), NIPQUAD(varp_mcast_addr));            goto exit;        }    }    varph = (void*)skb_pull_vn(skb, sizeof(struct udphdr));    if(skb->len < sizeof(struct VnetMsgHdr)){        wprintf("> Varp msg too short: %d < %d\n", skb->len, sizeof(struct VnetMsgHdr));        goto exit;    }    switch(ntohs(varph->hdr.id)){    case VARP_ID: // Varp message. Handled below.        if(skb->len < sizeof(*varph)){            wprintf("> Varp msg too short: %d < %d\n", skb->len, sizeof(*varph));            goto exit;        }        break;    case VUDP_ID: // Etherip-in-udp packet.        skb_pull_vn(skb, sizeof(struct VnetMsgHdr));        err = etherip_protocol_recv(skb);        goto exit;    case VFWD_ID: // Forwarded.        skb_pull_vn(skb, sizeof(struct VnetMsgHdr));        err = vnet_forward_recv(skb);        goto exit;    default:        // It's not varp at all - ignore it.        wprintf("> Invalid varp id: %d\n", ntohs(varph->hdr.id));        print_skb("INVALID", 0, skb);        goto exit;    }#ifdef DEBUG    {        char vnetbuf[VNET_ID_BUF];        char addrbuf[VARP_ADDR_BUF];        dprintf("> saddr=" IPFMT " daddr=" IPFMT "\n",                NIPQUAD(skb->nh.iph->saddr), NIPQUAD(skb->nh.iph->daddr));        dprintf("> sport=%u dport=%u\n", ntohs(skb->h.uh->source), ntohs(skb->h.uh->dest));        dprintf("> opcode=%d vnet=%s vmac=" MACFMT " addr=%s\n",                ntohs(varph->hdr.opcode),                VnetId_ntoa(&varph->vnet, vnetbuf),                MAC6TUPLE(varph->vmac.mac),                VarpAddr_ntoa(&varph->addr, addrbuf));        varp_dprint();    }#endif    switch(ntohs(varph->hdr.opcode)){    case VARP_OP_REQUEST:        err = varp_handle_request(skb, varph);        break;    case VARP_OP_ANNOUNCE:        err = varp_handle_announce(skb, varph);        break;    default:        wprintf("> Unknown opcode: %d \n", ntohs(varph->hdr.opcode));        break;    }  exit:    dprintf("< err=%d\n", err);    return err;}/** Send an outgoing packet on the appropriate vnet tunnel. * * @param skb outgoing message * @param vnet vnet (network order) * @return 0 on success, error code otherwise */int varp_output(struct sk_buff *skb, VnetId *vnet){    int err = 0;    unsigned char *mac = NULL;    Vmac *vmac = NULL;    VarpEntry *ventry = NULL;#if defined(DEBUG)    char vnetbuf[VNET_ID_BUF];#endif    dprintf("> vnet=%s\n", VnetId_ntoa(vnet, vnetbuf));    if(!varp_table){        err = -ENOSYS;        goto exit;    }    if(!skb->mac.raw){        wprintf("> No ethhdr in skb!\n");        err = -EINVAL;        goto exit;    }    mac = eth_hdr(skb)->h_dest;    vmac = (Vmac*)mac;    if(mac_is_multicast(mac)){        err = varp_multicast(vnet, skb);    } else {        ventry = VarpTable_lookup(varp_table, vnet, vmac, 1);        if(ventry){            err = VarpEntry_output(ventry, skb);            VarpEntry_decref(ventry);        } else {            err = -ENOMEM;        }    }  exit:    dprintf("< err=%d\n", err);    return err;}/** Set the varp multicast address (after initialization). * * @param addr address (network order) * @return 0 on success, error code otherwise */int varp_set_mcast_addr(uint32_t addr){    int err = 0;    varp_close();    varp_mcast_addr = addr;    err = varp_open(varp_mcast_addr, varp_port);    return err;}/** Initialize the varp multicast address from a module parameter. * * @param s address in IPv4 notation * @return 0 on success, error code otherwise */static void varp_init_mcast_addr(char *s){    unsigned long v = 0;    dprintf("> %s\n", s);    if(s && (get_inet_addr(s, &v) >= 0)){        varp_mcast_addr = (u32)v;    } else {        varp_mcast_addr = htonl(VARP_MCAST_ADDR);    }}/** Initialize the varp cache. * * @return 0 on success, error code otherwise */int varp_init(void){    int err = 0;        dprintf(">\n");    varp_table = VarpTable_new();    if(!varp_table){        err = -ENOMEM;        goto exit;    }    VarpTable_schedule(varp_table);    varp_init_mcast_addr(varp_mcaddr);    varp_port = htons(VARP_PORT);    err = varp_open(varp_mcast_addr, varp_port);  exit:    dprintf("< err=%d\n", err);    return err;}/** Close the varp cache. */void varp_exit(void){    dprintf(">\n");    varp_close();    if(varp_table){        VarpTable *vtable = varp_table;        varp_table = NULL;        VarpTable_free(vtable);    }    dprintf("<\n");}module_param(varp_mcaddr, charp, 0644);module_param(varp_device, charp, 0644);MODULE_PARM_DESC(varp_mcaddr, "VARP multicast address");MODULE_PARM_DESC(varp_device, "VARP network device");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?