diff --git a/examples/load_balancer/load_balancer.c b/examples/load_balancer/load_balancer.c index bf6948b7b..fef512772 100644 --- a/examples/load_balancer/load_balancer.c +++ b/examples/load_balancer/load_balancer.c @@ -69,10 +69,14 @@ #include "onvm_flow_table.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" +#include "onvm_config_common.h" +#include "cJSON.h" #define NF_TAG "load_balancer" #define TABLE_SIZE 65536 +enum lb_policy {RROBIN, RANDOM, WEIGHTED_RANDOM}; + /* Struct for load balancer information */ struct loadbalance { struct onvm_ft *ft; @@ -95,6 +99,14 @@ struct loadbalance { /* config file */ char *cfg_filename; + + /* LB policy */ + // char *policy; + enum lb_policy policy; + + /* structures to store server weights */ + int *weights; + int total_weight; }; /* Struct for backend servers */ @@ -115,6 +127,9 @@ struct loadbalance *lb; /* number of package between each print */ static uint32_t print_delay = 1000000; +/* switch for turning on debug mode */ +static int debug_mode = 0; + /* onvm struct for port info lookup */ extern struct port_info *ports; @@ -136,6 +151,7 @@ usage(const char *progname) { printf(" - `-t SERVER_PORT` : server port ID\n"); printf(" - `-f SERVER_CONFIG` : backend server config file\n"); printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); + printf(" - `-d`: debug info including when connections are recieved and when packets are recieved.\n"); } /* @@ -164,7 +180,7 @@ parse_app_args(int argc, char *argv[], const char *progname) { lb->client_port = RTE_MAX_ETHPORTS; lb->server_port = RTE_MAX_ETHPORTS; - while ((c = getopt(argc, argv, "c:r:s:t:f:p:")) != -1) { + while ((c = getopt(argc, argv, "c:r:s:t:f:p:d")) != -1) { switch (c) { case 'c': ret = parse_iface_ip(strdup(optarg), &lb->ip_lb_client); @@ -192,11 +208,12 @@ parse_app_args(int argc, char *argv[], const char *progname) { case 'p': print_delay = strtoul(optarg, NULL, 10); break; + case 'd': + debug_mode = 1; + break; case '?': usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') + if (optopt == 'p') RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); else if (isprint(optopt)) RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); @@ -240,24 +257,42 @@ parse_app_args(int argc, char *argv[], const char *progname) { /* * This function parses the backend config. It takes the filename * and fills up the backend_server array. This includes the mac and ip - * address of the backend servers + * address of the backend servers as well as their weights */ static int -parse_backend_config(void) { - int ret, temp, i; - char ip[32]; - char mac[32]; - FILE *cfg; +parse_backend_json_config(void) { + int ret, i; + i = 0; - cfg = fopen(lb->cfg_filename, "r"); - if (cfg == NULL) { - rte_exit(EXIT_FAILURE, "Error openning server \'%s\' config\n", lb->cfg_filename); - } - ret = fscanf(cfg, "%*s %d", &temp); - if (temp <= 0) { - rte_exit(EXIT_FAILURE, "Error parsing config, need at least one server configurations\n"); + cJSON *config_json = onvm_config_parse_file(lb->cfg_filename); + cJSON *list_size = NULL; + cJSON *policy = NULL; + cJSON *ip_addr = NULL; + cJSON *mac_addr = NULL; + cJSON *weight = NULL; + + if (config_json == NULL) { + rte_exit(EXIT_FAILURE, "%s file could not be parsed or was not found. Assure" + " the directory to the config file is being specified.\n", lb->cfg_filename); } - lb->server_count = temp; + + config_json = config_json -> child; + + list_size = cJSON_GetObjectItem(config_json, "list_size"); + policy = cJSON_GetObjectItem(config_json, "policy"); + + if (list_size == NULL) rte_exit(EXIT_FAILURE, "list_size not found/invalid\n"); + if (policy == NULL) rte_exit(EXIT_FAILURE, "policy not found/invalid\n"); + + + lb->server_count = list_size->valueint; + + if (!strcmp(policy->valuestring, "RROBIN")) lb->policy = RROBIN; + else if (!strcmp(policy->valuestring, "RANDOM")) lb->policy = RANDOM; + else if (!strcmp(policy->valuestring, "WEIGHTED_RANDOM")) lb->policy = WEIGHTED_RANDOM; + else rte_exit(EXIT_FAILURE, "Invalid policy. Check server.json\n"); + + lb->weights = (int*)calloc(lb->server_count,sizeof(int)); lb->server = (struct backend_server *)rte_malloc("backend server info", sizeof(struct backend_server) * lb->server_count, 0); @@ -265,24 +300,38 @@ parse_backend_config(void) { rte_exit(EXIT_FAILURE, "Malloc failed, can't allocate server information\n"); } - for (i = 0; i < lb->server_count; i++) { - ret = fscanf(cfg, "%s %s", ip, mac); - if (ret != 2) { - rte_exit(EXIT_FAILURE, "Invalid backend config structure\n"); - } + config_json = config_json->next; + + while (config_json != NULL) { + ip_addr = cJSON_GetObjectItem(config_json, "ip"); + mac_addr = cJSON_GetObjectItem(config_json, "mac_addr"); + weight = cJSON_GetObjectItem(config_json, "weight"); - ret = onvm_pkt_parse_ip(ip, &lb->server[i].d_ip); + if (ip_addr == NULL) rte_exit(EXIT_FAILURE, "IP not found/invalid\n"); + if (mac_addr == NULL) rte_exit(EXIT_FAILURE, "MAC address not found/invalid\n"); + + + ret = onvm_pkt_parse_ip(ip_addr->valuestring, &lb->server[i].d_ip); if (ret < 0) { rte_exit(EXIT_FAILURE, "Error parsing config IP address #%d\n", i); } - - ret = onvm_pkt_parse_mac(mac, lb->server[i].d_addr_bytes); + ret = onvm_pkt_parse_mac(mac_addr->valuestring, lb->server[i].d_addr_bytes); if (ret < 0) { rte_exit(EXIT_FAILURE, "Error parsing config MAC address #%d\n", i); } + + if (lb->policy != WEIGHTED_RANDOM) lb->weights[i] = 1; + else { + if (weight == NULL) rte_exit(EXIT_FAILURE, "Weight not found/invalid\n"); + lb->weights[i] = weight->valueint; + lb->total_weight += weight->valueint; + } + config_json = config_json->next; + i++; } + if ( i != lb->server_count) rte_exit(EXIT_FAILURE, "Invalid list_size in config file\n"); + cJSON_Delete(config_json); - fclose(cfg); printf("\nARP config:\n"); for (i = 0; i < lb->server_count; i++) { printf("%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", (lb->server[i].d_ip >> 24) & 0xFF, @@ -460,10 +509,34 @@ table_add_entry(struct onvm_ft_ipv4_5tuple *key, struct flow_info **flow) { } lb->num_stored++; - data->dest = lb->num_stored % lb->server_count; + + int i, wrand, cur_weight_sum; + switch (lb->policy) + { + case RANDOM: + data->dest = rand() % lb->server_count; + break; + case RROBIN: + data->dest = lb->num_stored % lb->server_count; + break; + case WEIGHTED_RANDOM: + wrand = rand() % lb->total_weight; + cur_weight_sum=0; + for (i = 0; i < lb->server_count; i++) { + cur_weight_sum+=lb->weights[i]; + if(wrand < cur_weight_sum) { + data->dest=i; + break; + } + } + break; + default: + rte_exit(EXIT_FAILURE, "Invalid policy while adding entry to table!\n"); + break; + } + data->last_pkt_cycles = lb->elapsed_cycles; data->is_active = 0; - *flow = data; return 0; @@ -518,10 +591,12 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, struct rte_ipv4_hdr *ip; struct rte_ether_hdr *ehdr; struct flow_info *flow_info; + struct rte_tcp_hdr* tcp; int i, ret; ehdr = onvm_pkt_ether_hdr(pkt); ip = onvm_pkt_ipv4_hdr(pkt); + tcp = onvm_pkt_tcp_hdr(pkt); /* Ignore packets without ip header, also ignore packets with invalid ip */ if (ip == NULL || ip->src_addr == 0 || ip->dst_addr == 0) { @@ -555,6 +630,12 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, for (i = 0; i < RTE_ETHER_ADDR_LEN; i++) { flow_info->s_addr_bytes[i] = ehdr->s_addr.addr_bytes[i]; } + if(debug_mode) { + printf("New connection made with server %d.\n",flow_info->dest); + printf("Source IP: %" PRIu32 " (%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 ":%" PRIu16") made a new connection with server %d.\n", ip->src_addr, + ip->src_addr & 0xFF, (ip->src_addr >> 8) & 0xFF, (ip->src_addr >> 16) & 0xFF, + (ip->src_addr >> 24) & 0xFF, rte_be_to_cpu_16(tcp->src_port),flow_info->dest); + } } if (pkt->port == lb->server_port) { @@ -600,6 +681,10 @@ main(int argc, char *argv[]) { int arg_offset; const char *progname = argv[0]; + time_t t; + /* Intializes RANDOM number generator */ + srand((unsigned) time(&t)); + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); onvm_nflib_start_signal_handler(nf_local_ctx, NULL); @@ -636,7 +721,7 @@ main(int argc, char *argv[]) { } validate_iface_config(); - parse_backend_config(); + parse_backend_json_config(); lb->expire_time = 32; lb->elapsed_cycles = rte_get_tsc_cycles(); @@ -644,6 +729,8 @@ main(int argc, char *argv[]) { onvm_nflib_run(nf_local_ctx); onvm_nflib_stop(nf_local_ctx); + + free(lb->weights); onvm_ft_free(lb->ft); rte_free(lb); printf("If we reach here, program is ending\n"); diff --git a/examples/load_balancer/server.conf b/examples/load_balancer/server.conf deleted file mode 100644 index 60956afac..000000000 --- a/examples/load_balancer/server.conf +++ /dev/null @@ -1,3 +0,0 @@ -LIST_SIZE 2 -10.10.2.1 3c:fd:fe:b4:fa:4c -10.10.2.3 3c:fd:fe:b0:f1:74 diff --git a/examples/load_balancer/server.json b/examples/load_balancer/server.json new file mode 100644 index 000000000..b6c96fea8 --- /dev/null +++ b/examples/load_balancer/server.json @@ -0,0 +1,18 @@ +{ + "Info" : { + "list_size": 2, + "policy": "RANDOM" + }, + + "Server0": { + "ip": "10.10.1.2", + "mac_addr": "90:e2:ba:ac:16:34", + "weight": 1 + }, + + "Server1": { + "ip": "10.10.1.3", + "mac_addr": "90:e2:ba:b3:bb:7d", + "weight": 1 + } +} \ No newline at end of file