From bd60c4424bd02ebb388664428df0e69903136316 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Wed, 25 Jun 2014 01:53:48 +0000 Subject: [PATCH] added parser for netem settings and several functions to apply them git-svn-id: https://zerberus.eonerc.rwth-aachen.de:8443/svn/s2ss/trunk@79 8ec27952-4edc-4aab-86aa-e87bb2611832 --- etc/example.conf | 24 +++++++------- include/cfg.h | 8 +++++ include/node.h | 5 +++ include/tc.h | 80 +++++++++++++++++++++++++++++++++++++++++++++++ src/cfg.c | 34 +++++++++++++++++++- src/tc.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 219 insertions(+), 13 deletions(-) create mode 100644 include/tc.h create mode 100644 src/tc.c diff --git a/etc/example.conf b/etc/example.conf index 10ad92833..9476b832c 100644 --- a/etc/example.conf +++ b/etc/example.conf @@ -19,7 +19,18 @@ nodes = { id = 2, type = "rtds", local = "127.0.0.1:10202", - remote = "127.0.0.1:10203" + remote = "127.0.0.1:10203", + + # Network emulation for single nodes + netem = { + limit = 99999, # Limit network emulation to a certain amount of packets + delay = 100000, # Additional latency in uS + jitter = 30000, # Jitter in uS + distribution = "normal",# Distribution of delay (uniform, normal, pareto, paretonormal) + loss = 10 # Loss in percentage + duplicate = 10, # Duplication in percentage + corrupt = 10, # Corruption in percentage + } } }; @@ -29,16 +40,5 @@ paths = ( reverse = true, # Setup a path in the reverse direction too in = "acs", # Name of the node we listen to (see above) out = "sintef", # Name of the node we send to - - # TODO: Some 'netem' settings might arrive here in the future... - # netem = { - # delay = (10, 10, # Time, jitter in milliseconds - # 10, # Correlation in percentage - # "normal") # Distribution: uniform, normal, pareto or paretonormal - # loss = (10, 25), # Loss in percentage - # duplicate = (10, 25), # Duplication in percentage - # corrupt = (10, 25), # Corruption in percentage - # reorder = (10, 25) # Reorder in percentage - # } } ); diff --git a/include/cfg.h b/include/cfg.h index e1a7cbf0c..5d2776865 100644 --- a/include/cfg.h +++ b/include/cfg.h @@ -14,6 +14,8 @@ struct node; struct path; struct interface; +struct netem; + /** Global configuration */ struct settings { /** Name of this node */ @@ -79,9 +81,15 @@ int config_parse_path(config_setting_t *cfg, */ int config_parse_node(config_setting_t *cfg, struct node **nodes, struct interface **interfaces); + +/** Parse network emulator (netem) settings + * + * @param cfg A libconfig object containing the settings + * @param em A pointer to the settings * @return * - 0 on success * - otherwise an error occured */ +int config_parse_netem(config_setting_t *cfg, struct netem *em); #endif /* _CFG_H_ */ diff --git a/include/node.h b/include/node.h index 87d944cb2..0f5c00192 100644 --- a/include/node.h +++ b/include/node.h @@ -18,6 +18,8 @@ #include #include +#include "tc.h" + /** The type of a node. * * This type is used to determine the message format of the remote node @@ -58,6 +60,9 @@ struct node /** The egress interface */ struct interface *interface; + /** Network emulator settings */ + struct netem *netem; + /** Socket mark for netem, routing and filtering */ int mark; diff --git a/include/tc.h b/include/tc.h new file mode 100644 index 000000000..0acb6c4dc --- /dev/null +++ b/include/tc.h @@ -0,0 +1,80 @@ +/** + * Setup interface queuing desciplines for network emulation + * + * @author Steffen Vogel + * @copyright 2014, Institute for Automation of Complex Power Systems, EONERC + */ + +#ifndef _TC_H_ +#define _TC_H_ + +#include + +/* Some helper for TC handles */ +typedef uint32_t tc_hdl_t; + +#define TC_HDL(maj, min) ((maj & 0xFFFF) << 16 | (min & 0xFFFF)) +#define TC_HDL_MAJ(h) ((h >> 16) & 0xFFFF) +#define TC_HDL_MIN(h) ((h >> 0) & 0xFFFF) +#define TC_HDL_ROOT (0xFFFFFFFFU) + +/* Bitfield for valid fields in struct netem */ +#define TC_NETEM_LIMIT (1 << 0) +#define TC_NETEM_DELAY (1 << 1) +#define TC_NETEM_JITTER (1 << 2) +#define TC_NETEM_DISTR (1 << 3) +#define TC_NETEM_LOSS (1 << 4) +#define TC_NETEM_CORRUPT (1 << 5) +#define TC_NETEM_DUPL (1 << 6) + +struct interface; + +struct netem { + /** Which fields of this struct contain valid data (TC_NETEM_*). */ + char valid; + + /** Delay distribution: uniform, normal, pareto, paretonormal */ + const char *distribution; + /** Fifo limit (packets) */ + int limit; + /** Added delay (uS) */ + int delay; + /** Delay jitter (uS) */ + int jitter; + /** Random loss probability (%) */ + int loss; + /** Packet corruption probability (%) */ + int corrupt; + /** Packet duplication probability (%) */ + int duplicate; +}; + +/** Remove all queuing disciplines and filters + * + * @param i The interface + */ +int tc_reset(struct interface *i); + +/** Create a prio queueing discipline + * + * @param i The interface + * @param parent + * @param handle + * @param bands + * @return + * - 0 on success + * - otherwise an error occured + */ +int tc_prio(struct interface *i, tc_hdl_t handle, int bands); + +/** Add a new network emulator discipline + * + * @param i The interface + * @param parent Make this + */ +int tc_netem(struct interface *i, tc_hdl_t parent, struct netem *em); + +/** Add a new fwmark filter */ +int tc_mark(struct interface *i, tc_hdl_t flowid, int mark); + +#endif /* _TC_H_ */ diff --git a/src/cfg.c b/src/cfg.c index e2de68495..caa44d5de 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -13,6 +13,7 @@ #include #include "if.h" +#include "tc.h" #include "cfg.h" #include "node.h" #include "path.h" @@ -147,7 +148,7 @@ int config_parse_path(config_setting_t *cfg, path_rev->in = path->out; /* Swap in/out */ path_rev->out = path->in; - + list_add(*paths, path_rev); } } @@ -202,6 +203,13 @@ int config_parse_node(config_setting_t *cfg, if (resolve_addr(remote_str, &node->remote, 0)) cerror(cfg, "Failed to resolve remote address '%s' of node '%s'", remote_str, node->name); + /* Optional settings */ + config_setting_t *cfg_netem = config_setting_get_member(cfg, "netem"); + if (cfg_netem) { + node->netem = (struct netem *) malloc(sizeof(struct netem)); + config_parse_netem(cfg_netem, node->netem); + } + /* Determine outgoing interface */ int index = if_getegress(&node->remote); struct interface *i = if_lookup_index(index, *interfaces); @@ -225,3 +233,27 @@ int config_parse_node(config_setting_t *cfg, return 0; } + +int config_parse_netem(config_setting_t *cfg, struct netem *em) +{ + em->valid = 0; + + if (config_setting_lookup_string(cfg, "distribution", &em->distribution)) + em->valid |= TC_NETEM_DISTR; + if (config_setting_lookup_int(cfg, "limit", &em->limit)) + em->valid |= TC_NETEM_LIMIT; + if (config_setting_lookup_int(cfg, "delay", &em->delay)) + em->valid |= TC_NETEM_DELAY; + if (config_setting_lookup_int(cfg, "jitter", &em->jitter)) + em->valid |= TC_NETEM_JITTER; + if (config_setting_lookup_int(cfg, "loss", &em->loss)) + em->valid |= TC_NETEM_LOSS; + if (config_setting_lookup_int(cfg, "duplicate", &em->duplicate)) + em->valid |= TC_NETEM_DUPL; + if (config_setting_lookup_int(cfg, "corrupt", &em->corrupt)) + em->valid |= TC_NETEM_CORRUPT; + + // TODO: check values + + return 0; +} diff --git a/src/tc.c b/src/tc.c new file mode 100644 index 000000000..43420ec9f --- /dev/null +++ b/src/tc.c @@ -0,0 +1,81 @@ +/** + * Traffic control: setup interface queuing desciplines + * + * @author Steffen Vogel + * @copyright 2014, Institute for Automation of Complex Power Systems, EONERC + */ + +#include +#include +#include + +#include "utils.h" +#include "if.h" +#include "tc.h" + +int tc_reset(struct interface *i) +{ + char cmd[128]; + snprintf(cmd, sizeof(cmd), "tc qdisc del dev %s root", i->name); + + debug(6, "system: %s", cmd); + return system(cmd); +} + +int tc_prio(struct interface *i, tc_hdl_t handle, int bands) +{ + char cmd[128]; + int len = 0; + + len += snprintf(cmd+len, sizeof(cmd)-len, + "tc qdisc add dev %s root handle %u prio bands %u priomap", + i->name, TC_HDL_MAJ(handle), bands); + + for (int i = 0; i < bands; i++) + len += snprintf(cmd+len, sizeof(cmd)-len, " 0"); + + debug(6, "system: %s", cmd); + return system(cmd); +} + +int tc_netem(struct interface *i, tc_hdl_t parent, struct netem *em) +{ + int len = 0; + char cmd[256]; + len += snprintf(cmd+len, sizeof(cmd)-len, + "tc qdisc add dev %s parent %u:%u netem", + i->name, TC_HDL_MAJ(parent), TC_HDL_MIN(parent)); + + if (em->valid & TC_NETEM_LIMIT) + len += snprintf(cmd+len, sizeof(cmd)-len, " limit %u", em->limit); + if (em->valid & TC_NETEM_DELAY) { + len += snprintf(cmd+len, sizeof(cmd)-len, " delay %u", em->delay); + + if (em->valid & TC_NETEM_JITTER) + len += snprintf(cmd+len, sizeof(cmd)-len, " %u", em->jitter); + if (em->valid & TC_NETEM_DISTR) + len += snprintf(cmd+len, sizeof(cmd)-len, " distribution %s", em->distribution); + } + + if (em->valid & TC_NETEM_LOSS) + len += snprintf(cmd+len, sizeof(cmd)-len, " loss random %u", em->loss); + if (em->valid & TC_NETEM_DUPL) + len += snprintf(cmd+len, sizeof(cmd)-len, " duplicate %u", em->duplicate); + if (em->valid & TC_NETEM_CORRUPT) + len += snprintf(cmd+len, sizeof(cmd)-len, " corrupt %u", em->corrupt); + + debug(6, "system: %s", cmd); + return system(cmd); +} + +int tc_mark(struct interface *i, tc_hdl_t flowid, int mark) +{ + char cmd[128]; + snprintf(cmd, sizeof(cmd), + "tc filter add dev %s protocol ip handle %u fw flowid %u:%u", + i->name, mark, TC_HDL_MAJ(flowid), TC_HDL_MIN(flowid)); + + debug(6, "system: %s", cmd); + return system(cmd); + +}