Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Fix to vnet/virtio networking
[palacios.git] / palacios / src / palacios / vmm_vnet.c
1 /* 
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.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
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.
14  *
15  * Author: Lei Xia <lxia@northwestern.edu>
16  *         Yuan Tang <ytang@northwestern.edu>
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21  
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>
27
28 #ifndef CONFIG_DEBUG_VNET
29 #undef PrintDebug
30 #define PrintDebug(fmt, args...)
31 #endif
32
33
34
35 struct eth_hdr {
36     uint8_t dst_mac[6];
37     uint8_t src_mac[6];
38     uint16_t type; // indicates layer 3 protocol type
39 } __attribute__((packed));
40
41
42
43
44
45 struct vnet_dev {
46
47     uint8_t mac_addr[6];
48     struct v3_vm_info * vm;
49     
50     int (*input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt,  void * private_data);
51     void * private_data;
52     
53     int dev_id;
54     struct list_head node;
55 } __attribute__((packed));
56
57
58
59
60
61 struct vnet_route_info {
62     struct v3_vnet_route route_def;
63
64     struct vnet_dev * dst_dev;
65     struct vnet_dev * src_dev;
66
67     struct list_head node;
68     struct list_head match_node; // used for route matching
69 };
70
71
72
73
74 struct route_list {
75     uint8_t hash_buf[VNET_HASH_SIZE];
76
77     uint32_t num_routes;
78     struct vnet_route_info * routes[0];
79 } __attribute__((packed));
80
81
82
83 static struct {
84     struct list_head routes;
85     struct list_head devs;
86
87     int num_routes;
88     int num_devs;
89
90     v3_lock_t lock;
91
92     struct gen_queue * inpkt_q;
93     struct hashtable * route_cache;
94
95 } vnet_state;
96
97
98
99
100 #ifdef CONFIG_DEBUG_VNET
101 static inline void mac_to_string(char mac[6], char * buf) {
102     snprintf(buf, 50, "%x:%x:%x:%x:%x:%x", 
103              mac[0], mac[1], mac[2],
104              mac[3], mac[4], mac[5]);
105 }
106
107 static void print_route(struct vnet_route_info *route){
108     char str[50];
109
110     mac_to_string(route->route_def.src_mac, str);
111     PrintDebug("Src Mac (%s),  src_qual (%d)\n", 
112                         str, route->route_def.src_mac_qual);
113     mac_to_string(route->route_def.dst_mac, str);
114     PrintDebug("Dst Mac (%s),  dst_qual (%d)\n", 
115                         str, route->route_def.dst_mac_qual);
116     PrintDebug("Src dev id (%d), src type (%d)", 
117                         route->route_def.src_id, 
118                         route->route_def.src_type);
119     PrintDebug("Dst dev id (%d), dst type (%d)", 
120                         route->route_def.dst_id, 
121                         route->route_def.dst_type);
122     PrintDebug("dst_dev (%p), dst_dev_id (%d), dst_dev_input (%p), dst_dev_data (%p)\n",
123                                         route->dst_dev,
124                                         route->dst_dev->dev_id,
125                                         route->dst_dev->input,
126                                         route->dst_dev->private_data);
127
128 }
129
130 static void dump_routes(){
131         struct vnet_route_info *route;
132
133         int i = 0;
134         PrintDebug("\n========Dump routes starts ============\n");
135         list_for_each_entry(route, &(vnet_state.routes), node) {
136                 PrintDebug("\nroute %d:\n", ++i);
137                 
138                 print_route(route);
139         }
140         PrintDebug("\n========Dump routes end ============\n");
141 }
142
143 #endif
144
145
146 /* 
147  * A VNET packet is a packed struct with the hashed fields grouped together.
148  * This means we can generate the hash from an offset into the pkt struct
149  */
150 static inline uint_t hash_fn(addr_t hdr_ptr) {    
151     uint8_t * hdr_buf = (uint8_t *)&(hdr_ptr);
152     
153     return v3_hash_buffer(hdr_buf, VNET_HASH_SIZE);
154 }
155
156 static inline int hash_eq(addr_t key1, addr_t key2) {
157     return (memcmp((uint8_t *)key1, (uint8_t *)key2, VNET_HASH_SIZE) == 0);
158 }
159
160
161 static int add_route_to_cache(struct v3_vnet_pkt * pkt, struct route_list * routes) {
162     memcpy(routes->hash_buf, pkt->hash_buf, VNET_HASH_SIZE);    
163
164     if (v3_htable_insert(vnet_state.route_cache, (addr_t)routes->hash_buf, (addr_t)routes) == 0) {
165         PrintError("Vnet: Failed to insert new route entry to the cache\n");
166         return -1;
167     }
168     
169     return 0;
170 }
171
172 static int clear_hash_cache() {
173
174     v3_free_htable(vnet_state.route_cache, 1, 1);
175     vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
176
177     return 0;
178 }
179
180 static int look_into_cache(struct v3_vnet_pkt * pkt, struct route_list ** routes) {
181     
182     *routes = (struct route_list *)v3_htable_search(vnet_state.route_cache, (addr_t)pkt);
183    
184     return 0;
185 }
186
187
188 static struct vnet_dev * find_dev_by_id(int idx) {
189     struct vnet_dev * dev = NULL; 
190     
191     list_for_each_entry(dev, &(vnet_state.devs), node) {
192         int dev_id = dev->dev_id;
193
194         if (dev_id == idx)
195             return dev;
196     }
197
198     return NULL;
199 }
200
201 static struct vnet_dev * find_dev_by_mac(char mac[6]) {
202     struct vnet_dev * dev = NULL; 
203     
204     list_for_each_entry(dev, &(vnet_state.devs), node) {
205         if (!memcmp(dev->mac_addr, mac, 6))
206             return dev;
207     }
208
209     return NULL;
210 }
211
212 int get_device_id_by_mac(char mac[6]){
213
214     struct vnet_dev *dev = find_dev_by_mac(mac);
215
216     if (dev == NULL)
217         return -1;
218
219     return dev->dev_id;
220 }
221
222
223 int v3_vnet_add_route(struct v3_vnet_route route) {
224     struct vnet_route_info * new_route = NULL;
225     unsigned long flags; 
226
227     new_route = (struct vnet_route_info *)V3_Malloc(sizeof(struct vnet_route_info));
228     memset(new_route, 0, sizeof(struct vnet_route_info));
229
230     PrintDebug("Vnet: vnet_add_route_entry: dst_id: %d, dst_type: %d\n",
231                         route.dst_id, route.dst_type);  
232     
233     memcpy(new_route->route_def.src_mac, route.src_mac, 6);
234     memcpy(new_route->route_def.dst_mac, route.dst_mac, 6);
235     new_route->route_def.src_mac_qual = route.src_mac_qual;
236     new_route->route_def.dst_mac_qual = route.dst_mac_qual;
237     new_route->route_def.dst_id = route.dst_id;
238     new_route->route_def.dst_type = route.dst_type;
239     new_route->route_def.src_id = route.src_id;
240     new_route->route_def.src_type = route.src_type;
241
242     if (new_route->route_def.dst_type == LINK_INTERFACE) {
243         new_route->dst_dev = find_dev_by_id(new_route->route_def.dst_id);
244         PrintDebug("Vnet: Add route, get device: dev_id %d, input : %p, private_data %p\n",
245                         new_route->dst_dev->dev_id, new_route->dst_dev->input, new_route->dst_dev->private_data);
246     }
247
248     if (new_route->route_def.src_type == LINK_INTERFACE) {
249         new_route->src_dev = find_dev_by_id(new_route->route_def.src_id);
250     }
251
252     flags = v3_lock_irqsave(vnet_state.lock);
253     list_add(&(new_route->node), &(vnet_state.routes));
254     v3_unlock_irqrestore(vnet_state.lock, flags);
255    
256     clear_hash_cache();
257
258 #ifdef CONFIG_DEBUG_VNET
259     dump_routes();
260 #endif
261
262     return 0;
263 }
264
265
266
267 // At the end allocate a route_list
268 // This list will be inserted into the cache so we don't need to free it
269 static struct route_list * match_route(struct v3_vnet_pkt * pkt) {
270     struct vnet_route_info * route = NULL; 
271     struct route_list * matches = NULL;
272     int num_matches = 0;
273     int max_rank = 0;
274     struct list_head match_list;
275     struct eth_hdr * hdr = (struct eth_hdr *)(pkt->data);
276     uint8_t src_type = pkt->src_type;
277     uint32_t src_link = pkt->src_id;
278
279 #ifdef CONFIG_DEBUG_VNET
280     {
281         char dst_str[50];
282         char src_str[50];
283
284         mac_to_string(hdr->src_mac, src_str);  
285         mac_to_string(hdr->dst_mac, dst_str);
286         PrintDebug("Vnet: match_route. pkt: SRC(%s), DEST(%s)\n", src_str, dst_str);
287     }
288 #endif
289
290     INIT_LIST_HEAD(&match_list);
291     
292 #define UPDATE_MATCHES(rank) do {                               \
293         if (max_rank < (rank)) {                                \
294             max_rank = (rank);                                  \
295             INIT_LIST_HEAD(&match_list);                        \
296                                                                 \
297             list_add(&(route->match_node), &match_list);        \
298             num_matches = 1;                                    \
299         } else if (max_rank == (rank)) {                        \
300             list_add(&(route->match_node), &match_list);        \
301             num_matches++;                                      \
302         }                                                       \
303     } while (0)
304     
305
306     list_for_each_entry(route, &(vnet_state.routes), node) {
307         struct v3_vnet_route * route_def = &(route->route_def);
308
309         // CHECK SOURCE TYPE HERE
310         if ( (route_def->src_type != LINK_ANY) && 
311              ( (route_def->src_type != src_type) || 
312                ( (route_def->src_id != src_link) &&
313                  (route_def->src_id != (uint32_t)-1)))) {
314             continue;
315         }
316
317
318         if ((route_def->dst_mac_qual == MAC_ANY) &&
319             (route_def->src_mac_qual == MAC_ANY)) {      
320             UPDATE_MATCHES(3);
321         }
322         
323         if (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) {
324             if (route_def->src_mac_qual != MAC_NOT) {
325                 if (route_def->dst_mac_qual == MAC_ANY) {
326                     UPDATE_MATCHES(6);
327                 } else if (route_def->dst_mac_qual != MAC_NOT &&
328                            memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
329                     UPDATE_MATCHES(8);
330                 }
331             }
332         }
333             
334         if (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
335             if (route_def->dst_mac_qual != MAC_NOT) {
336                 if (route_def->src_mac_qual == MAC_ANY) {
337                     UPDATE_MATCHES(6);
338                 } else if ((route_def->src_mac_qual != MAC_NOT) && 
339                            (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {
340                     UPDATE_MATCHES(8);
341                 }
342             }
343         }
344             
345         if ((route_def->dst_mac_qual == MAC_NOT) &&
346             (memcmp(route_def->dst_mac, hdr->dst_mac, 6) != 0)) {
347             if (route_def->src_mac_qual == MAC_ANY) {
348                 UPDATE_MATCHES(5);
349             } else if ((route_def->src_mac_qual != MAC_NOT) && 
350                        (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {     
351                 UPDATE_MATCHES(7);
352             }
353         }
354         
355         if ((route_def->src_mac_qual == MAC_NOT) &&
356             (memcmp(route_def->src_mac, hdr->src_mac, 6) != 0)) {
357             if (route_def->dst_mac_qual == MAC_ANY) {
358                 UPDATE_MATCHES(5);
359             } else if ((route_def->dst_mac_qual != MAC_NOT) &&
360                        (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0)) {
361                 UPDATE_MATCHES(7);
362             }
363         }
364         
365         // Default route
366         if ( (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) &&
367              (route_def->dst_mac_qual == MAC_NONE)) {
368             UPDATE_MATCHES(4);
369         }
370     }
371
372     PrintDebug("Vnet: match_route: Matches=%d\n", num_matches);
373
374     if (num_matches == 0) {
375         return NULL;
376     }
377
378     matches = V3_Malloc(sizeof(struct route_list) + 
379                         (sizeof(struct vnet_route_info *) * num_matches));
380
381     matches->num_routes = num_matches;
382
383     {
384         int i = 0;
385         list_for_each_entry(route, &match_list, match_node) {
386             matches->routes[i++] = route;
387         }
388     }
389
390     return matches;
391 }
392
393 static int handle_one_pkt(struct v3_vnet_pkt * pkt, void *private_data) {
394     struct route_list * matched_routes = NULL;
395     unsigned long flags;
396     int i;
397
398
399 #ifdef CONFIG_DEBUG_VNET
400    {
401         struct eth_hdr * hdr = (struct eth_hdr *)(pkt->data);
402         char dest_str[30];
403         char src_str[30];
404
405         mac_to_string(hdr->src_mac, src_str);  
406         mac_to_string(hdr->dst_mac, dest_str);
407         PrintDebug("Vnet: HandleDataOverLink. SRC(%s), DEST(%s)\n", src_str, dest_str);
408    }
409 #endif
410
411 #ifdef CONFIG_VNET_PROFILE
412     struct guest_info *core = (struct guest_info *)private_data;
413     uint64_t start, end;
414     rdtscll(start);
415 #endif
416
417     flags = v3_lock_irqsave(vnet_state.lock);
418
419     look_into_cache(pkt, &matched_routes);
420         
421     if (matched_routes == NULL) {  
422         matched_routes = match_route(pkt);
423                 
424         if (matched_routes) {
425             add_route_to_cache(pkt, matched_routes);
426         } else {
427             PrintDebug("Could not find route for packet...\n");
428             v3_unlock_irqrestore(vnet_state.lock, flags);
429             return -1;
430         }
431     }
432
433     v3_unlock_irqrestore(vnet_state.lock, flags);
434
435 #ifdef CONFIG_VNET_PROFILE
436     {
437         rdtscll(end);
438         core->vnet_times.time_route_lookup = end - start;
439     }
440 #endif
441
442     PrintDebug("Vnet: HandleOnePacket: %d\n", matched_routes->num_routes);
443     for (i = 0; i < matched_routes->num_routes; i++) {
444          struct vnet_route_info * route = matched_routes->routes[i];
445         
446         if (route->route_def.dst_type == LINK_EDGE) {
447
448         } else if (route->route_def.dst_type == LINK_INTERFACE) {
449             if (route->dst_dev->input(route->dst_dev->vm, pkt, route->dst_dev->private_data) == -1) {
450                 PrintDebug("VNET: Packet not sent properly\n");
451                 continue;
452              }
453         } else {
454             PrintDebug("Vnet: Wrong Edge type\n");
455             continue;
456         }
457
458         PrintDebug("Vnet: HandleOnePacket: Forward packet according to Route\n");
459     }
460
461 #ifdef CONFIG_VNET_PROFILE
462     {
463         rdtscll(start);
464         core->vnet_times.time_copy_to_guest = start - end;
465     }
466 #endif
467     
468     return 0;
469 }
470
471 int v3_vnet_send_pkt(struct v3_vnet_pkt * pkt, void *private_data) {
472     PrintDebug("In Vnet Send: pkt size: %d\n", pkt->size);
473                 
474     if (handle_one_pkt(pkt, private_data) != -1) {
475         PrintDebug("VNET: send one packet! pt length %d\n", pkt->size);  
476     } else {
477         PrintDebug("VNET: Fail to forward one packet, discard it!\n"); 
478     }
479
480     return 0;
481 }
482
483 int v3_vnet_add_dev(struct v3_vm_info *vm,uint8_t mac[6], 
484                     int (*netif_input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt, void * private_data), 
485                     void * priv_data){
486     struct vnet_dev * new_dev = NULL;
487     unsigned long flags;
488     int dev_id;
489
490     flags = v3_lock_irqsave(vnet_state.lock);
491         
492     new_dev = find_dev_by_mac(mac);
493
494     if (new_dev) {
495         PrintDebug("VNET: register device: Already has device with the same mac\n");
496         dev_id = -1;
497         goto exit;
498     }
499     
500     new_dev = (struct vnet_dev *)V3_Malloc(sizeof(struct vnet_dev)); 
501
502     if (new_dev == NULL) {
503         PrintError("VNET: Malloc fails\n");
504         dev_id = -1;
505         goto exit;
506     }
507    
508     memcpy(new_dev->mac_addr, mac, 6);
509     new_dev->input = netif_input;
510     new_dev->private_data = priv_data;
511     new_dev->vm = vm;
512         
513     list_add(&(new_dev->node), &(vnet_state.devs));
514     vnet_state.num_devs ++;
515     new_dev->dev_id = vnet_state.num_devs;
516     dev_id = new_dev->dev_id;
517
518     PrintDebug("Vnet: Add Device: dev_id %d, input : %p, private_data %p\n",
519                         new_dev->dev_id, new_dev->input, new_dev->private_data);
520
521 exit:
522         
523     v3_unlock_irqrestore(vnet_state.lock, flags);
524  
525     return dev_id;
526 }
527
528
529 int V3_init_vnet() {
530         
531     INIT_LIST_HEAD(&(vnet_state.routes));
532     INIT_LIST_HEAD(&(vnet_state.devs));
533
534     vnet_state.num_devs = 0;
535     vnet_state.num_routes = 0;
536
537     PrintDebug("VNET: Links and Routes tables initiated\n");
538
539     if (v3_lock_init(&(vnet_state.lock)) == -1){
540         PrintError("VNET: Failure to init lock for routes table\n");
541     }
542
543     PrintDebug("VNET: Locks initiated\n");
544
545     vnet_state.inpkt_q = v3_create_queue();
546     v3_init_queue(vnet_state.inpkt_q);
547     PrintDebug("VNET: Receiving queue initiated\n");
548
549     vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
550
551     if (vnet_state.route_cache == NULL) {
552         PrintError("Vnet: Route Cache Init Fails\n");
553         return -1;
554     }
555
556     PrintDebug("VNET: initiated\n");
557
558     return 0;
559 }