vnet_forward.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 390 行

C
390
字号
/* * Copyright (C) 2005, 2006 Mike Wray <mike.wray@hp.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by the  * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. *  * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free software Foundation, Inc., * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA * */#ifdef __KERNEL__#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/version.h>#include <linux/spinlock.h>#include <linux/skbuff.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/netfilter_bridge.h>#include <linux/netfilter_ipv4.h>#include <linux/udp.h>#include <net/ip.h>#include <net/protocol.h>#include <net/route.h>#include <net/checksum.h>#else#include <netinet/in.h>#include <arpa/inet.h>#include "sys_kernel.h"#include "spinlock.h"#include "skbuff.h"#include <linux/ip.h>#include <linux/udp.h>#endif#include <varp.h>#include <if_varp.h>#include <varp.h>#include <skb_util.h>#include <skb_context.h>#include "allocate.h"#include "iostream.h"#include "hash_table.h"#include "vnet_forward.h"#define MODULE_NAME "VNET"#define DEBUG 1#undef DEBUG#include "debug.h"extern int _skb_xmit(struct sk_buff *skb, uint32_t saddr);typedef struct VnetPeer {    struct VarpAddr addr;    uint16_t port;    atomic_t refcount;    int tx_packets;    int rx_packets;} VnetPeer;static HashTable *vnet_peer_table = NULL;static rwlock_t vnet_peer_table_lock = RW_LOCK_UNLOCKED;#define vnet_peer_read_lock(flags)    read_lock_irqsave(&vnet_peer_table_lock, (flags))#define vnet_peer_read_unlock(flags)  read_unlock_irqrestore(&vnet_peer_table_lock, (flags))#define vnet_peer_write_lock(flags)   write_lock_irqsave(&vnet_peer_table_lock, (flags))#define vnet_peer_write_unlock(flags) write_unlock_irqrestore(&vnet_peer_table_lock, (flags))static void VnetPeer_decref(VnetPeer *peer){    if(!peer) return;    if(atomic_dec_and_test(&peer->refcount)){        kfree(peer);    }}static void VnetPeer_incref(VnetPeer *peer){    if(!peer) return;    atomic_inc(&peer->refcount);}static void VnetPeer_print(VnetPeer *peer, IOStream *io){    char addrbuf[VARP_ADDR_BUF];        IOStream_print(io, "(vnet_peer\n");    IOStream_print(io, "  (addr %s)\n", VarpAddr_ntoa(&peer->addr, addrbuf));    IOStream_print(io, "  (port %d)\n", htons(peer->port));    IOStream_print(io, "  (tx_packets %d)\n", peer->tx_packets);    IOStream_print(io, "  (rx_packets %d)\n", peer->tx_packets);    IOStream_print(io, ")\n");}static int VnetPeer_forward(VnetPeer *peer, struct sk_buff *fwdskb){    int err = 0;    const int ip_n = sizeof(struct iphdr);    const int udp_n = sizeof(struct udphdr);    const int vnet_n = sizeof(struct VnetMsgHdr);    int head_n = 16 + ip_n + udp_n + vnet_n;    int push_n = 0;    struct sk_buff *skb = NULL;    struct VnetMsgHdr *vhdr;    uint32_t saddr = 0;    uint16_t sport = varp_port;    uint32_t daddr = peer->addr.u.ip4.s_addr;    uint16_t dport = varp_port;    if(!fwdskb) goto exit;    if(daddr == fwdskb->nh.iph->saddr){        // Don't forward if the skb src addr is the peer addr.        dprintf("> Forward loop on " IPFMT "\n", NIPQUAD(daddr));        goto exit;    }    // On entry fwdskb->data should be at fwdskb->nh.raw (adjust if not).    // Also fwdskb->h.raw and fwdskb->nh.raw are set.    if(fwdskb->data > fwdskb->nh.raw){        push_n = fwdskb->data - fwdskb->nh.raw;        head_n += push_n;    }    // If has headroom, copies header (which incs ref on dst),    // otherwise only clones header, which does not inc ref on dst.    skb = skb_realloc_headroom(fwdskb, head_n);    //skb = skb_copy_expand(fwdskb, head_n, 0, GFP_ATOMIC);    if(!skb){        err = -ENOMEM;        goto exit;    }    if(push_n){        skb_push(skb, push_n);    }#ifdef DEBUG    printk("\nOriginal packet:\n");    print_iphdr(__FUNCTION__, skb);    skb_print_bits(__FUNCTION__, skb, 0, skb->len);#endif    skb->mac.raw = NULL;    vhdr = (void*)skb_push(skb, vnet_n);    vhdr->id       = htons(VFWD_ID);    vhdr->opcode   = 0;    // Setup the UDP header.    skb->h.raw = skb_push(skb, udp_n);    skb->h.uh->source = sport;		        // Source port.    skb->h.uh->dest   = dport;		        // Destination port.    skb->h.uh->len    = htons(skb->len);	// Total packet length (bytes).    skb->h.uh->check  = 0;    // Setup the IP header.    skb->nh.raw = skb_push(skb, ip_n);     skb->nh.iph->version  = 4;			// Standard version.    skb->nh.iph->ihl      = ip_n / 4;		// IP header length (32-bit words).    skb->nh.iph->tos      = 0;			// No special type-of-service.    skb->nh.iph->tot_len  = htons(skb->len);    // Total packet length (bytes).    skb->nh.iph->id       = 0;			// No flow id.    skb->nh.iph->protocol = IPPROTO_UDP;        // IP protocol number.    skb->nh.iph->frag_off = 0;    skb->nh.iph->ttl      = 64;			// Linux default time-to-live.    skb->nh.iph->saddr    = saddr;		// Source address.    skb->nh.iph->daddr    = daddr;              // Destination address.    skb->nh.iph->check    = 0;#ifdef DEBUG    printk("\nWrapped packet:\n");    print_iphdr(__FUNCTION__, skb);    print_udphdr(__FUNCTION__, skb);    skb_print_bits(__FUNCTION__, skb, 0, skb->len);#endif    err = _skb_xmit(skb, saddr);    peer->tx_packets++;  exit:    if(err < 0) kfree_skb(skb);    return err;}int vnet_peer_get(VarpAddr *addr, VnetPeer **peer){    unsigned long flags;    vnet_peer_read_lock(flags);    *peer = HashTable_get(vnet_peer_table, addr);    VnetPeer_incref(*peer);    vnet_peer_read_unlock(flags);    return (*peer ? 0 : -ENOENT);}int vnet_peer_add(VarpAddr *addr, uint16_t port){    int err = 0;    unsigned long flags;    VnetPeer *peer;        vnet_peer_write_lock(flags);    peer = HashTable_get(vnet_peer_table, addr);    if(peer){        VnetPeer_incref(peer);        goto exit;    }    peer = ALLOCATE(VnetPeer);    if(!peer){        err = -ENOMEM;        goto exit;    }    peer->addr = *addr;    peer->port = port;    VnetPeer_incref(peer);    if(!HashTable_add(vnet_peer_table, &peer->addr, peer)){        VnetPeer_decref(peer);        err = -ENOMEM;    }  exit:    vnet_peer_write_unlock(flags);    return err;}int vnet_peer_del(VarpAddr *addr){    int ret = 0;    unsigned long flags;    vnet_peer_write_lock(flags);    ret = HashTable_remove(vnet_peer_table, addr);    vnet_peer_write_unlock(flags);    return ret;}void vnet_peer_print(IOStream *io){    HashTable_for_decl(entry);    unsigned long flags;    if(!vnet_peer_table) return;    vnet_peer_read_lock(flags);    HashTable_for_each(entry, vnet_peer_table){        VnetPeer *peer = entry->value;        VnetPeer_print(peer, io);    }    vnet_peer_read_unlock(flags);}int vnet_forward_send(struct sk_buff *skb){    int err = 0;    unsigned long flags;    HashTable_for_decl(entry);    int count = 0;    if(!vnet_peer_table){        goto exit;    }    vnet_peer_read_lock(flags);    HashTable_for_each(entry, vnet_peer_table){        VnetPeer *peer = entry->value;        VnetPeer_forward(peer, skb);        count++;    }    vnet_peer_read_unlock(flags);  exit:    return err;}int vnet_forward_recv(struct sk_buff *skb){    int err = 0;    VarpAddr addr = { .family = AF_INET };    VnetPeer *peer = NULL;    unsigned char eth[ETH_HLEN] = {};    struct sk_buff *recvskb;    if(!vnet_peer_table){        dprintf("> no table\n");        return -ENOSYS;    }    // On entry mac.raw, h.raw, nh.raw are set.    // skb->data points after the fwd vnet header, at the complete    // forwarded packet (which has IP hdr, no eth hdr).    // Save the eth hdr and source addr (peer).    memcpy(eth, skb->mac.raw, ETH_HLEN);    addr.u.ip4.s_addr = skb->nh.iph->saddr;    err = vnet_peer_get(&addr, &peer);    if(err){        wprintf("> no peer for " IPFMT "\n", NIPQUAD(skb->nh.iph->saddr));        goto exit;    }    peer->rx_packets++;    skb->mac.raw = NULL;    skb->nh.raw = skb->data;    skb->h.raw = skb->data + sizeof(struct iphdr);    if(!skb->nh.iph->saddr){        skb->nh.iph->saddr = addr.u.ip4.s_addr;    }#ifdef __KERNEL__    // Fix IP options, checksum, skb dst, netfilter state.    memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));    skb->dev = NULL;    dst_release(skb->dst);    skb->dst = NULL;    nf_reset(skb);#endif // __KERNEL__    skb->mac.raw = skb->nh.raw - ETH_HLEN;    memcpy(skb->mac.raw, eth, ETH_HLEN);    // Map destination mcast addresses to our mcast address.    if(MULTICAST(skb->nh.iph->daddr)){        skb->nh.iph->daddr = varp_mcast_addr;        //xmit does this: ip_eth_mc_map(varp_mcast_addr, eth_hdr(skb)->h_dest);    }    // Handle (a copy of) it ourselves, because    // if it is looped-back by xmit it will be ignored.    recvskb = alloc_skb(skb->len, GFP_ATOMIC);    if(recvskb){        recvskb->protocol = htons(ETH_P_IP);        recvskb->nh.raw = skb_put(recvskb, skb->len);        recvskb->h.raw = recvskb->data + sizeof(struct iphdr);         skb_copy_bits(skb, 0, recvskb->data, skb->len);                // Data points at the unwrapped iphdr, but varp_handle_message()        // expects it to point at the udphdr, so pull.        skb_pull_vn(recvskb, sizeof(struct iphdr));        if(varp_handle_message(recvskb) <= 0){            kfree_skb(recvskb);        }    }    err = _skb_xmit(skb, skb->nh.iph->saddr);    if(err >= 0) err = 1;  exit:    return err;}/** Hash function for keys in the peer table. */static Hashcode peer_key_hash_fn(void *k){    return hash_hvoid(0, k, sizeof(struct VarpAddr));}/** Equality function for keys in the peer table. */static int peer_key_equal_fn(void *k1, void *k2){    return memcmp(k1, k2, sizeof(struct VarpAddr)) == 0;}static void peer_entry_free_fn(HashTable *table, HTEntry *entry){    if(!entry) return;    VnetPeer_decref((VnetPeer*)entry->value);    HTEntry_free(entry);}int vnet_forward_init(void){    int err = 0;    if(vnet_peer_table) goto exit;    vnet_peer_table = HashTable_new(0);    if(!vnet_peer_table){        err = -ENOMEM;        goto exit;    }    vnet_peer_table->key_size = sizeof(struct VarpAddr);    vnet_peer_table->key_equal_fn = peer_key_equal_fn;    vnet_peer_table->key_hash_fn = peer_key_hash_fn;    vnet_peer_table->entry_free_fn = peer_entry_free_fn;  exit:    return err;}void vnet_forward_exit(void){    HashTable_free(vnet_peer_table);    vnet_peer_table = NULL;}

⌨️ 快捷键说明

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