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) 2010, 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/vm_guest_mem.h>
24 #include <palacios/vmm_lock.h>
25 #include <palacios/vmm_queue.h>
26 #include <palacios/vmm_sprintf.h>
28 #ifndef CONFIG_DEBUG_VNET
30 #define PrintDebug(fmt, args...)
34 /* for UDP encapuslation */
39 }__attribute__((packed));
54 }__attribute__((packed));
60 uint16_t csum;//set to zero, disable the xsum
61 }__attribute__((packed));
63 struct udp_link_header {
64 struct eth_header eth_hdr;
65 struct ip_header ip_hdr;
66 struct udp_header udp_hdr;
67 }__attribute__((packed));
68 /* end with UDP encapuslation structures */
76 uint16_t type; /* indicates layer 3 protocol type */
77 } __attribute__((packed));
83 struct v3_vm_info * vm;
84 struct v3_vnet_dev_ops dev_ops;
89 struct list_head node;
90 } __attribute__((packed));
94 struct v3_vm_info * vm;
95 struct v3_vnet_bridge_ops brg_ops;
99 } __attribute__((packed));
103 struct vnet_route_info {
104 struct v3_vnet_route route_def;
106 struct vnet_dev * dst_dev;
107 struct vnet_dev * src_dev;
109 struct list_head node;
110 struct list_head match_node; // used for route matching
115 uint8_t hash_buf[VNET_HASH_SIZE];
118 struct vnet_route_info * routes[0];
119 } __attribute__((packed));
122 #define BUF_SIZE 4096
127 struct v3_vnet_pkt pkts[BUF_SIZE];
132 struct list_head routes;
133 struct list_head devs;
138 struct vnet_brg_dev *bridge;
142 uint8_t sidecores; /* 0 -vnet not running on sidecore, > 0, number of extra cores that can be used by VNET */
143 uint64_t cores_map; /* bitmaps for which cores can be used by VNET for sidecore, maxium 64 */
145 struct hashtable * route_cache;
151 #ifdef CONFIG_DEBUG_VNET
152 static inline void mac_to_string(char mac[6], char * buf) {
153 snprintf(buf, 100, "%d:%d:%d:%d:%d:%d",
154 mac[0], mac[1], mac[2],
155 mac[3], mac[4], mac[5]);
158 static void print_route(struct vnet_route_info *route){
161 mac_to_string(route->route_def.src_mac, str);
162 PrintDebug("Src Mac (%s), src_qual (%d)\n",
163 str, route->route_def.src_mac_qual);
164 mac_to_string(route->route_def.dst_mac, str);
165 PrintDebug("Dst Mac (%s), dst_qual (%d)\n",
166 str, route->route_def.dst_mac_qual);
167 PrintDebug("Src dev id (%d), src type (%d)",
168 route->route_def.src_id,
169 route->route_def.src_type);
170 PrintDebug("Dst dev id (%d), dst type (%d)\n",
171 route->route_def.dst_id,
172 route->route_def.dst_type);
173 if (route->route_def.dst_type == LINK_INTERFACE) {
174 PrintDebug("dst_dev (%p), dst_dev_id (%d), dst_dev_ops(%p), dst_dev_data (%p)\n",
176 route->dst_dev->dev_id,
177 (void *)&(route->dst_dev->dev_ops),
178 route->dst_dev->private_data);
182 static void dump_routes(){
183 struct vnet_route_info *route;
186 PrintDebug("\n========Dump routes starts ============\n");
187 list_for_each_entry(route, &(vnet_state.routes), node) {
188 PrintDebug("\nroute %d:\n", ++i);
192 PrintDebug("\n========Dump routes end ============\n");
199 * A VNET packet is a packed struct with the hashed fields grouped together.
200 * This means we can generate the hash from an offset into the pkt struct
202 static inline uint_t hash_fn(addr_t hdr_ptr) {
203 uint8_t * hdr_buf = (uint8_t *)hdr_ptr;
205 return v3_hash_buffer(hdr_buf, VNET_HASH_SIZE);
208 static inline int hash_eq(addr_t key1, addr_t key2) {
209 return (memcmp((uint8_t *)key1, (uint8_t *)key2, VNET_HASH_SIZE) == 0);
212 static int add_route_to_cache(const struct v3_vnet_pkt * pkt, struct route_list * routes) {
213 memcpy(routes->hash_buf, pkt->hash_buf, VNET_HASH_SIZE);
215 if (v3_htable_insert(vnet_state.route_cache, (addr_t)routes->hash_buf, (addr_t)routes) == 0) {
216 PrintError("Vnet: Failed to insert new route entry to the cache\n");
223 static int clear_hash_cache() {
225 v3_free_htable(vnet_state.route_cache, 1, 1);
226 vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
231 static int look_into_cache(const struct v3_vnet_pkt * pkt, struct route_list ** routes) {
233 *routes = (struct route_list *)v3_htable_search(vnet_state.route_cache, (addr_t)(pkt->hash_buf));
239 static struct vnet_dev * find_dev_by_id(int idx) {
240 struct vnet_dev * dev = NULL;
242 list_for_each_entry(dev, &(vnet_state.devs), node) {
243 int dev_id = dev->dev_id;
252 static struct vnet_dev * find_dev_by_mac(char mac[6]) {
253 struct vnet_dev * dev = NULL;
255 list_for_each_entry(dev, &(vnet_state.devs), node) {
256 if (!memcmp(dev->mac_addr, mac, 6))
263 int v3_vnet_id_by_mac(char mac[6]){
265 struct vnet_dev *dev = find_dev_by_mac(mac);
274 int v3_vnet_add_route(struct v3_vnet_route route) {
275 struct vnet_route_info * new_route = NULL;
278 new_route = (struct vnet_route_info *)V3_Malloc(sizeof(struct vnet_route_info));
279 memset(new_route, 0, sizeof(struct vnet_route_info));
281 PrintDebug("Vnet: vnet_add_route_entry: dst_id: %d, dst_type: %d\n",
282 route.dst_id, route.dst_type);
284 memcpy(new_route->route_def.src_mac, route.src_mac, 6);
285 memcpy(new_route->route_def.dst_mac, route.dst_mac, 6);
286 new_route->route_def.src_mac_qual = route.src_mac_qual;
287 new_route->route_def.dst_mac_qual = route.dst_mac_qual;
288 new_route->route_def.dst_id = route.dst_id;
289 new_route->route_def.dst_type = route.dst_type;
290 new_route->route_def.src_id = route.src_id;
291 new_route->route_def.src_type = route.src_type;
293 if (new_route->route_def.dst_type == LINK_INTERFACE) {
294 new_route->dst_dev = find_dev_by_id(new_route->route_def.dst_id);
297 if (new_route->route_def.src_type == LINK_INTERFACE) {
298 new_route->src_dev = find_dev_by_id(new_route->route_def.src_id);
301 flags = v3_lock_irqsave(vnet_state.lock);
303 list_add(&(new_route->node), &(vnet_state.routes));
306 v3_unlock_irqrestore(vnet_state.lock, flags);
309 #ifdef CONFIG_DEBUG_VNET
318 /* At the end allocate a route_list
319 * This list will be inserted into the cache so we don't need to free it
321 static struct route_list * match_route(const struct v3_vnet_pkt * pkt) {
322 struct vnet_route_info * route = NULL;
323 struct route_list * matches = NULL;
326 struct list_head match_list;
327 struct eth_hdr * hdr = (struct eth_hdr *)(pkt->data);
328 uint8_t src_type = pkt->src_type;
329 uint32_t src_link = pkt->src_id;
331 #ifdef CONFIG_DEBUG_VNET
336 mac_to_string(hdr->src_mac, src_str);
337 mac_to_string(hdr->dst_mac, dst_str);
338 PrintDebug("Vnet: match_route. pkt: SRC(%s), DEST(%s)\n", src_str, dst_str);
342 INIT_LIST_HEAD(&match_list);
344 #define UPDATE_MATCHES(rank) do { \
345 if (max_rank < (rank)) { \
347 INIT_LIST_HEAD(&match_list); \
349 list_add(&(route->match_node), &match_list); \
351 } else if (max_rank == (rank)) { \
352 list_add(&(route->match_node), &match_list); \
358 list_for_each_entry(route, &(vnet_state.routes), node) {
359 struct v3_vnet_route * route_def = &(route->route_def);
361 // CHECK SOURCE TYPE HERE
362 if ( (route_def->src_type != LINK_ANY) &&
363 ( (route_def->src_type != src_type) ||
364 ( (route_def->src_id != src_link) &&
365 (route_def->src_id != (uint32_t)-1)))) {
370 if ((route_def->dst_mac_qual == MAC_ANY) &&
371 (route_def->src_mac_qual == MAC_ANY)) {
375 if (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) {
376 if (route_def->src_mac_qual != MAC_NOT) {
377 if (route_def->dst_mac_qual == MAC_ANY) {
379 } else if (route_def->dst_mac_qual != MAC_NOT &&
380 memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
386 if (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
387 if (route_def->dst_mac_qual != MAC_NOT) {
388 if (route_def->src_mac_qual == MAC_ANY) {
390 } else if ((route_def->src_mac_qual != MAC_NOT) &&
391 (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {
397 if ((route_def->dst_mac_qual == MAC_NOT) &&
398 (memcmp(route_def->dst_mac, hdr->dst_mac, 6) != 0)) {
399 if (route_def->src_mac_qual == MAC_ANY) {
401 } else if ((route_def->src_mac_qual != MAC_NOT) &&
402 (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {
407 if ((route_def->src_mac_qual == MAC_NOT) &&
408 (memcmp(route_def->src_mac, hdr->src_mac, 6) != 0)) {
409 if (route_def->dst_mac_qual == MAC_ANY) {
411 } else if ((route_def->dst_mac_qual != MAC_NOT) &&
412 (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0)) {
418 if ( (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) &&
419 (route_def->dst_mac_qual == MAC_NONE)) {
424 PrintDebug("Vnet: match_route: Matches=%d\n", num_matches);
426 if (num_matches == 0) {
430 matches = (struct route_list *)V3_Malloc(sizeof(struct route_list) +
431 (sizeof(struct vnet_route_info *) * num_matches));
433 matches->num_routes = num_matches;
437 list_for_each_entry(route, &match_list, match_node) {
438 matches->routes[i++] = route;
445 static int send_to_bridge(struct v3_vnet_pkt * pkt){
446 struct vnet_brg_dev *bridge = vnet_state.bridge;
448 if (bridge == NULL) {
449 PrintError("VNET: No bridge to sent data to links\n");
453 return bridge->brg_ops.input(bridge->vm, pkt, 1, bridge->private_data);
457 /* enable a vnet device, notify VNET it can send pkts to it */
458 int v3_vnet_enable_device(int dev_id){
459 struct vnet_dev *dev = find_dev_by_id(dev_id);
465 if(!dev->rx_disabled)
468 flags = v3_lock_irqsave(vnet_state.lock);
469 dev->rx_disabled = 0;
470 v3_unlock_irqrestore(vnet_state.lock, flags);
472 /* TODO: Wake up all other guests who are trying to send pkts */
474 list_for_each_entry(dev, &(vnet_state.devs), node) {
475 if (dev->dev_id != dev_id)
476 dev->dev_ops.start_tx(dev->private_data);
482 /* Notify VNET to stop sending pkts to it */
483 int v3_vnet_disable_device(int dev_id){
484 struct vnet_dev *dev = find_dev_by_id(dev_id);
490 flags = v3_lock_irqsave(vnet_state.lock);
491 dev->rx_disabled = 1;
492 v3_unlock_irqrestore(vnet_state.lock, flags);
495 /* TODO: Notify all other guests to stop send pkts */
497 list_for_each_entry(dev, &(vnet_state.devs), node) {
498 if (dev->dev_id != dev_id)
499 dev->dev_ops.stop_tx(dev->private_data);
505 int v3_vnet_send_pkt(struct v3_vnet_pkt * pkt, void * private_data) {
506 struct route_list * matched_routes = NULL;
510 #ifdef CONFIG_DEBUG_VNET
512 struct eth_hdr * hdr = (struct eth_hdr *)(pkt->header);
516 mac_to_string(hdr->src_mac, src_str);
517 mac_to_string(hdr->dst_mac, dest_str);
518 int cpu = V3_Get_CPU();
519 PrintDebug("Vnet: on cpu %d, HandleDataOverLink. SRC(%s), DEST(%s), pkt size: %d\n", cpu, src_str, dest_str, pkt->size);
523 flags = v3_lock_irqsave(vnet_state.lock);
525 look_into_cache(pkt, &matched_routes);
527 if (matched_routes == NULL) {
528 PrintError("Vnet: send pkt Looking into routing table\n");
530 matched_routes = match_route(pkt);
532 if (matched_routes) {
533 add_route_to_cache(pkt, matched_routes);
535 PrintDebug("Could not find route for packet... discards packet\n");
536 v3_unlock_irqrestore(vnet_state.lock, flags);
537 return 0; /* do we return -1 here?*/
541 v3_unlock_irqrestore(vnet_state.lock, flags);
543 PrintDebug("Vnet: send pkt route matches %d\n", matched_routes->num_routes);
545 for (i = 0; i < matched_routes->num_routes; i++) {
546 struct vnet_route_info * route = matched_routes->routes[i];
548 if (route->route_def.dst_type == LINK_EDGE) {
549 pkt->dst_type = LINK_EDGE;
550 pkt->dst_id = route->route_def.dst_id;
552 if (send_to_bridge(pkt) == -1) {
553 PrintDebug("VNET: Packet not sent properly to bridge\n");
556 } else if (route->route_def.dst_type == LINK_INTERFACE) {
557 if (!route->dst_dev->rx_disabled){
558 if(route->dst_dev->dev_ops.input(route->dst_dev->vm, pkt, route->dst_dev->private_data) == -1) {
559 PrintDebug("VNET: Packet not sent properly\n");
564 PrintError("VNET: Wrong Edge type\n");
567 PrintDebug("VNET: Forward one packet according to Route %d\n", i);
573 int v3_vnet_add_dev(struct v3_vm_info *vm, uint8_t mac[6],
574 struct v3_vnet_dev_ops *ops,
576 struct vnet_dev * new_dev = NULL;
579 new_dev = (struct vnet_dev *)V3_Malloc(sizeof(struct vnet_dev));
581 if (new_dev == NULL) {
582 PrintError("VNET: Malloc fails\n");
586 memcpy(new_dev->mac_addr, mac, 6);
587 new_dev->dev_ops.input = ops->input;
588 new_dev->dev_ops.poll = ops->poll;
589 new_dev->private_data = priv_data;
593 flags = v3_lock_irqsave(vnet_state.lock);
595 if (!find_dev_by_mac(mac)) {
596 list_add(&(new_dev->node), &(vnet_state.devs));
597 new_dev->dev_id = ++vnet_state.num_devs;
600 v3_unlock_irqrestore(vnet_state.lock, flags);
602 /* if the device was found previosly the id should still be 0 */
603 if (new_dev->dev_id == 0) {
604 PrintError("Device Alrady exists\n");
608 PrintDebug("Vnet: Add Device: dev_id %d\n", new_dev->dev_id);
610 return new_dev->dev_id;
614 /* TODO: Still need to figure out how to handle this multicore part --Lei
616 void v3_vnet_poll(struct v3_vm_info *vm){
617 struct vnet_dev * dev = NULL;
619 switch (vnet_state.sidecores) {
621 list_for_each_entry(dev, &(vnet_state.devs), node) {
623 dev->dev_ops.poll(vm, dev->private_data);
630 list_for_each_entry(dev, &(vnet_state.devs), node) {
631 int cpu_id = vm->cores[0].cpu_id + 2; /* temporary here, should use vnet_state.cores_map */
632 struct v3_vnet_dev_xcall_args dev_args; /* could cause problem here -LX */
634 dev_args.private_data = dev->private_data;
635 V3_Call_On_CPU(cpu_id, dev->dev_ops.poll_xcall, (void *)&dev_args);
643 int v3_vnet_add_bridge(struct v3_vm_info * vm,
644 struct v3_vnet_bridge_ops *ops,
648 struct vnet_brg_dev * tmp_bridge = NULL;
650 flags = v3_lock_irqsave(vnet_state.lock);
652 if (vnet_state.bridge == NULL) {
654 vnet_state.bridge = (void *)1;
657 v3_unlock_irqrestore(vnet_state.lock, flags);
659 if (bridge_free == 0) {
660 PrintError("Bridge already set\n");
664 tmp_bridge = (struct vnet_brg_dev *)V3_Malloc(sizeof(struct vnet_brg_dev));
666 if (tmp_bridge == NULL) {
667 PrintError("Malloc Fails\n");
668 vnet_state.bridge = NULL;
673 tmp_bridge->brg_ops.input = ops->input;
674 tmp_bridge->brg_ops.xcall_input = ops->xcall_input;
675 tmp_bridge->brg_ops.polling_pkt = ops->polling_pkt;
676 tmp_bridge->private_data = priv_data;
677 tmp_bridge->disabled = 0;
679 /* make this atomic to avoid possible race conditions */
680 flags = v3_lock_irqsave(vnet_state.lock);
681 vnet_state.bridge = tmp_bridge;
682 v3_unlock_irqrestore(vnet_state.lock, flags);
689 int v3_vnet_disable_bridge() {
692 flags = v3_lock_irqsave(vnet_state.lock);
694 if (vnet_state.bridge != NULL) {
695 vnet_state.bridge->disabled = 1;
698 v3_unlock_irqrestore(vnet_state.lock, flags);
704 int v3_vnet_enable_bridge() {
707 flags = v3_lock_irqsave(vnet_state.lock);
709 if (vnet_state.bridge != NULL) {
710 vnet_state.bridge->disabled = 0;
713 v3_unlock_irqrestore(vnet_state.lock, flags);
720 memset(&vnet_state, 0, sizeof(vnet_state));
722 INIT_LIST_HEAD(&(vnet_state.routes));
723 INIT_LIST_HEAD(&(vnet_state.devs));
725 vnet_state.num_devs = 0;
726 vnet_state.num_routes = 0;
728 PrintDebug("VNET: Links and Routes tables initiated\n");
730 if (v3_lock_init(&(vnet_state.lock)) == -1){
731 PrintError("VNET: Failure to init lock for routes table\n");
733 PrintDebug("VNET: Locks initiated\n");
735 vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
737 if (vnet_state.route_cache == NULL) {
738 PrintError("Vnet: Route Cache Init Fails\n");
742 vnet_state.sidecores = 0;
743 vnet_state.cores_map = 0;
745 PrintDebug("VNET: initiated\n");