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.


cac5dc0de5bc022d06735dba6b308222684d4f01
[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->hash_buf));
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         PrintDebug("Vnet: can not find route in cache, looking into routing table\n");
423         
424         matched_routes = match_route(pkt);
425                 
426         if (matched_routes) {
427             add_route_to_cache(pkt, matched_routes);
428         } else {
429             PrintDebug("Could not find route for packet...\n");
430             v3_unlock_irqrestore(vnet_state.lock, flags);
431             return -1;
432         }
433     }
434
435     v3_unlock_irqrestore(vnet_state.lock, flags);
436
437 #ifdef CONFIG_VNET_PROFILE
438     {
439         rdtscll(end);
440         core->vnet_times.time_route_lookup = end - start;
441     }
442 #endif
443
444     PrintDebug("Vnet: HandleOnePacket: %d\n", matched_routes->num_routes);
445     for (i = 0; i < matched_routes->num_routes; i++) {
446          struct vnet_route_info * route = matched_routes->routes[i];
447         
448         if (route->route_def.dst_type == LINK_EDGE) {
449
450         } else if (route->route_def.dst_type == LINK_INTERFACE) {
451             if (route->dst_dev->input(route->dst_dev->vm, pkt, route->dst_dev->private_data) == -1) {
452                 PrintDebug("VNET: Packet not sent properly\n");
453                 continue;
454              }
455         } else {
456             PrintDebug("Vnet: Wrong Edge type\n");
457             continue;
458         }
459
460         PrintDebug("Vnet: HandleOnePacket: Forward packet according to Route\n");
461     }
462
463 #ifdef CONFIG_VNET_PROFILE
464     {
465         rdtscll(start);
466         core->vnet_times.time_copy_to_guest = start - end;
467     }
468 #endif
469     
470     return 0;
471 }
472
473 int v3_vnet_send_pkt(struct v3_vnet_pkt * pkt, void *private_data) {
474     PrintDebug("In Vnet Send: pkt size: %d\n", pkt->size);
475                 
476     if (handle_one_pkt(pkt, private_data) != -1) {
477         PrintDebug("VNET: send one packet! pt length %d\n", pkt->size);  
478     } else {
479         PrintDebug("VNET: Fail to forward one packet, discard it!\n"); 
480     }
481
482     return 0;
483 }
484
485 int v3_vnet_add_dev(struct v3_vm_info *vm,uint8_t mac[6], 
486                     int (*netif_input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt, void * private_data), 
487                     void * priv_data){
488     struct vnet_dev * new_dev = NULL;
489     unsigned long flags;
490     int dev_id;
491
492     flags = v3_lock_irqsave(vnet_state.lock);
493         
494     new_dev = find_dev_by_mac(mac);
495
496     if (new_dev) {
497         PrintDebug("VNET: register device: Already has device with the same mac\n");
498         dev_id = -1;
499         goto exit;
500     }
501     
502     new_dev = (struct vnet_dev *)V3_Malloc(sizeof(struct vnet_dev)); 
503
504     if (new_dev == NULL) {
505         PrintError("VNET: Malloc fails\n");
506         dev_id = -1;
507         goto exit;
508     }
509    
510     memcpy(new_dev->mac_addr, mac, 6);
511     new_dev->input = netif_input;
512     new_dev->private_data = priv_data;
513     new_dev->vm = vm;
514         
515     list_add(&(new_dev->node), &(vnet_state.devs));
516     vnet_state.num_devs ++;
517     new_dev->dev_id = vnet_state.num_devs;
518     dev_id = new_dev->dev_id;
519
520     PrintDebug("Vnet: Add Device: dev_id %d, input : %p, private_data %p\n",
521                         new_dev->dev_id, new_dev->input, new_dev->private_data);
522
523 exit:
524         
525     v3_unlock_irqrestore(vnet_state.lock, flags);
526  
527     return dev_id;
528 }
529
530
531 int V3_init_vnet() {
532         
533     INIT_LIST_HEAD(&(vnet_state.routes));
534     INIT_LIST_HEAD(&(vnet_state.devs));
535
536     vnet_state.num_devs = 0;
537     vnet_state.num_routes = 0;
538
539     PrintDebug("VNET: Links and Routes tables initiated\n");
540
541     if (v3_lock_init(&(vnet_state.lock)) == -1){
542         PrintError("VNET: Failure to init lock for routes table\n");
543     }
544
545     PrintDebug("VNET: Locks initiated\n");
546
547     vnet_state.inpkt_q = v3_create_queue();
548     v3_init_queue(vnet_state.inpkt_q);
549     PrintDebug("VNET: Receiving queue initiated\n");
550
551     vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
552
553     if (vnet_state.route_cache == NULL) {
554         PrintError("Vnet: Route Cache Init Fails\n");
555         return -1;
556     }
557
558     PrintDebug("VNET: initiated\n");
559
560     return 0;
561 }