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