/* * This file is part of the Palacios Virtual Machine Monitor developed * by the V3VEE Project with funding from the United States National * Science Foundation and the Department of Energy. * * The V3VEE Project is a joint project between Northwestern University * and the University of New Mexico. You can find out more at * http://www.v3vee.org * * Copyright (c) 2010, Lei Xia * Copyright (c) 2010, The V3VEE Project * All rights reserved. * * Author: Lei Xia * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ /* VNET backend bridge for Linux host */ #include #include #include #include #include #include #ifndef CONFIG_DEBUG_VNET_LNX_BRIGE #undef PrintDebug #define PrintDebug(fmt, args...) #endif typedef enum {SOCK_UDP, SOCK_TCP, SOCK_OTHER} sock_type_t; const uint16_t vnet_udp_port = 20003; struct vnet_link { uint32_t dst_ip; uint16_t dst_port; int socket; sock_type_t type; int link_idx; struct list_head node; }; struct vnet_brg_state { uint32_t num_links; struct list_head link_list; int serv_sock; sock_type_t serv_sock_type; int serv_port; v3_lock_t lock; unsigned long pkt_sent, pkt_recv, pkt_drop; }; static int vnet_lnxbrg_reset(struct vnet_brg_state * state) { memset(state, 0, sizeof(struct vnet_brg_state)); state->num_links = 0; state->serv_sock = -1; state->serv_port = vnet_udp_port; state->serv_sock_type = SOCK_UDP; if(v3_lock_init(&(state->lock)) < 0){ PrintError("VNET Linux Bridge: error to initiate vnet lock\n"); } return 0; } struct vnet_link * link_by_ip(struct vnet_brg_state * state, uint32_t ip) { struct vnet_link * link = NULL; list_for_each_entry(link, &(state->link_list), node) { if (link->dst_ip == ip) { return link; } } return NULL; } struct vnet_link * link_by_idx(struct vnet_brg_state * state, int idx) { struct vnet_link * link = NULL; list_for_each_entry(link, &(state->link_list), node) { if (link->link_idx == idx) { return link; } } return NULL; } static int udp_send(int sockid, uint32_t dst_ip, uint16_t dst_port, uchar_t * pkt, uint16_t len){ if(dst_ip > 0 && dst_port > 0){ return V3_SendTo_IP(sockid, dst_ip, dst_port, pkt, len); } return V3_Send(sockid, pkt, len); } static int udp_recv(int sockid, uint32_t * src_ip, uint16_t * src_port, uchar_t *buf, uint16_t len) { return V3_Recv(sockid, buf, len); } static int brg_send( struct v3_vm_info * vm, struct v3_vnet_pkt * vnet_pkt, void * private_data){ struct vnet_brg_state * state = (struct vnet_brg_state *)private_data; struct vnet_link * link = NULL; #ifdef CONFIG_DEBUG_VNET_LNX_BRIGE { PrintDebug("vnet_brg_send... pkt size: %d, link: %d, struct len: %d\n", vnet_pkt->size, vnet_pkt->dst_id, sizeof(struct v3_vnet_pkt)); } #endif state->pkt_recv ++; link = link_by_idx(state, vnet_pkt->dst_id); if (link != NULL) { if(link->type == SOCK_UDP){ udp_send(link->socket, 0, 0, vnet_pkt->data, vnet_pkt->size); state->pkt_sent ++; }else { PrintError("VNET Linux Bridge: wrong link type\n"); return -1; } } else { PrintDebug("VNET Linux Bridge: wrong dst link, idx: %d, discards the packet\n", vnet_pkt->dst_id); state->pkt_drop ++; } return 0; } static int init_serv(struct vnet_brg_state * state) { int sock, err; if(state->serv_sock_type == SOCK_UDP){ sock = V3_Create_UDP_Socket(); if (sock < 0) { PrintError("Could not create socket, Initiate VNET server error\n"); return -1; } err = V3_Bind_Socket(sock, state->serv_port); if(err < 0){ PrintError("Error to bind VNET Linux bridge receiving UDP socket\n"); return -1; } state->serv_sock = sock; } return 0; } static int vnet_server(void * arg) { struct v3_vnet_pkt pkt; struct vnet_link *link; struct vnet_brg_state * state = (struct vnet_brg_state *)arg; uchar_t buf[ETHERNET_MTU]; uint32_t ip = 0; uint16_t port = 0; int len; while (1) { len = udp_recv(state->serv_sock, &ip, &port, buf, ETHERNET_MTU); if(len < 0) { PrintError("VNET Linux Bridge: receive error\n"); continue; } link = link_by_ip(state, ip); if (link != NULL) { pkt.src_id= link->link_idx; } else { pkt.src_id= -1; } pkt.size = len; pkt.src_type = LINK_EDGE; memcpy(pkt.header, buf, ETHERNET_HEADER_LEN); pkt.data = buf; #ifdef CONFIG_DEBUG_VNET_LNX_BRIGE { PrintDebug("VNET Linux Bridge: recv pkt size: %d, pkt src_id: %d\n", len, pkt.src_id); v3_hexdump(buf, len, NULL, 0); } #endif v3_vnet_send_pkt(&pkt, NULL); state->pkt_recv ++; } return 0; } static int vnet_lnxbrg_init() { struct v3_vnet_bridge_ops brg_ops; struct vnet_brg_state * state; state = (struct vnet_brg_state *)V3_Malloc(sizeof(struct vnet_brg_state)); V3_ASSERT(state != NULL); vnet_lnxbrg_reset(state); brg_ops.input = brg_send; v3_vnet_add_bridge(NULL, &brg_ops, state); init_serv(state); V3_CREATE_THREAD(vnet_server, state, "VNET_LNX_BRIDGE"); PrintDebug("VNET Linux Bridge initiated\n"); return 0; } device_register("LNX_VNET_BRIDGE", vnet_lnxbrg_init)