commit 677d25d6c613211f4122d4db880718e05fc9cd08
parent 1d7d1efc81a871e53a680a8e3282a68276dfdef4
Author: Robin Bron <robin@finwo.nl>
Date: Sat, 30 Apr 2022 03:45:45 +0200
Implemented basic config loading
Diffstat:
6 files changed, 176 insertions(+), 318 deletions(-)
diff --git a/README.md b/README.md
@@ -46,8 +46,9 @@ Select the broadcast method for the bonded interface
Select the balancing mode for non-broadcast packages
-- balance-rr: weighted round-robin balancing
- active-backup: define a primary and backup interface
+- broadcast: always sent packets to all interfaces
+- balance-rr: weighted round-robin balancing
#### mac
@@ -61,8 +62,9 @@ Define which bonded interface the current described interface belongs to
Behaves differently depending on the mode the bonded interface is in:
-- balance-rr: Specifies the share of traffic the interface transmits
-- active-backup: Specifies the priority of the interface (higher = more preferred)
+- active-backup: priority of the interface (higher = more preferred)
+- broadcast: no effect
+- balance-rr: share of traffic the interface transmits
Try to keep this number as low as possible, as it's used directly to define
the number of packets to send before moving to the next interface in the
diff --git a/empty.conf b/empty.conf
diff --git a/example.conf b/example.conf
@@ -0,0 +1,12 @@
+[bond0]
+broadcast=flood
+mode=balance-rr
+mac=01:23:45:67:89:AB
+
+[eth0]
+master=bond0
+weight=1
+
+[eth1]
+master=bond0
+weight=10
diff --git a/manpage.1.md b/manpage.1.md
@@ -46,8 +46,9 @@ Select the broadcast method for the bonded interface
Select the balancing mode for non-broadcast packages
-- balance-rr: weighted round-robin balancing
- active-backup: define a primary and backup interface
+- broadcast: always sent packets to all interfaces
+- balance-rr: weighted round-robin balancing
#### mac
@@ -61,8 +62,9 @@ Define which bonded interface the current described interface belongs to
Behaves differently depending on the mode the bonded interface is in:
-- balance-rr: Specifies the share of traffic the interface transmits
-- active-backup: Specifies the priority of the interface (higher = more preferred)
+- active-backup: priority of the interface (higher = more preferred)
+- broadcast: no effect
+- balance-rr: share of traffic the interface transmits
Try to keep this number as low as possible, as it's used directly to define
the number of packets to send before moving to the next interface in the
diff --git a/pmlag.html b/pmlag.html
@@ -1,311 +0,0 @@
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
-<head>
- <meta charset="utf-8" />
- <meta name="generator" content="pandoc" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
- <title>pmlag(1) | General Commands Manual</title>
- <style>
- html {
- line-height: 1.5;
- font-family: Georgia, serif;
- font-size: 20px;
- color: #1a1a1a;
- background-color: #fdfdfd;
- }
- body {
- margin: 0 auto;
- max-width: 36em;
- padding-left: 50px;
- padding-right: 50px;
- padding-top: 50px;
- padding-bottom: 50px;
- hyphens: auto;
- overflow-wrap: break-word;
- text-rendering: optimizeLegibility;
- font-kerning: normal;
- }
- @media (max-width: 600px) {
- body {
- font-size: 0.9em;
- padding: 1em;
- }
- h1 {
- font-size: 1.8em;
- }
- }
- @media print {
- body {
- background-color: transparent;
- color: black;
- font-size: 12pt;
- }
- p, h2, h3 {
- orphans: 3;
- widows: 3;
- }
- h2, h3, h4 {
- page-break-after: avoid;
- }
- }
- p {
- margin: 1em 0;
- }
- a {
- color: #1a1a1a;
- }
- a:visited {
- color: #1a1a1a;
- }
- img {
- max-width: 100%;
- }
- h1, h2, h3, h4, h5, h6 {
- margin-top: 1.4em;
- }
- h5, h6 {
- font-size: 1em;
- font-style: italic;
- }
- h6 {
- font-weight: normal;
- }
- ol, ul {
- padding-left: 1.7em;
- margin-top: 1em;
- }
- li > ol, li > ul {
- margin-top: 0;
- }
- blockquote {
- margin: 1em 0 1em 1.7em;
- padding-left: 1em;
- border-left: 2px solid #e6e6e6;
- color: #606060;
- }
- code {
- font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
- font-size: 85%;
- margin: 0;
- }
- pre {
- margin: 1em 0;
- overflow: auto;
- }
- pre code {
- padding: 0;
- overflow: visible;
- overflow-wrap: normal;
- }
- .sourceCode {
- background-color: transparent;
- overflow: visible;
- }
- hr {
- background-color: #1a1a1a;
- border: none;
- height: 1px;
- margin: 1em 0;
- }
- table {
- margin: 1em 0;
- border-collapse: collapse;
- width: 100%;
- overflow-x: auto;
- display: block;
- font-variant-numeric: lining-nums tabular-nums;
- }
- table caption {
- margin-bottom: 0.75em;
- }
- tbody {
- margin-top: 0.5em;
- border-top: 1px solid #1a1a1a;
- border-bottom: 1px solid #1a1a1a;
- }
- th {
- border-top: 1px solid #1a1a1a;
- padding: 0.25em 0.5em 0.25em 0.5em;
- }
- td {
- padding: 0.125em 0.5em 0.25em 0.5em;
- }
- header {
- margin-bottom: 4em;
- text-align: center;
- }
- #TOC li {
- list-style: none;
- }
- #TOC ul {
- padding-left: 1.3em;
- }
- #TOC > ul {
- padding-left: 0;
- }
- #TOC a:not(:hover) {
- text-decoration: none;
- }
- code{white-space: pre-wrap;}
- span.smallcaps{font-variant: small-caps;}
- span.underline{text-decoration: underline;}
- div.column{display: inline-block; vertical-align: top; width: 50%;}
- div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
- pre > code.sourceCode { white-space: pre; position: relative; }
- pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
- pre > code.sourceCode > span:empty { height: 1.2em; }
- .sourceCode { overflow: visible; }
- code.sourceCode > span { color: inherit; text-decoration: inherit; }
- div.sourceCode { margin: 1em 0; }
- pre.sourceCode { margin: 0; }
- @media screen {
- div.sourceCode { overflow: auto; }
- }
- @media print {
- pre > code.sourceCode { white-space: pre-wrap; }
- pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
- }
- pre.numberSource code
- { counter-reset: source-line 0; }
- pre.numberSource code > span
- { position: relative; left: -4em; counter-increment: source-line; }
- pre.numberSource code > span > a:first-child::before
- { content: counter(source-line);
- position: relative; left: -1em; text-align: right; vertical-align: baseline;
- border: none; display: inline-block;
- -webkit-touch-callout: none; -webkit-user-select: none;
- -khtml-user-select: none; -moz-user-select: none;
- -ms-user-select: none; user-select: none;
- padding: 0 4px; width: 4em;
- color: #aaaaaa;
- }
- pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
- div.sourceCode
- { }
- @media screen {
- pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
- }
- code span.al { color: #ff0000; font-weight: bold; } /* Alert */
- code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
- code span.at { color: #7d9029; } /* Attribute */
- code span.bn { color: #40a070; } /* BaseN */
- code span.bu { } /* BuiltIn */
- code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
- code span.ch { color: #4070a0; } /* Char */
- code span.cn { color: #880000; } /* Constant */
- code span.co { color: #60a0b0; font-style: italic; } /* Comment */
- code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
- code span.do { color: #ba2121; font-style: italic; } /* Documentation */
- code span.dt { color: #902000; } /* DataType */
- code span.dv { color: #40a070; } /* DecVal */
- code span.er { color: #ff0000; font-weight: bold; } /* Error */
- code span.ex { } /* Extension */
- code span.fl { color: #40a070; } /* Float */
- code span.fu { color: #06287e; } /* Function */
- code span.im { } /* Import */
- code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
- code span.kw { color: #007020; font-weight: bold; } /* Keyword */
- code span.op { color: #666666; } /* Operator */
- code span.ot { color: #007020; } /* Other */
- code span.pp { color: #bc7a00; } /* Preprocessor */
- code span.sc { color: #4070a0; } /* SpecialChar */
- code span.ss { color: #bb6688; } /* SpecialString */
- code span.st { color: #4070a0; } /* String */
- code span.va { color: #19177c; } /* Variable */
- code span.vs { color: #4070a0; } /* VerbatimString */
- code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
- .display.math{display: block; text-align: center; margin: 0.5rem auto;}
- </style>
- <!--[if lt IE 9]>
- <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
- <![endif]-->
-</head>
-<body>
-<header id="title-block-header">
-<h1 class="title">pmlag(1) | General Commands Manual</h1>
-</header>
-<h1 id="name">NAME</h1>
-<p>pmlag - Poor man’s link aggregator</p>
-<h1 id="synopsis">SYNOPSIS</h1>
-<p><code>pmlag [options]</code></p>
-<h1 id="description">DESCRIPTION</h1>
-<p>pmlag is a tool for bonding network interfaces together when the
-hardware on the other side of the cable(s) doesn’t necessarily support
-it.</p>
-<h1 id="options">OPTIONS</h1>
-<h4 id="h---help"><code>-h, --help</code></h4>
-<p>Show the help message and exit</p>
-<h4 id="c---config-path"><code>-c, --config <PATH></code></h4>
-<p>Load a custom configuration instead of the default
-(/etc/pmlag.conf)</p>
-<h1 id="configuration">CONFIGURATION</h1>
-<p>The configuration is an ini-like format, where the sections are the
-name of the device being configured.</p>
-<p>The configuration can be reloaded by sending the USR1 signal to the
-process.</p>
-<h4 id="broadcast">broadcast</h4>
-<p>Select the broadcast method for the bonded interface</p>
-<ul>
-<li>flood: send out broadcast packages out over all interfaces</li>
-<li>balanced: send out broadcast packages according to balancing
-strategy</li>
-</ul>
-<h4 id="mode">mode</h4>
-<p>Select the balancing mode for non-broadcast packages</p>
-<ul>
-<li>balance-rr: weighted round-robin balancing</li>
-<li>active-backup: define a primary and backup interface</li>
-</ul>
-<h4 id="mac">mac</h4>
-<p>Define the mac address of the bonded interface</p>
-<h4 id="master">master</h4>
-<p>Define which bonded interface the current described interface belongs
-to</p>
-<h4 id="weight">weight</h4>
-<p>Behaves differently depending on the mode the bonded interface is
-in:</p>
-<ul>
-<li>balance-rr: Specifies the share of traffic the interface
-transmits</li>
-<li>active-backup: Specifies the priority of the interface (higher =
-more preferred)</li>
-</ul>
-<p>Try to keep this number as low as possible, as it’s used directly to
-define the number of packets to send before moving to the next interface
-in the round-robin strategy.</p>
-<h3 id="example">Example:</h3>
-<div class="sourceCode" id="cb1"><pre
-class="sourceCode ini"><code class="sourceCode ini"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[bond0]</span></span>
-<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="dt">broadcast</span><span class="ot">=</span><span class="st">flood</span></span>
-<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="dt">mode</span><span class="ot">=</span><span class="st">balance-rr</span></span>
-<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="dt">mac</span><span class="ot">=</span><span class="st">01:23:45:67:89:AB</span></span>
-<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
-<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">[eth0]</span></span>
-<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">master</span><span class="ot">=</span><span class="st">bond0</span></span>
-<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="dt">weight</span><span class="ot">=</span><span class="dv">1</span></span>
-<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
-<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="kw">[eth1]</span></span>
-<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="dt">master</span><span class="ot">=</span><span class="st">bond0</span></span>
-<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="dt">weight</span><span class="ot">=</span><span class="dv">10</span></span></code></pre></div>
-<h1 id="license">LICENSE</h1>
-<p>The MIT License (MIT)</p>
-<p>Copyright © 2022 finwo</p>
-<p>Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-“Software”), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:</p>
-<p>The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.</p>
-<p>THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
-</body>
-</html>
diff --git a/src/main.c b/src/main.c
@@ -20,30 +20,183 @@ extern "C" {
#define NAME "pmlag"
#endif
+#define BROADCAST_FLOOD 1
+#define BROADCAST_BALANCED 2
+
+#define MODE_SLAVE 0
+#define MODE_ACTIVE_BACKUP 1
+#define MODE_BROADCAST 2
+#define MODE_BALANCE_RR 3
+
static const char *const usage[] = {
NAME " [options]",
NULL
};
+typedef struct {
+ // Linked list
+ void *next; // Ref to the next interface
+ // Config
+ char *name; // Name of the interface
+ char *master; // Name of the controlling interface
+ char *mac; // Mac address to set on the interface
+ int broadcast; // Broadcast mode
+ int mode; // Balancing mode
+ int weight; // Weight of the interface
+ // Activity
+ int socket; // Socket file descriptor
+ int remainder; // Round robin remainder
+} pmlag_interface;
+
+typedef struct {
+ pmlag_interface *interfaces;
+} pmlag_configuration;
+
+pmlag_configuration *configuration_active = NULL;
+pmlag_configuration *configuration_loading = NULL;
+
+static int config_load_handler(
+ void *user,
+ const char *section,
+ const char *name,
+ const char *value
+) {
+ pmlag_configuration* config = (pmlag_configuration *) user;
+
+ // Get interface being configured
+ pmlag_interface *iface = config->interfaces;
+ while(iface) {
+ if (!strcmp(iface->name, section)) {
+ break;
+ }
+ iface = iface->next;
+ }
+
+ // Create interface if not found
+ if (!iface) {
+ iface = calloc(1, sizeof(pmlag_interface));
+ iface->next = config->interfaces;
+ iface->name = strdup(section);
+ config->interfaces = iface;
+ }
+
+
+ // Set found value
+ if (!strcmp("master", name)) {
+ iface->master = strdup(value);
+ } else if (!strcmp("weight", name)) {
+ iface->weight = atoi(value);
+ } else if (!strcmp("broadcast", name)) {
+ if (!strcmp("flood", value)) {
+ iface->broadcast = BROADCAST_FLOOD;
+ } else if (!strcmp("balanced", value)) {
+ iface->broadcast = BROADCAST_BALANCED;
+ } else {
+ fprintf(stderr, "Unknown broadcast: %s\n", value);
+ return 0;
+ }
+ } else if (!strcmp("mode", name)) {
+ if (!strcmp("slave", value)) {
+ iface->mode = MODE_SLAVE;
+ } else if (!strcmp("active-backup", value)) {
+ iface->mode = MODE_ACTIVE_BACKUP;
+ } else if (!strcmp("broadcast", value)) {
+ iface->mode = MODE_BROADCAST;
+ } else if (!strcmp("balance-rr", value)) {
+ iface->mode = MODE_BALANCE_RR;
+ } else {
+ fprintf(stderr, "Unknown mode: %s\n", value);
+ return 0;
+ }
+ } else if (!strcmp("mac", name)) {
+ // TODO: parse mac address
+ iface->mac = strdup(value);
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+void config_free(pmlag_configuration *config) {
+ if (!config) return;
+ pmlag_interface *iface_next;
+ pmlag_interface *iface = config->interfaces;
+ while(iface) {
+ iface_next = iface->next;
+ if (iface_next == config->interfaces) break;
+ if (iface->mac ) free(iface->mac);
+ if (iface->master) free(iface->master);
+ if (iface->name ) free(iface->name);
+ free(iface);
+ iface = iface_next;
+ }
+ free(config);
+}
+
+pmlag_configuration * config_load(const char * filename) {
+ // Load config, entry-by-entry
+ pmlag_configuration *config = calloc(1, sizeof(pmlag_configuration));
+ if (ini_parse(filename, config_load_handler, config) < 0) {
+ fprintf(stderr, "Can not load %s\n", filename);
+ return NULL;
+ }
+ // Loop interface list
+ pmlag_interface *iface = config->interfaces;
+ while (iface && iface->next) iface = iface->next;
+ if (iface) iface->next = config->interfaces;
+ // Return produce
+ return config;
+}
+
int main(int argc, const char **argv) {
const char *config_file = "/etc/" NAME ".conf";
+ // Define which options we support
struct argparse_option options[] = {
OPT_HELP(),
OPT_STRING('c', "config", &config_file, "Config file to use", NULL, 0, 0),
OPT_END(),
};
+ // Parse command line arguments
struct argparse argparse;
argparse_init(&argparse, options, usage, 0);
argparse_describe(&argparse, NULL,
// TODO: format to terminal width
"\n" NAME " is a tool for bonding network interfaces together when the "
- "hardware\non the other side of the cable doesn't necessarily support it."
+ "hardware\non the other side of the cable(s) doesn't support it."
"\n"
);
argc = argparse_parse(&argparse, argc, argv);
+ // Load configuration
+ pmlag_configuration *loaded = config_load(config_file);
+
+ // VERY validation
+ if (!loaded->interfaces) {
+ fprintf(stderr, "No interfaces defined in config\n");
+ return 1;
+ }
+
+ // TODO:
+ // - start thread for every interface
+ // - let thread create bond
+ // - let thread create socket
+ // - make thread close bond if not found in config
+ // - make thread close socket if not found in config
+ // - incoming on socket = send to master
+ // - incoming on master = <mode> to slaves
+ // - mode_broadcast = to all
+ // - mode_active_backup = preferred based upon socket open or not?
+ // - mode_rr
+ // - move interface to back of line when remainder = 0
+ // - send packet and decrement remainder
+ // - implement config replacement on USR1 in parent
+ // - mutex all the things
+
+ config_free(loaded);
+
/* int sockfd = sockraw_open(INTERFACE); */
/* // Prepare ingress buffer */