2 * Palacios VNET Control Module
6 #include <linux/spinlock.h>
7 #include <linux/seq_file.h>
8 #include <linux/proc_fs.h>
9 #include <asm/uaccess.h>
10 #include <linux/inet.h>
11 #include <linux/kthread.h>
13 #include <linux/netdevice.h>
16 #include <linux/net.h>
17 #include <linux/string.h>
18 #include <linux/preempt.h>
19 #include <linux/sched.h>
22 #include <vnet/vnet.h>
23 #include <vnet/vnet_hashtable.h>
24 #include "palacios-vnet.h"
27 #define VNET_SERVER_PORT 9000
29 struct vnet_route_iter {
30 struct v3_vnet_route route;
33 struct list_head node;
37 struct vnet_link_iter {
40 vnet_brg_proto_t proto;
43 struct list_head node;
47 struct vnet_ctrl_state {
53 struct list_head route_list;
54 struct list_head link_iter_list;
58 struct proc_dir_entry * vnet_proc_root;
62 static struct vnet_ctrl_state vnet_ctrl_s;
65 char *skip_blank(char **start)
70 cur = strsep(start, " \t");
71 } while (cur != NULL && *cur=='\0');
76 char *skip_lines(char **start)
81 cur = strsep(start, "\r\n");
82 } while (cur != NULL && *cur=='\0');
88 static int parse_mac_str(char * str, uint8_t * qual, uint8_t * mac) {
91 INFO("Parsing MAC (%s)\n", str);
94 if(strnicmp("any", str, strlen(str)) == 0){
97 }else if(strnicmp("none", str, strlen(str)) == 0){
101 if (strstr(str, "-")) {
102 token = strsep(&str, "-");
104 if (strnicmp("not", token, strlen("not")) == 0) {
107 WARNING("Invalid MAC String token (%s)\n", token);
112 if (strstr(str, ":")) {
115 if(*qual == MAC_NOSET){
119 for (i = 0; i < 6; i++) {
120 token = strsep(&str, ":");
122 WARNING("Invalid MAC String token (%s)\n", token);
125 mac[i] = simple_strtol(token, &token, 16);
127 DEBUG("MAC: %2x:%2x:%2x:%2x:%2x:%2x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
130 WARNING("Invalid MAC String token (%s)\n", token);
140 static int str2mac(char * str, uint8_t * mac){
144 for (i = 0; i < ETH_ALEN; i++) {
145 hex = strsep(&str, ":");
147 WARNING("Invalid MAC String token (%s)\n", str);
150 mac[i] = simple_strtol(hex, &hex, 16);
157 static inline struct vnet_link_iter * link_by_ip(uint32_t ip) {
158 struct vnet_link_iter * link = NULL;
160 list_for_each_entry(link, &(vnet_ctrl_s.link_iter_list), node) {
162 if (link->dst_ip == ip) {
170 static inline struct vnet_link_iter * link_by_idx(int idx) {
171 struct vnet_link_iter * link = NULL;
173 list_for_each_entry(link, &(vnet_ctrl_s.link_iter_list), node) {
174 if (link->idx == idx) {
183 static int parse_route_str(char * str, struct v3_vnet_route * route) {
185 struct vnet_link_iter * link = NULL;
188 token = skip_blank(&str);
192 parse_mac_str(token, &(route->src_mac_qual), route->src_mac);
195 token = skip_blank(&str);
199 parse_mac_str(token, &(route->dst_mac_qual), route->dst_mac);
202 token = skip_blank(&str);
206 INFO("dst type =(%s)\n", token);
208 if (strnicmp("interface", token, strlen("interface")) == 0) {
209 route->dst_type = LINK_INTERFACE;
210 } else if (strnicmp("edge", token, strlen("edge")) == 0) {
211 route->dst_type = LINK_EDGE;
213 WARNING("Invalid Destination Link Type (%s)\n", token);
218 token = skip_blank(&str);
222 DEBUG("dst ID=(%s)\n", token);
224 // Figure out link here
225 if (route->dst_type == LINK_EDGE) {
228 // Figure out Link Here
229 if (in4_pton(token, strlen(token), (uint8_t *)&(link_ip), '\0', NULL) != 1) {
230 WARNING("Invalid Dst IP address (%s)\n", token);
234 link = link_by_ip(link_ip);
236 route->dst_id = link->idx;
238 WARNING("can not find dst link %s\n", token);
242 INFO("link_ip = %d, link_id = %d\n", link_ip, link->idx);
243 } else if (route->dst_type == LINK_INTERFACE) {
244 uint8_t mac[ETH_ALEN];
246 if(str2mac(token, mac) == -1){
247 WARNING("wrong MAC format (%s)\n", token);
251 route->dst_id = v3_vnet_find_dev(mac);
252 if (route->dst_id == -1){
253 WARNING("can not find dst device %s\n", token);
257 WARNING("Unsupported dst link type\n");
262 route->src_type = -1;
265 token = skip_blank(&str);
267 INFO("SRC type = %s\n", token);
273 if (strnicmp("interface", token, strlen("interface")) == 0) {
274 route->src_type = LINK_INTERFACE;
275 } else if (strnicmp("edge", token, strlen("edge")) == 0) {
276 route->src_type = LINK_EDGE;
277 } else if (strnicmp("any", token, strlen("any")) == 0) {
278 route->src_type = LINK_ANY;
280 WARNING("Invalid Src link type (%s)\n", token);
285 if (route->src_type == LINK_ANY) {
287 } else if (route->src_type == LINK_EDGE) {
289 token = skip_blank(&str);
295 // Figure out Link Here
296 if (in4_pton(token, strlen(token), (uint8_t *)&(src_ip), '\0', NULL) != 1) {
297 WARNING("Invalid SRC IP address (%s)\n", token);
301 link = link_by_ip(src_ip);
303 route->src_id = link->idx;
305 WARNING("can not find src link %s\n", token);
308 } else if(route->src_type == LINK_INTERFACE){
309 uint8_t mac[ETH_ALEN];
311 if(str2mac(token, mac) == -1){
312 WARNING("wrong MAC format (%s)\n", token);
316 route->src_id = v3_vnet_find_dev(mac);
317 if (route->src_id == -1){
318 WARNING("can not find dst device %s\n", token);
322 WARNING("Invalid link type\n");
330 static void * route_seq_start(struct seq_file * s, loff_t * pos) {
331 struct vnet_route_iter * route_iter = NULL;
334 if (*pos >= vnet_ctrl_s.num_routes) {
338 list_for_each_entry(route_iter, &(vnet_ctrl_s.route_list), node) {
350 static void * route_seq_next(struct seq_file * s, void * v, loff_t * pos) {
351 struct vnet_route_iter * route_iter = NULL;
353 route_iter = list_entry(((struct vnet_route_iter *)v)->node.next, struct vnet_route_iter, node);
355 // Check if the list has looped
356 if (&(route_iter->node) == &(vnet_ctrl_s.route_list)) {
365 static void route_seq_stop(struct seq_file * s, void * v) {
370 static void * link_seq_start(struct seq_file * s, loff_t * pos) {
371 struct vnet_link_iter * link_iter = NULL;
374 if (*pos >= vnet_ctrl_s.num_links) {
378 list_for_each_entry(link_iter, &(vnet_ctrl_s.link_iter_list), node) {
389 static int route_seq_show(struct seq_file * s, void * v) {
390 struct vnet_route_iter * route_iter = v;
391 struct v3_vnet_route * route = &(route_iter->route);
393 seq_printf(s, "%d:\t", route_iter->idx);
395 seq_printf(s, "\nSrc:\t");
396 switch (route->src_mac_qual) {
398 seq_printf(s, "any ");
401 seq_printf(s, "none ");
404 seq_printf(s, "not-%2x:%2x:%2x:%2x:%2x:%2x ",
405 route->src_mac[0], route->src_mac[1], route->src_mac[2],
406 route->src_mac[3], route->src_mac[4], route->src_mac[5]);
409 seq_printf(s, "%x:%x:%x:%x:%x:%x ",
410 route->src_mac[0], route->src_mac[1], route->src_mac[2],
411 route->src_mac[3], route->src_mac[4], route->src_mac[5]);
415 seq_printf(s, "\nDst:\t");
416 switch (route->dst_mac_qual) {
418 seq_printf(s, "any ");
421 seq_printf(s, "none ");
424 seq_printf(s, "not-%x:%x:%x:%x:%x:%x ",
425 route->dst_mac[0], route->dst_mac[1], route->dst_mac[2],
426 route->dst_mac[3], route->dst_mac[4], route->dst_mac[5]);
429 seq_printf(s, "%x:%x:%x:%x:%x:%x ",
430 route->dst_mac[0], route->dst_mac[1], route->dst_mac[2],
431 route->dst_mac[3], route->dst_mac[4], route->dst_mac[5]);
435 seq_printf(s, "\nDst-Type:\t");
436 switch (route->dst_type) {
438 struct vnet_link_iter * link = (struct vnet_link_iter *)link_by_idx(route->dst_id);
439 seq_printf(s, "EDGE %pI4", &link->dst_ip);
442 case LINK_INTERFACE: {
443 seq_printf(s, "INTERFACE ");
444 seq_printf(s, "%d ", route->dst_id);
448 seq_printf(s, "Invalid Dst Link Type (%d) ", route->dst_type);
452 seq_printf(s, "\nSrc-Type:\t");
453 switch (route->src_type) {
455 struct vnet_link_iter * link = (struct vnet_link_iter *)link_by_idx(route->src_id);
456 seq_printf(s, "EDGE %pI4", &link->dst_ip);
459 case LINK_INTERFACE: {
460 seq_printf(s, "INTERFACE %d", route->src_id);
464 seq_printf(s, "ANY");
467 seq_printf(s, "Invalid Src Link Type (%d) ", route->src_type);
476 static void * link_seq_next(struct seq_file * s, void * v, loff_t * pos) {
477 struct vnet_link_iter * link_iter = NULL;
479 link_iter = list_entry(((struct vnet_link_iter *)v)->node.next, struct vnet_link_iter, node);
481 // Check if the list has looped
482 if (&(link_iter->node) == &(vnet_ctrl_s.link_iter_list)) {
491 static void link_seq_stop(struct seq_file * s, void * v) {
496 static int link_seq_show(struct seq_file * s, void * v) {
497 struct vnet_link_iter * link_iter = v;
498 struct nic_statistics stats;
500 vnet_brg_link_stats(link_iter->idx, &stats);
502 seq_printf(s, "%d:\t%pI4\t%d\n\t\tReceived Pkts: %lld, Received Bytes %lld\n\t\tSent Pkts: %lld, Sent Bytes: %lld\n\n",
515 static struct seq_operations route_seq_ops = {
516 .start = route_seq_start,
517 .next = route_seq_next,
518 .stop = route_seq_stop,
519 .show = route_seq_show
523 static struct seq_operations link_seq_ops = {
524 .start = link_seq_start,
525 .next = link_seq_next,
526 .stop = link_seq_stop,
527 .show = link_seq_show
531 static int route_open(struct inode * inode, struct file * file) {
532 return seq_open(file, &route_seq_ops);
536 static int link_open(struct inode * inode, struct file * file) {
537 return seq_open(file, &link_seq_ops);
542 static int inject_route(struct vnet_route_iter * route) {
545 route->idx = v3_vnet_add_route(route->route);
547 palacios_spinlock_lock_irqsave(&(vnet_ctrl_s.lock), flags);
548 list_add(&(route->node), &(vnet_ctrl_s.route_list));
549 vnet_ctrl_s.num_routes ++;
550 palacios_spinlock_unlock_irqrestore(&(vnet_ctrl_s.lock), flags);
552 INFO("VNET Control: One route added to VNET core\n");
558 static void delete_route(struct vnet_route_iter * route) {
561 v3_vnet_del_route(route->idx);
563 palacios_spinlock_lock_irqsave(&(vnet_ctrl_s.lock), flags);
564 list_del(&(route->node));
565 vnet_ctrl_s.num_routes --;
566 palacios_spinlock_unlock_irqrestore(&(vnet_ctrl_s.lock), flags);
568 INFO("VNET Control: Route %d deleted from VNET\n", route->idx);
570 palacios_free(route);
576 * add src-MAC dst-MAC dst-TYPE [dst-ID] src-TYPE [src-ID]
578 * src-MAC = dst-MAC = not-MAC|any|none|MAC
579 * dst-TYPE = edge|interface
580 * src-TYPE = edge|interface|any
581 * dst-ID = src-ID = IP|MAC
582 * MAC=xx:xx:xx:xx:xx:xx
583 * IP = xxx.xxx.xxx.xxx
590 route_write(struct file * file,
595 char * buf_iter = NULL;
596 char * line_str = route_buf;
603 if (copy_from_user(route_buf, buf, size)) {
607 route_buf[size] = '\0';
608 INFO("Route written: %s\n", route_buf);
610 while ((buf_iter = skip_lines(&line_str))) {
612 token = skip_blank(&buf_iter);
617 if (strnicmp("ADD", token, strlen("ADD")) == 0) {
618 struct vnet_route_iter * new_route = NULL;
619 new_route = palacios_alloc(sizeof(struct vnet_route_iter));
622 ERROR("Cannot allocate new route\n");
626 memset(new_route, 0, sizeof(struct vnet_route_iter));
628 if (parse_route_str(buf_iter, &(new_route->route)) == -1) {
629 ERROR("Cannot parse new route\n");
630 palacios_free(new_route);
634 if (inject_route(new_route) != 0) {
635 ERROR("Cannot inject new route\n");
636 palacios_free(new_route);
639 } else if (strnicmp("DEL", token, strlen("DEL")) == 0) {
640 char * idx_str = NULL;
642 struct vnet_route_iter * route = NULL;
644 idx_str = skip_blank(&buf_iter);
647 WARNING("Missing route idx in DEL Route command\n");
651 d_idx = simple_strtoul(idx_str, &idx_str, 10);
653 INFO("VNET: deleting route %d\n", d_idx);
655 list_for_each_entry(route, &(vnet_ctrl_s.route_list), node) {
656 if (route->idx == d_idx) {
662 WARNING("Invalid Route command string\n");
670 static void delete_link(struct vnet_link_iter * link){
673 vnet_brg_delete_link(link->idx);
675 palacios_spinlock_lock_irqsave(&(vnet_ctrl_s.lock), flags);
676 list_del(&(link->node));
677 vnet_ctrl_s.num_links --;
678 palacios_spinlock_unlock_irqrestore(&(vnet_ctrl_s.lock), flags);
685 static void deinit_links_list(void){
686 struct vnet_link_iter * link, * tmp_link;
688 list_for_each_entry_safe(link, tmp_link, &(vnet_ctrl_s.link_iter_list), node) {
693 static void deinit_routes_list(void){
694 struct vnet_route_iter * route, * tmp_route;
696 list_for_each_entry_safe(route, tmp_route, &(vnet_ctrl_s.route_list), node) {
701 /* ADD dst-ip 9000 [udp|tcp] */
704 link_write(struct file * file, const char * buf, size_t size, loff_t * ppos) {
706 char * link_iter = NULL;
707 char * line_str = link_buf;
714 if (copy_from_user(link_buf, buf, size)) {
718 link_buf[size] = '\0';
719 INFO("Link written: %s\n", link_buf);
721 while ((link_iter = skip_lines(&line_str))) {
723 token = skip_blank(&link_iter);
728 if (strnicmp("ADD", token, strlen("ADD")) == 0) {
729 struct vnet_link_iter * link = NULL;
730 char * ip_str = NULL;
733 vnet_brg_proto_t d_proto;
737 ip_str = skip_blank(&link_iter);
739 if ((!ip_str) || (!link_iter)) {
740 WARNING("Missing fields in ADD Link command\n");
744 if (in4_pton(ip_str, strlen(ip_str), (uint8_t *)&(d_ip), '\0', NULL) != 1) {
745 WARNING("Invalid Dst IP address (%s)\n", ip_str);
749 d_port = simple_strtol(link_iter, &link_iter, 10);
752 link_idx = vnet_brg_add_link(d_ip, d_port, d_proto);
754 WARNING("VNET Control: Failed to create link\n");
758 link = palacios_alloc(sizeof(struct vnet_link_iter));
760 WARNING("VNET Control: Cannot allocate link\n");
764 memset(link, 0, sizeof(struct vnet_link_iter));
767 link->dst_port = d_port;
768 link->proto = d_proto;
769 link->idx = link_idx;
771 palacios_spinlock_lock_irqsave(&(vnet_ctrl_s.lock), flags);
772 list_add(&(link->node), &(vnet_ctrl_s.link_iter_list));
773 vnet_ctrl_s.num_links ++;
774 palacios_spinlock_unlock_irqrestore(&(vnet_ctrl_s.lock), flags);
775 } else if (strnicmp("DEL", token, strlen("DEL")) == 0) {
776 char * idx_str = NULL;
778 struct vnet_link_iter * link = NULL;
780 idx_str = skip_blank(&link_iter);
783 WARNING("Missing link idx in DEL Link command\n");
787 d_idx = simple_strtoul(idx_str, &idx_str, 10);
789 INFO("VNET: deleting link %d\n", d_idx);
791 list_for_each_entry(link, &(vnet_ctrl_s.link_iter_list), node) {
792 if (link->idx == d_idx) {
798 DEBUG("VNET Control: One link deleted\n");
801 WARNING("Invalid Link command string\n");
809 static struct file_operations route_fops = {
810 .owner = THIS_MODULE,
813 .write = route_write,
815 .release = seq_release
819 static struct file_operations link_fops = {
820 .owner = THIS_MODULE,
825 .release = seq_release
830 debug_write(struct file * file, const char * buf, size_t size, loff_t * ppos) {
832 char * in_iter = NULL;
833 char * line_str = in_buf;
840 if (copy_from_user(in_buf, buf, size)) {
844 in_iter = skip_lines(&line_str);
845 level = simple_strtol(in_iter, &in_iter, 10);
847 DEBUG("VNET Control: Set VNET Debug level to %d\n", level);
857 static int debug_show(struct seq_file * file, void * v){
858 seq_printf(file, "Current NET Debug Level: %d\n", net_debug);
863 static int debug_open(struct inode * inode, struct file * file) {
864 return single_open(file, debug_show, NULL);
867 static struct file_operations debug_fops = {
868 .owner = THIS_MODULE,
871 .write = debug_write,
873 .release = seq_release
876 static int stat_show(struct seq_file * file, void * v){
877 struct vnet_stat stats;
878 struct vnet_brg_stats brg_stats;
880 v3_vnet_stat(&stats);
882 seq_printf(file, "VNET Core\n");
883 seq_printf(file, "\tReceived Packets: %d\n", stats.rx_pkts);
884 seq_printf(file, "\tReceived Bytes: %lld\n", stats.rx_bytes);
885 seq_printf(file, "\tTransmitted Packets: %d\n", stats.tx_pkts);
886 seq_printf(file, "\tTransmitted Bytes: %lld\n", stats.tx_bytes);
888 vnet_brg_stats(&brg_stats);
890 seq_printf(file, "\nVNET Bridge Server\n");
891 seq_printf(file, "\tReceived From VMM: %lld\n", brg_stats.pkt_from_vmm);
892 seq_printf(file, "\tSent To VMM: %lld\n", brg_stats.pkt_to_vmm);
893 seq_printf(file, "\tDropped From VMM: %lld\n", brg_stats.pkt_drop_vmm);
894 seq_printf(file, "\tReceived From Extern Network: %lld\n", brg_stats.pkt_from_phy);
895 seq_printf(file, "\tSent To Extern Network: %lld\n", brg_stats.pkt_to_phy);
896 seq_printf(file, "\tDropped From Extern Network: %lld\n", brg_stats.pkt_drop_phy);
901 static int stat_open(struct inode * inode, struct file * file) {
902 return single_open(file, stat_show, NULL);
905 static struct file_operations stat_fops = {
906 .owner = THIS_MODULE,
910 .release = seq_release
914 static int init_proc_files(void) {
915 struct proc_dir_entry * route_entry = NULL;
916 struct proc_dir_entry * link_entry = NULL;
917 struct proc_dir_entry * stat_entry = NULL;
918 struct proc_dir_entry * debug_entry = NULL;
919 struct proc_dir_entry * vnet_root = NULL;
922 vnet_root = proc_mkdir("vnet", palacios_get_procdir());
924 if (vnet_root == NULL) {
929 PAL_PROC_CREATE(route_entry,"routes",0644, vnet_root,&route_fops);
931 if (route_entry == NULL) {
932 remove_proc_entry("vnet", NULL);
936 PAL_PROC_CREATE(link_entry,"links", 0644, vnet_root,&link_fops);
938 if (link_entry == NULL) {
939 remove_proc_entry("routes", vnet_root);
940 remove_proc_entry("vnet", NULL);
944 PAL_PROC_CREATE(stat_entry,"stats", 0644, vnet_root, &stat_fops);
946 if(stat_entry == NULL) {
947 remove_proc_entry("links", vnet_root);
948 remove_proc_entry("routes", vnet_root);
949 remove_proc_entry("vnet", NULL);
953 PAL_PROC_CREATE(debug_entry,"debug", 0644, vnet_root,&debug_fops);
955 if(debug_entry == NULL) {
956 remove_proc_entry("links", vnet_root);
957 remove_proc_entry("routes", vnet_root);
958 remove_proc_entry("stats", vnet_root);
959 remove_proc_entry("vnet", NULL);
963 vnet_ctrl_s.vnet_proc_root = vnet_root;
969 static void destroy_proc_files(void) {
970 struct proc_dir_entry * vnet_root = vnet_ctrl_s.vnet_proc_root;
972 remove_proc_entry("debug", vnet_root);
973 remove_proc_entry("links", vnet_root);
974 remove_proc_entry("routes", vnet_root);
975 remove_proc_entry("stats", vnet_root);
976 remove_proc_entry("vnet", palacios_get_procdir());
980 int vnet_ctrl_init(void) {
981 if(vnet_ctrl_s.status != 0) {
984 vnet_ctrl_s.status = 1;
986 memset(&vnet_ctrl_s, 0, sizeof(struct vnet_ctrl_state));
988 INIT_LIST_HEAD(&(vnet_ctrl_s.link_iter_list));
989 INIT_LIST_HEAD(&(vnet_ctrl_s.route_list));
990 palacios_spinlock_init(&(vnet_ctrl_s.lock));
994 NOTICE("VNET Linux control module initiated\n");
1000 void vnet_ctrl_deinit(void){
1002 INFO("VNET Control Deinit Started\n");
1004 destroy_proc_files();
1006 deinit_links_list();
1007 deinit_routes_list();
1009 vnet_ctrl_s.status = 0;
1011 palacios_spinlock_deinit(&(vnet_ctrl_s.lock));
1013 INFO("VNET Control Deinit Finished\n");