2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2009, Lei Xia <lxia@northwestern.edu>
11 * Copyright (c) 2009, Yuan Tang <ytang@northwestern.edu>
12 * Copyright (c) 2009, The V3VEE Project <http://www.v3vee.org>
13 * All rights reserved.
15 * Author: Lei Xia <lxia@northwestern.edu>
16 * Yuan Tang <ytang@northwestern.edu>
18 * This is free software. You are permitted to use,
19 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
22 #include <palacios/vmm_vnet.h>
23 #include <palacios/vmm_hypercall.h>
24 #include <palacios/vm_guest_mem.h>
25 #include <palacios/vmm_lock.h>
27 #ifndef CONFIG_DEBUG_VNET
29 #define PrintDebug(fmt, args...)
33 #define ETHERNET_HEADER_LEN 14
34 #define ETHERNET_DATA_MIN 46
35 #define ETHERNET_DATA_MAX 1500
36 #define ETHERNET_PACKET_LEN (ETHERNET_HEADER_LEN + ETHERNET_DATA_MAX)
39 typedef enum {TCP_TYPE, UDP_TYPE, NONE_TYPE} prot_type_t;
41 #define VNET_INITAB_HCALL 0xca00
46 uint16_t type; // indicates layer 3 protocol type
47 }__attribute__((packed));
62 }__attribute__((packed));
68 uint16_t csum;//set to zero, disable the xsum
69 }__attribute__((packed));
71 struct udp_link_header {
72 struct eth_header eth_hdr;
73 struct ip_header ip_hdr;
74 struct udp_header udp_hdr;
75 }__attribute__((packed));
79 uint32_t size; //size of data field
82 struct udp_link_header ext_hdr;
83 char data[ETHERNET_PACKET_LEN];
84 }__attribute__((packed));
87 #define DEVICE_NAME_LEN 20
88 struct vnet_if_device {
89 char name[DEVICE_NAME_LEN];
91 struct v3_vm_info *vm;
93 int (*input)(struct v3_vm_info *vm, uchar_t *data, uint32_t len, void *private_data);
96 }__attribute__((packed));
99 prot_type_t pro_type; //transport layer protocal type of this link
100 unsigned long dest_ip;
103 struct udp_link_header vnet_header; //header applied to the packet in/out from this link
105 int (*input)(uchar_t *data, uint32_t len, void *private_data);
108 }__attribute__((packed));
115 struct vnet_if_device *dst_dev;
116 struct vnet_if_link *dst_link;
117 } __attribute__((packed));
120 }__attribute__((packed));
122 //routing table entry
123 struct routing_entry {
127 mac_type_t src_mac_qual;
128 mac_type_t dest_mac_qual;
130 int link_idx; //link[dest] is the link to be used to send pkt
131 link_type_t link_type; //EDGE|INTERFACE|ANY
134 link_type_t src_type; //EDGE|INTERFACE|ANY
138 // 14 (ethernet frame) + 20 bytes
139 struct in_pkt_header {
140 uchar_t ethernetdest[6];
141 uchar_t ethernetsrc[6];
142 uchar_t ethernettype[2]; //layer 3 protocol type
144 }__attribute__((packed));
148 #define MAX_ROUTES 10
149 #define HASH_KEY_LEN 16
150 #define MIN_CACHE_SIZE 100
151 #define HASH_KEY_SIZE 16
154 struct link_entry * links[MAX_LINKS];
157 }__attribute__((packed));
159 struct routing_table {
160 struct routing_entry * routes[MAX_ROUTES];
163 }__attribute__((packed));
165 typedef char * route_hashkey_t;
167 struct route_cache_entry {
168 int num_matched_routes;
172 struct vnet_state_t {
173 struct link_table g_links;
174 struct routing_table g_routes;
175 struct gen_queue * g_inpkt_q;
176 struct hashtable * g_route_cache;
177 v3_lock_t cache_lock;
180 static struct vnet_state_t g_vnet_state;//global state for vnet
182 static uint16_t ip_xsum(struct ip_header *ip_hdr, int hdr_len){
185 uint16_t *p = (uint16_t*) ip_hdr;
189 sum = (sum & 0xFFFF) + (sum >> 16);
194 sum += (uint16_t) *(uchar_t *)p;
197 sum = (sum & 0xFFFF) + (sum >> 16);
199 return (uint16_t)~sum;
202 static void ethernet_packet_init(struct ethernet_pkt *pt,
207 memset(&pt->ext_hdr, 0, sizeof(struct udp_link_header));
208 memcpy(pt->data, data, size);
211 static inline uint_t hash_from_key_fn(addr_t hashkey) {
212 return v3_hash_buffer((uint8_t *)hashkey, HASH_KEY_LEN);
215 static inline int hash_key_equal(addr_t key1, addr_t key2) {
216 return (memcmp((uint8_t *)key1, (uint8_t *)key2, HASH_KEY_LEN) == 0);
219 static int init_route_cache(struct vnet_state_t *vnet_state) {
220 vnet_state->g_route_cache = v3_create_htable(MIN_CACHE_SIZE,
224 if (vnet_state->g_route_cache == NULL) {
225 PrintError("Vnet: Route Cache Init Fails\n");
232 static void make_hash_key(route_hashkey_t hashkey,
239 for (j = 0; j < 6; j++) {
240 hashkey[j] = src_addr[j];
241 hashkey[j + 6] = dest_addr[j] + 1;
244 hashkey[12] = src_type;
246 *(int *)(hashkey + 12) = src_index;
249 static int add_route_to_cache(route_hashkey_t hashkey,
252 struct route_cache_entry * new_entry = NULL;
253 struct vnet_state_t *vnet_state = &g_vnet_state;
256 new_entry = (struct route_cache_entry *)V3_Malloc(sizeof(struct route_cache_entry));
257 if (new_entry == NULL) {
258 PrintError("Vnet: Malloc fails\n");
262 new_entry->num_matched_routes = num_matched_r;
263 new_entry->matches = (int *)V3_Malloc(num_matched_r * sizeof(int));
265 if (new_entry->matches == NULL) {
266 PrintError("Vnet: Malloc fails\n");
270 for (i = 0; i < num_matched_r; i++) {
271 new_entry->matches[i] = matches[i];
274 if (v3_htable_insert(vnet_state->g_route_cache, (addr_t)hashkey, (addr_t)new_entry) == 0) {
275 PrintError("Vnet: Failed to insert new route entry to the cache\n");
276 V3_Free(new_entry->matches);
283 static int clear_hash_cache() {
284 struct vnet_state_t *vnet_state = &g_vnet_state;
286 v3_free_htable(vnet_state->g_route_cache, 1, 1);
287 vnet_state->g_route_cache = v3_create_htable(MIN_CACHE_SIZE,
291 if (vnet_state->g_route_cache == NULL) {
292 PrintError("Vnet: Route Cache Create Failurely\n");
299 static int look_into_cache(route_hashkey_t hashkey, int * matches) {
300 struct route_cache_entry * found = NULL;
303 struct vnet_state_t *vnet_state = &g_vnet_state;
305 found = (struct route_cache_entry *)v3_htable_search(vnet_state->g_route_cache,
309 n_matches = found->num_matched_routes;
311 for (i = 0; i < n_matches; i++) {
312 matches[i] = found->matches[i];
320 #ifdef CONFIG_DEBUG_VNET
322 static void print_packet(uchar_t *pkt, int size) {
323 PrintDebug("Vnet: data_packet: size: %d\n", size);
324 v3_hexdump(pkt, size, NULL, 0);
327 static inline uint8_t hex_nybble_to_nybble(const uint8_t hexnybble) {
328 uint8_t x = toupper(hexnybble);
333 return 10 + (x - 'A');
337 static inline uint8_t hex_byte_to_byte(const uint8_t hexbyte[2]) {
338 return ((hex_nybble_to_nybble(hexbyte[0]) << 4) +
339 (hex_nybble_to_nybble(hexbyte[1]) & 0xf));
342 static inline void string_to_mac(const char *str, uint8_t mac[6]) {
345 for (k = 0; k < 6; k++) {
346 mac[k] = hex_byte_to_byte(&(str[(2 * k) + k]));
350 static inline void mac_to_string(char mac[6], char * buf) {
351 snprintf(buf, 20, "%02x:%02x:%02x:%02x:%02x:%02x",
352 mac[0], mac[1], mac[2],
353 mac[3], mac[4], mac[5]);
357 static void print_route(struct routing_entry *route){
361 mac_to_string(route->src_mac, src_str);
362 mac_to_string(route->dest_mac, dest_str);
364 PrintDebug("SRC(%s), DEST(%s), src_mac_qual(%d), dst_mac_qual(%d)\n",
368 route->dest_mac_qual);
369 PrintDebug("Src_Link(%d), src_type(%d), dst_link(%d), dst_type(%d)\n\n",
377 static void dump_routes(struct routing_entry **route_tables) {
380 PrintDebug("\nVnet: route table dump start =====\n");
382 for(i = 0; i < MAX_ROUTES; i++) {
383 if (route_tables[i] != NULL){
384 print_route(route_tables[i]);
388 PrintDebug("\nVnet: route table dump end =====\n");
391 static void dump_dom0_routes(struct routing_entry routes[], int size) {
394 PrintDebug("\nVnet: route table from dom0 guest =====\n");
396 for(i = 0; i <size; i++) {
397 print_route(&routes[i]);
400 PrintDebug("\nVnet: route table dom0 guest end =====\n");
403 static void dump_dom0_links(struct vnet_if_link links[], int size) {
404 struct vnet_if_link *link = NULL;
407 PrintDebug("\nVnet: link table from dom0 guest =====\n");
409 for(i = 0; i <size; i++) {
411 PrintDebug("link: %d\n", i);
412 PrintDebug("dest_ip(%ld), dst_port(%d), prot_type(%d)\n",
416 PrintDebug("vnet_header:\n");
417 v3_hexdump(&link->vnet_header, sizeof(struct udp_link_header), NULL, 0);
420 PrintDebug("\nVnet: link table dom0 guest end =====\n");
425 static int __add_link_entry(struct link_entry * link) {
427 struct vnet_state_t *vnet_state = &g_vnet_state;
429 v3_lock(vnet_state->g_links.lock);
430 for (idx = 0; idx < MAX_LINKS; idx++) {
431 if (vnet_state->g_links.links[idx] == NULL) {
432 vnet_state->g_links.links[idx] = link;
433 vnet_state->g_links.size++;
437 v3_unlock(vnet_state->g_links.lock);
439 if (idx == MAX_LINKS) {
440 PrintDebug("VNET: No available Link entry for new link\n");
447 static int __add_route_entry(struct routing_entry * route) {
449 struct vnet_state_t *vnet_state = &g_vnet_state;
451 v3_lock(vnet_state->g_routes.lock);
452 for (idx = 0; idx < MAX_ROUTES; idx++) {
453 if (vnet_state->g_routes.routes[idx] == NULL) {
454 vnet_state->g_routes.routes[idx] = route;
455 vnet_state->g_routes.size++;
459 v3_unlock(vnet_state->g_routes.lock);
461 if(idx == MAX_LINKS){
462 PrintDebug("VNET: No available route entry for new route\n");
466 #ifdef CONFIG_DEBUG_VNET
467 dump_routes(vnet_state->g_routes.routes);
473 int v3_vnet_add_route(struct v3_vnet_route *route){
474 struct routing_entry * new_route = (struct routing_entry *)V3_Malloc(sizeof(struct routing_entry));
477 PrintDebug("Vnet: vnet_add_route_entry\n");
479 memset(new_route, 0, sizeof(struct routing_entry));
480 if ((route->src_mac_qual != MAC_ANY)) {
481 memcpy(new_route->src_mac, route->src_mac, 6);
484 if ((route->dest_mac_qual != MAC_ANY)) {
485 memcpy(new_route->dest_mac, route->dest_mac, 6);
488 new_route->src_mac_qual = route->src_mac_qual;
489 new_route->dest_mac_qual = route->dest_mac_qual;
490 new_route->link_idx= route->link_idx;
491 new_route->link_type = route->link_type;
492 new_route->src_link_idx = route->src_link_idx;
493 new_route->src_type = route->src_type;
495 if ((idx = __add_route_entry(new_route)) == -1) {
496 PrintError("Could not add route entry\n");
505 static int match_route(uint8_t * src_mac,
507 link_type_t src_type,
510 struct routing_entry *route = NULL;
511 struct vnet_state_t *vnet_state = &g_vnet_state;
512 int matched_routes[MAX_ROUTES];
516 #ifdef CONFIG_DEBUG_VNET
520 mac_to_string(src_mac, src_str);
521 mac_to_string(dst_mac, dest_str);
522 PrintDebug("Vnet: match_route. pkt: SRC(%s), DEST(%s)\n", src_str, dest_str);
525 for(i = 0; i < MAX_ROUTES; i++) {
526 if (vnet_state->g_routes.routes[i] != NULL){
527 route = vnet_state->g_routes.routes[i];
529 if(src_type == LINK_ANY && src_index == -1) {
530 if ((route->dest_mac_qual == MAC_ANY) &&
531 (route->src_mac_qual == MAC_ANY)) {
532 matched_routes[num_matches] = i;
536 if (memcmp((void *)&route->src_mac, (void *)src_mac, 6) == 0) {
537 if (route->src_mac_qual != MAC_NOT) {
538 if (route->dest_mac_qual == MAC_ANY) {
539 matched_routes[num_matches] = i;
541 } else if (route->dest_mac_qual != MAC_NOT &&
542 memcmp((void *)&route->dest_mac, (void *)dst_mac, 6) == 0) {
543 matched_routes[num_matches] = i;
549 if (memcmp((void *)&route->dest_mac, (void *)dst_mac, 6) == 0) {
550 if (route->dest_mac_qual != MAC_NOT) {
551 if (route->src_mac_qual == MAC_ANY) {
552 matched_routes[num_matches] = i;
554 } else if ((route->src_mac_qual != MAC_NOT) &&
555 (memcmp((void *)&route->src_mac, (void *)src_mac, 6) == 0)) {
556 matched_routes[num_matches] = i;
562 if ((route->dest_mac_qual == MAC_NOT) &&
563 (memcmp((void *)&route->dest_mac, (void *)dst_mac, 6) != 0)) {
564 if (route->src_mac_qual == MAC_ANY) {
565 matched_routes[num_matches] = i;
567 } else if ((route->src_mac_qual != MAC_NOT) &&
568 (memcmp((void *)&route->src_mac, (void *)src_mac, 6) == 0)) {
569 matched_routes[num_matches] = i;
574 if ((route->src_mac_qual == MAC_NOT) &&
575 (memcmp((void *)&route->src_mac, (void *)src_mac, 6) != 0)) {
576 if (route->dest_mac_qual == MAC_ANY) {
577 matched_routes[num_matches] = i;
579 } else if ((route->dest_mac_qual != MAC_NOT) &&
580 (memcmp((void *)&route->dest_mac, (void *)dst_mac, 6) == 0)) {
581 matched_routes[num_matches] = i;
585 }//end if src_type == Link_any
589 PrintDebug("Vnet: match_route: Matches=%d\n", num_matches);
591 for (i = 0; i < num_matches; i++) {
592 matches[i] = matched_routes[i];
598 static int handle_one_pkt(struct ethernet_pkt *pkt) {
599 int src_link_index = -1; //the value of src_link_index of udp always is 0
602 int matches[MAX_ROUTES];
603 int num_matched_routes = 0;
604 struct in_pkt_header header;
605 char hash_key[HASH_KEY_SIZE];
606 struct vnet_state_t *vnet_state = &g_vnet_state;
609 // get the ethernet and ip headers from the packet
610 memcpy((void *)&header, (void *)pkt->data, sizeof(header));
611 memcpy(src_mac, header.ethernetsrc, 6);
612 memcpy(dst_mac, header.ethernetdest, 6);
614 #ifdef CONFIG_DEBUG_VNET
618 mac_to_string(src_mac, src_str);
619 mac_to_string(dst_mac, dest_str);
620 PrintDebug("Vnet: HandleDataOverLink. SRC(%s), DEST(%s)\n", src_str, dest_str);
623 make_hash_key(hash_key, src_mac, dst_mac, LINK_EDGE, src_link_index);
624 num_matched_routes = look_into_cache((route_hashkey_t)hash_key, matches);
626 if (num_matched_routes == -1) {
627 num_matched_routes = match_route(src_mac, dst_mac, LINK_ANY, src_link_index, matches);
628 if (num_matched_routes > 0) {
629 add_route_to_cache(hash_key, num_matched_routes,matches);
633 PrintDebug("Vnet: HandleDataOverLink: Matches=%d\n", num_matched_routes);
635 if (num_matched_routes == 0) {
639 for (i = 0; i < num_matched_routes; i++) {//send packet to all destinations
640 int route_index = -1;
643 struct link_entry * link = NULL;
645 route_index = matches[i];
646 link_index = vnet_state->g_routes.routes[route_index]->link_idx;
648 if ((link_index < 0) || (link_index > MAX_LINKS) ||
649 (vnet_state->g_links.links[link_index] == NULL)) {
653 link = vnet_state->g_links.links[link_index];
655 if (link->type == LINK_EDGE) {
657 //apply the header in the beginning of the packet to be sent
658 if (link->dst_link->pro_type == UDP_TYPE) {
659 struct udp_link_header *hdr = &(link->dst_link->vnet_header);
660 struct ip_header *ip = &hdr->ip_hdr;
661 struct udp_header *udp = &hdr->udp_hdr;
662 udp->len = pkt_len + sizeof(struct udp_header);
663 ip->total_len = pkt_len + sizeof(struct udp_header) + sizeof(struct ip_header);
664 ip->cksum = ip_xsum(ip, sizeof(struct ip_header));
666 int hdr_size = sizeof(struct udp_link_header);
667 memcpy(&pkt->ext_hdr, hdr, hdr_size);
670 if ((link->dst_link->input((uchar_t *)&pkt->ext_hdr, pkt_len, link->dst_link->private_data)) != pkt_len) {
671 PrintDebug("VNET: Packet not sent properly to link: %d\n", link_index);
675 PrintDebug("VNET: Link protocol type not support\n");
678 } else if (link->type == LINK_INTERFACE) {
679 if ((link->dst_dev->input(link->dst_dev->vm, pkt->data, pkt_len, link->dst_dev->private_data)) != pkt_len) {
680 PrintDebug("VNET: Packet not sent properly to link: %d\n", link_index);
684 PrintDebug("Vnet: Wrong Edge type of link: %d\n", link_index);
688 PrintDebug("Vnet: HandleDataOverLink: Forward packet according to Route entry %d to link %d\n", route_index, link_index);
694 static int send_ethernet_pkt(uchar_t *data, int len, void *private_data) {
695 struct ethernet_pkt *pkt;
696 struct vnet_state_t *vnet_state = &g_vnet_state;
698 pkt = (struct ethernet_pkt *)V3_Malloc(sizeof(struct ethernet_pkt));
700 PrintError("VNET: Memory allocate fails\n");
704 memset(pkt, 0, sizeof(struct ethernet_pkt));
705 ethernet_packet_init(pkt, data, len);
706 v3_enqueue(vnet_state->g_inpkt_q, (addr_t)pkt);
708 #ifdef CONFIG_DEBUG_VNET
709 PrintDebug("VNET: send_pkt: transmitting packet: (size:%d)\n", (int)pkt->size);
710 print_packet((char *)data, len);
716 int v3_vnet_send_rawpkt(uchar_t * buf,
718 void *private_data) {
719 PrintDebug("VNET: In v3_vnet_send_rawpkt: pkt length %d\n", len);
721 return send_ethernet_pkt(buf, len, private_data);
724 //sending the packet from Dom0, should remove the link header
725 int v3_vnet_send_udppkt(uchar_t * buf,
727 void *private_data) {
728 uint_t hdr_len = sizeof(struct udp_link_header);
730 PrintDebug("VNET: In v3_vnet_send_udppkt: pkt length %d\n", len);
732 return send_ethernet_pkt((uchar_t *)(buf+hdr_len), len - hdr_len, private_data);
735 static int search_device(char * device_name) {
736 struct vnet_state_t *vnet_state = &g_vnet_state;
739 for (i = 0; i < MAX_LINKS; i++) {
740 if ((vnet_state->g_links.links[i] != NULL) && (vnet_state->g_links.links[i]->type == LINK_INTERFACE)) {
741 if (strcmp(device_name, vnet_state->g_links.links[i]->dst_dev->name) == 0) {
750 int v3_vnet_add_node(struct v3_vm_info *info,
753 int (*netif_input)(struct v3_vm_info * vm, uchar_t * pkt, uint_t size, void * private_data),
755 struct vnet_if_device * if_dev;
757 int idx = search_device(dev_name);
759 PrintDebug("VNET: register device: Already has device with the name %s\n", dev_name);
763 if_dev = (struct vnet_if_device *)V3_Malloc(sizeof(struct vnet_if_device));
764 if (if_dev == NULL) {
765 PrintError("VNET: Malloc fails\n");
769 strcpy(if_dev->name, dev_name);
770 strncpy(if_dev->mac_addr, mac, 6);
771 if_dev->input = netif_input;
772 if_dev->private_data = priv_data;
775 struct link_entry * link = (struct link_entry *)V3_Malloc(sizeof(struct link_entry));
776 link->type = LINK_INTERFACE;
777 link->dst_dev = if_dev;
779 idx = __add_link_entry(link);
781 if (idx < 0) return -1;
786 int v3_vnet_pkt_process() {
787 struct ethernet_pkt * pkt;
788 struct vnet_state_t *vnet_state = &g_vnet_state;
790 while ((pkt = (struct ethernet_pkt *)v3_dequeue(vnet_state->g_inpkt_q))!= NULL) {
791 if (handle_one_pkt(pkt) != -1) {
792 PrintDebug("VNET: vnet_check: handle one packet! pt length %d, pt type %d\n", (int)pkt->size, (int)pkt->type);
794 PrintDebug("VNET: vnet_check: Fail to forward one packet, discard it!\n");
797 V3_Free(pkt); // be careful here
803 static void vnet_state_init(struct vnet_state_t *vnet_state) {
806 /*initial links table */
807 for (i = 0; i < MAX_LINKS; i++) {
808 vnet_state->g_links.links[i] = NULL;
810 vnet_state->g_links.size = 0;
811 if(v3_lock_init(&(vnet_state->g_links.lock)) == -1){
812 PrintError("VNET: Failure to init lock for links table\n");
814 PrintDebug("VNET: Links table initiated\n");
816 /*initial routes table */
817 for (i = 0; i < MAX_ROUTES; i++) {
818 vnet_state->g_routes.routes[i] = NULL;
820 vnet_state->g_routes.size = 0;
821 if(v3_lock_init(&(vnet_state->g_routes.lock)) == -1){
822 PrintError("VNET: Failure to init lock for routes table\n");
824 PrintDebug("VNET: Routes table initiated\n");
826 /*initial pkt receiving queue */
827 vnet_state->g_inpkt_q = v3_create_queue();
828 v3_init_queue(vnet_state->g_inpkt_q);
829 PrintDebug("VNET: Receiving queue initiated\n");
831 /*initial routing cache */
832 init_route_cache(vnet_state);
837 vnet_state_init(&g_vnet_state);
839 PrintDebug("VNET Initialized\n");