Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON load balancer config file #328

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
140 changes: 110 additions & 30 deletions examples/load_balancer/load_balancer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
andreaseno marked this conversation as resolved.
Show resolved Hide resolved

#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;
Expand All @@ -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 */
Expand All @@ -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;

Expand All @@ -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 <print_delay>`: 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");
}

/*
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -240,49 +257,81 @@ 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);
andreaseno marked this conversation as resolved.
Show resolved Hide resolved
}
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));
andreaseno marked this conversation as resolved.
Show resolved Hide resolved

lb->server = (struct backend_server *)rte_malloc("backend server info",
sizeof(struct backend_server) * lb->server_count, 0);
if (lb->server == NULL) {
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) {
andreaseno marked this conversation as resolved.
Show resolved Hide resolved
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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -555,6 +628,7 @@ 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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have this display the src IP and port? Something like:
192.168.1.100:4256 assigned to server 2

}

if (pkt->port == lb->server_port) {
Expand Down Expand Up @@ -600,6 +674,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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check indentation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the issue with the indentation here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like tabs not spaces. @catherinemeadows -- do you know if we have a working linter script that checks for this kind of formatting issue in ONVM? I thought our GitHub actions would do it automatically, but it seems not.

Copy link
Contributor

@catherinemeadows catherinemeadows Aug 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in /openNetVM/style, there is documentation about manually running our linter scripts. I think the GitHub actions weren't triggered yet because there are still merge conflicts.


nf_local_ctx = onvm_nflib_init_nf_local_ctx();
onvm_nflib_start_signal_handler(nf_local_ctx, NULL);

Expand Down Expand Up @@ -636,14 +714,16 @@ 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();

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");
Expand Down
3 changes: 0 additions & 3 deletions examples/load_balancer/server.conf

This file was deleted.

18 changes: 18 additions & 0 deletions examples/load_balancer/server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"Info" : {
"list_size": 2,
"policy": "random"
andreaseno marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be all caps?

},

"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
}
}
andreaseno marked this conversation as resolved.
Show resolved Hide resolved