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.


Minor fix to 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, 20, "%02x:%02x:%02x:%02x:%02x:%02x", 
103              mac[0], mac[1], mac[2],
104              mac[3], mac[4], mac[5]);
105 }
106
107 #endif
108
109
110 /* 
111  * A VNET packet is a packed struct with the hashed fields grouped together.
112  * This means we can generate the hash from an offset into the pkt struct
113  */
114 static inline uint_t hash_fn(addr_t hdr_ptr) {    
115     uint8_t * hdr_buf = (uint8_t *)&(hdr_ptr);
116     
117     return v3_hash_buffer(hdr_buf, VNET_HASH_SIZE);
118 }
119
120 static inline int hash_eq(addr_t key1, addr_t key2) {
121     return (memcmp((uint8_t *)key1, (uint8_t *)key2, VNET_HASH_SIZE) == 0);
122 }
123
124
125 static int add_route_to_cache(struct v3_vnet_pkt * pkt, struct route_list * routes) {
126     memcpy(routes->hash_buf, pkt->hash_buf, VNET_HASH_SIZE);    
127
128     if (v3_htable_insert(vnet_state.route_cache, (addr_t)routes->hash_buf, (addr_t)routes) == 0) {
129         PrintError("Vnet: Failed to insert new route entry to the cache\n");
130         return -1;
131     }
132     
133     return 0;
134 }
135
136 static int clear_hash_cache() {
137
138     v3_free_htable(vnet_state.route_cache, 1, 1);
139     vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
140
141     return 0;
142 }
143
144 static int look_into_cache(struct v3_vnet_pkt * pkt, struct route_list ** routes) {
145     
146     *routes = (struct route_list *)v3_htable_search(vnet_state.route_cache, (addr_t)pkt);
147    
148     return 0;
149 }
150
151
152 struct vnet_dev * find_dev_by_id(int idx) {
153     struct vnet_dev * dev = NULL; 
154     
155     list_for_each_entry(dev, &(vnet_state.devs), node) {
156         int dev_id = dev->dev_id;
157
158         if (dev_id == idx)
159             return dev;
160     }
161
162     return NULL;
163 }
164
165 static struct vnet_dev * find_dev_by_mac(char * name) {
166     struct vnet_dev * dev = NULL; 
167     
168     list_for_each_entry(dev, &(vnet_state.devs), node) {
169         if (!memcmp(dev->mac_addr, name, 6))
170             return dev;
171     }
172
173     return NULL;
174 }
175
176
177 int v3_vnet_add_route(struct v3_vnet_route route) {
178     struct vnet_route_info * new_route = NULL;
179     unsigned long flags; 
180
181     new_route = (struct vnet_route_info *)V3_Malloc(sizeof(struct vnet_route_info));
182     memset(new_route, 0, sizeof(struct vnet_route_info));
183
184     PrintDebug("Vnet: vnet_add_route_entry\n"); 
185     
186     new_route->route_def = route;
187
188     /* TODO: Find devices */
189     if (new_route->route_def.dst_type == LINK_INTERFACE) {
190         new_route->dst_dev = find_dev_by_id(new_route->route_def.dst_id);
191     }
192
193     if (new_route->route_def.src_type == LINK_INTERFACE) {
194         new_route->src_dev = find_dev_by_id(new_route->route_def.src_id);
195     }
196
197     flags = v3_lock_irqsave(vnet_state.lock);
198     list_add(&(new_route->node), &(vnet_state.routes));
199     v3_unlock_irqrestore(vnet_state.lock, flags);
200    
201     clear_hash_cache();
202
203     return 0;
204 }
205
206
207
208 // At the end allocate a route_list
209 // This list will be inserted into the cache so we don't need to free it
210 static struct route_list * match_route(struct v3_vnet_pkt * pkt) {
211     struct vnet_route_info * route = NULL; 
212     struct route_list * matches = NULL;
213     int num_matches = 0;
214     int max_rank = 0;
215     struct list_head match_list;
216     struct eth_hdr * hdr = (struct eth_hdr *)(pkt->data);
217     uint8_t src_type = pkt->src_type;
218     uint32_t src_link = pkt->src_id;
219
220 #ifdef CONFIG_DEBUG_VNET
221     {
222         char dst_str[18];
223         char src_str[18];
224
225         mac_to_string(hdr->src_mac, src_str);  
226         mac_to_string(hdr->dst_mac, dst_str);
227         PrintDebug("Vnet: match_route. pkt: SRC(%s), DEST(%s)\n", src_str, dst_str);
228     }
229 #endif
230
231     INIT_LIST_HEAD(&match_list);
232     
233 #define UPDATE_MATCHES(rank) do {                               \
234         if (max_rank < (rank)) {                                \
235             max_rank = (rank);                                  \
236             INIT_LIST_HEAD(&match_list);                        \
237                                                                 \
238             list_add(&(route->match_node), &match_list);        \
239             num_matches = 1;                                    \
240         } else if (max_rank == (rank)) {                        \
241             list_add(&(route->match_node), &match_list);        \
242             num_matches++;                                      \
243         }                                                       \
244     } while (0)
245     
246
247     list_for_each_entry(route, &(vnet_state.routes), node) {
248         struct v3_vnet_route * route_def = &(route->route_def);
249
250         // CHECK SOURCE TYPE HERE
251         if ( (route_def->src_type != LINK_ANY) && 
252              ( (route_def->src_type != src_type) || 
253                ( (route_def->src_id != src_link) &&
254                  (route_def->src_id != (uint32_t)-1)))) {
255             continue;
256         }
257
258
259         if ((route_def->dst_mac_qual == MAC_ANY) &&
260             (route_def->src_mac_qual == MAC_ANY)) {      
261             UPDATE_MATCHES(3);
262         }
263         
264         if (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) {
265             if (route_def->src_mac_qual != MAC_NOT) {
266                 if (route_def->dst_mac_qual == MAC_ANY) {
267                     UPDATE_MATCHES(6);
268                 } else if (route_def->dst_mac_qual != MAC_NOT &&
269                            memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
270                     UPDATE_MATCHES(8);
271                 }
272             }
273         }
274             
275         if (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
276             if (route_def->dst_mac_qual != MAC_NOT) {
277                 if (route_def->src_mac_qual == MAC_ANY) {
278                     UPDATE_MATCHES(6);
279                 } else if ((route_def->src_mac_qual != MAC_NOT) && 
280                            (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {
281                     UPDATE_MATCHES(8);
282                 }
283             }
284         }
285             
286         if ((route_def->dst_mac_qual == MAC_NOT) &&
287             (memcmp(route_def->dst_mac, hdr->dst_mac, 6) != 0)) {
288             if (route_def->src_mac_qual == MAC_ANY) {
289                 UPDATE_MATCHES(5);
290             } else if ((route_def->src_mac_qual != MAC_NOT) && 
291                        (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {     
292                 UPDATE_MATCHES(7);
293             }
294         }
295         
296         if ((route_def->src_mac_qual == MAC_NOT) &&
297             (memcmp(route_def->src_mac, hdr->src_mac, 6) != 0)) {
298             if (route_def->dst_mac_qual == MAC_ANY) {
299                 UPDATE_MATCHES(5);
300             } else if ((route_def->dst_mac_qual != MAC_NOT) &&
301                        (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0)) {
302                 UPDATE_MATCHES(7);
303             }
304         }
305         
306         // Default route
307         if ( (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) &
308              (route_def->dst_mac_qual == MAC_NONE)) {
309             UPDATE_MATCHES(4);
310         }
311     }
312
313     PrintDebug("Vnet: match_route: Matches=%d\n", num_matches);
314
315     if (num_matches == 0) {
316         return NULL;
317     }
318
319     matches = V3_Malloc(sizeof(struct route_list) + 
320                         (sizeof(struct vnet_route_info *) * num_matches));
321
322     matches->num_routes = num_matches;
323
324     {
325         int i = 0;
326         list_for_each_entry(route, &match_list, node) {
327             matches->routes[i++] = route;
328         }
329     }
330
331     return matches;
332 }
333
334 static int handle_one_pkt(struct v3_vnet_pkt * pkt) {
335     struct route_list * matched_routes = NULL;
336     unsigned long flags;
337     int i;
338
339
340 #ifdef CONFIG_DEBUG_VNET
341    {
342         struct eth_hdr * hdr = (struct eth_hdr *)(pkt->data);
343         char dest_str[18];
344         char src_str[18];
345
346         mac_to_string(hdr->src_mac, src_str);  
347         mac_to_string(hdr->dst_mac, dest_str);
348         PrintDebug("Vnet: HandleDataOverLink. SRC(%s), DEST(%s)\n", src_str, dest_str);
349    }
350 #endif
351
352     flags = v3_lock_irqsave(vnet_state.lock);
353
354     look_into_cache(pkt, &matched_routes);
355         
356     if (matched_routes == NULL) {  
357         matched_routes = match_route(pkt);
358                 
359       if (matched_routes) {
360             add_route_to_cache(pkt, matched_routes);
361         } else {
362             PrintError("Could not find route for packet...\n");
363             v3_unlock_irqrestore(vnet_state.lock, flags);
364             return -1;
365         }
366     }
367
368     v3_unlock_irqrestore(vnet_state.lock, flags);
369     
370     
371     for (i = 0; i < matched_routes->num_routes; i++) {
372         struct vnet_route_info * route = matched_routes->routes[i];
373
374         if (route->route_def.dst_type == LINK_EDGE) {
375
376         } else if (route->route_def.dst_type == LINK_INTERFACE) {
377             if (route->dst_dev->input(route->dst_dev->vm, pkt, route->dst_dev->private_data) == -1) {
378                 PrintDebug("VNET: Packet not sent properly\n");
379                 continue;
380              }
381         } else {
382             PrintDebug("Vnet: Wrong Edge type\n");
383             continue;
384         }
385
386         PrintDebug("Vnet: HandleDataOverLink: Forward packet according to Route\n");
387     }
388     
389     return 0;
390 }
391
392 int v3_vnet_send_pkt(struct v3_vnet_pkt * pkt) {
393     
394     PrintDebug("In Vnet Send: pkt size: %d\n", pkt->size);
395                 
396     if (handle_one_pkt(pkt) != -1) {
397         PrintDebug("VNET: send one packet! pt length %d\n", pkt->size);  
398     } else {
399         PrintDebug("VNET: Fail to forward one packet, discard it!\n"); 
400     }
401
402     return 0;
403 }
404
405 int v3_vnet_add_dev(struct v3_vm_info *vm,uint8_t mac[6], 
406                     int (*netif_input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt, void * private_data), 
407                     void * priv_data){
408     struct vnet_dev * new_dev = NULL;
409
410     new_dev = find_dev_by_mac(mac);
411
412     PrintDebug("VNET: register device\n");
413
414     if (new_dev) {
415         PrintDebug("VNET: register device: Already has device with the same mac\n");
416         return -1;
417     }
418     
419     new_dev = (struct vnet_dev *)V3_Malloc(sizeof(struct vnet_dev)); 
420
421     if (new_dev == NULL) {
422         PrintError("VNET: Malloc fails\n");
423         return -1;
424     }
425    
426     memcpy(new_dev->mac_addr, mac, 6);
427     new_dev->input = netif_input;
428     new_dev->private_data = priv_data;
429     new_dev->vm = vm;
430
431     PrintDebug("VNET: register device new_dev22 %p\n", (void *)new_dev);
432         
433     list_add(&(new_dev->node), &(vnet_state.devs));
434     vnet_state.num_devs ++;
435     new_dev->dev_id = vnet_state.num_devs;
436
437     return 0;
438 }
439
440 #if 0
441 static int v3_vnet_pkt_process() {
442     struct v3_vnet_pkt * pkt = NULL;
443
444     while ((pkt = (struct v3_vnet_pkt *)v3_dequeue(vnet_state.inpkt_q)) != NULL) {
445         if (handle_one_pkt(pkt) != -1) {
446             PrintDebug("VNET: vnet_check: handle one packet! pt length %d\n", (int)pkt->size);  
447         } else {
448             PrintDebug("VNET: vnet_check: Fail to forward one packet, discard it!\n"); 
449         }
450         
451         V3_Free(pkt); // be careful here
452     }
453     
454     return 0;
455 }
456 #endif
457
458 int V3_init_vnet() {
459         
460     INIT_LIST_HEAD(&(vnet_state.routes));
461     INIT_LIST_HEAD(&(vnet_state.devs));
462
463     vnet_state.num_devs = 0;
464     vnet_state.num_routes = 0;
465
466     PrintDebug("VNET: Links and Routes tables initiated\n");
467
468     if (v3_lock_init(&(vnet_state.lock)) == -1){
469         PrintError("VNET: Failure to init lock for routes table\n");
470     }
471
472     PrintDebug("VNET: Locks initiated\n");
473
474     /*initial pkt receiving queue */
475     vnet_state.inpkt_q = v3_create_queue();
476     v3_init_queue(vnet_state.inpkt_q);
477     PrintDebug("VNET: Receiving queue initiated\n");
478
479     vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
480
481     if (vnet_state.route_cache == NULL) {
482         PrintError("Vnet: Route Cache Init Fails\n");
483         return -1;
484     }
485
486     PrintDebug("VNET: initiated\n");
487
488     return 0;
489 }