pmlag

Poor man's link aggregation
git clone git://git.finwo.net/app/pmlag
Log | Files | Refs | LICENSE

commit 31a735047b37a631ea87d82795f6a899b3f3bce7
parent fbeb0c0dd24fd9958b5ce88e2bedb6dcafe67690
Author: finwo <finwo@pm.me>
Date:   Sat, 11 Jul 2026 01:42:46 +0200

Project restart: reliable section reading from config

Diffstat:
A.dep | 3+++
M.editorconfig | 1+
M.gitignore | 1+
DCODE_OF_CONDUCT.md | 6------
ADESIGN.md | 17+++++++++++++++++
DLICENSE | 18------------------
ALICENSE.md | 34++++++++++++++++++++++++++++++++++
MMakefile | 47++++++++---------------------------------------
DREADME.md | 114-------------------------------------------------------------------------------
Darch/common/Makefile | 40----------------------------------------
Darch/common/Makefile.pkg | 7-------
Ddocs/compiling.md | 11-----------
Ddocs/index.md | 9---------
Ddocs/installing.md | 12------------
Aexample.conf | 14++++++++++++++
Dinstall.sh | 43-------------------------------------------
Mmanpage.1.md | 41++++++-----------------------------------
Dpackage.ini | 7-------
Dpackage/common/files/etc/pmlag.ini | 8--------
Dpackage/generic/build.sh | 17-----------------
Dpackage/openrc/build.sh | 17-----------------
Dpackage/openrc/files/etc/init.d/pmlag | 11-----------
Dpackage/procd/build.sh | 17-----------------
Dpackage/procd/files/etc/init.d/pmlag | 10----------
Msrc/main.c | 368+------------------------------------------------------------------------------
Msrc/util/config.c | 173+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/util/config.h | 36+++++++-----------------------------
Dsrc/util/routing-table.c | 96-------------------------------------------------------------------------------
Dsrc/util/routing-table.h | 31-------------------------------
Dsrc/util/socket.c | 156-------------------------------------------------------------------------------
Dsrc/util/socket.h | 9---------
Rarch/linux-glibc-amd64/.gitkeep -> target/common/.gitkeep | 0
Atarget/common/Makefile | 41+++++++++++++++++++++++++++++++++++++++++
Rarch/linux-musl-amd64/.gitkeep -> target/linux-glibc-amd64/.gitkeep | 0
Dtest.ini | 12------------
35 files changed, 225 insertions(+), 1202 deletions(-)

diff --git a/.dep b/.dep @@ -0,0 +1,3 @@ +benhoyt/inih https://git.finwo.net/misc/dep-repository/archives/heads/pkg/benhoyt/inih.tar.gz +cofyc/argparse https://git.finwo.net/misc/dep-repository/archives/heads/pkg/cofyc/argparse.tar.gz +finwo/mindex https://git.finwo.net/lib/mindex.c/archives/heads/main.tar.gz diff --git a/.editorconfig b/.editorconfig @@ -7,6 +7,7 @@ insert_final_newline = true charset = utf-8 indent_size = 2 indent_style = space +trim_trailing_whitespace = true [Makefile*] indent_style = tab diff --git a/.gitignore b/.gitignore @@ -1,2 +1,3 @@ /bak/ /build/ +/lib/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md @@ -1,6 +0,0 @@ -<!-- 46b43825-f791-485e-9445-415ee7bbbf2d --> -# Contributor Code of Conduct - -This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters. - -For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage. diff --git a/DESIGN.md b/DESIGN.md @@ -0,0 +1,17 @@ +Custom ethertype wrapping our discovery protocol: 0xDEAD + +All discovery protocol packets are broadcasted, we're managing L2 here + +Packet format: + ethernet: + 6x DST MAC + 6x SRC MAC + 2x Ethertype (0xDEAD) + discovery protocol: + 1x Version (0x00) + 1x Segment + +Rough idea: + peer broadcasts N packets each segment, segment tick = commit + speeds up broken link detection: + if we receive only the SRC on a single interface, we know the MAC is unreachable on other interfaces within 2 ticks diff --git a/LICENSE b/LICENSE @@ -1,18 +0,0 @@ -Copyright 2023 finwo - -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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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. diff --git a/LICENSE.md b/LICENSE.md @@ -0,0 +1,34 @@ +Copyright (c) 2026 finwo + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to use, copy, +modify, and distribute the Software, subject to the following conditions: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. + + 2. Redistributions in binary form, or any public offering of the Software + (including hosted or managed services), must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided. + + 3. Any redistribution or public offering of the Software must clearly attribute + the Software to the original copyright holder, reference this License, and + include a link to the official project repository or website. + + 4. The Software may not be renamed, rebranded, or marketed in a manner that + implies it is an independent or proprietary product. Derivative works must + clearly state that they are based on the Software. + + 5. Modifications to copies of the Software must carry prominent notices stating + that changes were made, the nature of the modifications, and the date of the + modifications. + +Any violation of these conditions terminates the permissions granted herein. + +THIS 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 COPYRIGHT +HOLDER OR CONTRIBUTORS 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. diff --git a/Makefile b/Makefile @@ -1,47 +1,16 @@ TARGET:=linux-glibc-amd64 -SVC:=generic - -# # Initial config -# LIBS= -# SRC:= -# SRC+=$(wildcard src/*.c) -# SRC+=$(wildcard src/*/*.c) - -# override CFLAGS?=-Wall -s -O2 - -# INCLUDES:= -# INCLUDES+=-I src - -# include lib/.dep/config.mk - -# override CFLAGS+=$(INCLUDES) - -# # Which objects to generate before merging everything together -# OBJ:=$(SRC:.c=.o) .PHONY: default default: build/${TARGET}/pmlag -.PHONY: package -package: build/${TARGET}/pmlag-${SVC}-${TARGET}.tar.gz - -build/${TARGET}: $(wildcard src/*.c src/*.h src/*/*.c src/*/*.h) - mkdir -p build/${TARGET} - cp package.ini build/${TARGET}/package.ini - cp manpage.1.md build/${TARGET}/manpage.1.md - cp -rT src/ build/${TARGET}/src - cp -rT package/ build/${TARGET}/package - cp -rT arch/common/ build/${TARGET} - cp -rT arch/${TARGET}/ build/${TARGET} - cp -rT arch/${TARGET}/ build/${TARGET} - -build/${TARGET}/pmlag: build/${TARGET} $(wildcard build/${TARGET}/src/*.c build/${TARGET}/src/*.h build/${TARGET}/src/*/*.c build/${TARGET}/src/*/*.h) +build/${TARGET}/pmlag: build/${TARGET} $(wildcard src/*.c src/*.h src/*/*.c src/*/*.h) cd build/${TARGET} && dep install $(MAKE) --directory build/${TARGET} TARGET=${TARGET} -build/${TARGET}/pmlag-${SVC}-${TARGET}.tar.gz: build/${TARGET}/pmlag - $(MAKE) --directory build/${TARGET} TARGET=${TARGET} SVC=${SVC} pmlag-${SVC}-${TARGET}.tar.gz - -.PHONY: clean -clean: - rm -rf build +build/${TARGET}: $(wildcard src/*.c src/*.h src/*/*.c src/*/*.h) + mkdir -p build/${TARGET} + cp -rT .dep build/${TARGET}/.dep + cp -rT manpage.1.md build/${TARGET}/manpage.1.md + cp -rT src/ build/${TARGET}/src + cp -rT target/common/ build/${TARGET} + cp -rT target/${TARGET}/ build/${TARGET} diff --git a/README.md b/README.md @@ -1,114 +0,0 @@ -pmlag -===== -<small>Poor man's link aggregator</small> - -pmlag is a tool for bonding network interfaces together when the hardware on -the other side of the cable(s) doesn't support it. - -TL;DR; ------- - -```sh -curl -sSL https://raw.githubusercontent.com/finwo/pmlag/edge/install.sh | sudo TARGET=<target> bash -``` - -**Targets:** -- openwrt-amd64 -- linux-amd64 (default) - -Getting started ---------------- - -To get started, please check out [the documentation](doc/index.md). - -LICENSE -------- - -This program itself is licensed under the MIT license: - -Copyright 2023 finwo - -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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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. - ---- - -This project makes use of the following libraries, each of which having their -own license. Please note that the licenses shown here may be out of date. - -- [benhoyt/inih](https://github.com/benhoyt/inih) - - The "inih" library is distributed under the New BSD license: - - Copyright (c) 2009, Ben Hoyt - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Ben Hoyt nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -- [cofyc/argparse](https://github.com/cofyc/argparse) - - The MIT License (MIT) - - Copyright (c) 2012-2013 Yecheng Fu <cofyc.jackson@gmail.com> - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. - -- [finwo/mindex](https://github.com/finwo/c-mindex) - - Copyright 2023 finwo - - 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: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - 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. diff --git a/arch/common/Makefile b/arch/common/Makefile @@ -1,40 +0,0 @@ -TARGET?= -SVC?= - -# Initial config -LIBS= -SRC:= -SRC+=$(wildcard src/*.c) -SRC+=$(wildcard src/*/*.c) - -override CFLAGS?=-Wall -s -O2 - -INCLUDES:= -INCLUDES+=-I src - -include lib/.dep/config.mk - -override CFLAGS+=$(INCLUDES) - -# Which objects to generate before merging everything together -OBJ:=$(SRC:.c=.o) - -BIN=pmlag - -.PHONY: default -default: $(BIN) $(BIN).1 - -include Makefile.pkg - -%.o: %.c $(LIBS) - $(CC) $(CFLAGS) $(@:.o=.c) -D__NAME=\"$(BIN)\" -c -o $@ - -$(BIN): $(OBJ) - $(CC) $(CFLAGS) $(OBJ) -o $@ - -# Build manpage -$(BIN).1: manpage.1.md - env NAME=$(BIN) envsubst < manpage.1.md | pandoc --standalone --from markdown --to man -o $(BIN).1 - -${BIN}-${SVC}-${TARGET}.tar.gz: $(BIN) - package/${SVC}/build.sh diff --git a/arch/common/Makefile.pkg b/arch/common/Makefile.pkg @@ -1,7 +0,0 @@ -default: - # Run 'make install' to install this package - -.PHONY: install -install: default - install pmlag $(DESTDIR)/bin - install pmlag.1 $(DESTDIR)/share/man/man1 diff --git a/docs/compiling.md b/docs/compiling.md @@ -1,11 +0,0 @@ -Compiling ---------- - -This library makes use of [dep](https://github.com/finwo/dep) to manage it's -dependencies and exports. - -To build, first run `dep install` from the root of the repository, after which -you can run `make` to build the project. - -After you've compiled the pmlag utility, you may want to run `sudo make install` -to install it on your machine. diff --git a/docs/index.md b/docs/index.md @@ -1,9 +0,0 @@ -pmlag -===== -<small>Poor man's link aggregator</small> - -This is the documentation index. Feel free to check out one of the topics in -here: - -- [Compiling](compiling.md) -- [Installing](installing.md) diff --git a/docs/installing.md b/docs/installing.md @@ -1,12 +0,0 @@ -Installing ----------- - -For now, there's no package managers that include the pmlag utility that we know -of. - -You can either install pmlag from source by [compiling it -yourself](compiling.md), or by going to the -[edge](https://github.com/finwo/pmlag/releases/tag/edge) or -[stable](https://github.com/finwo/pmlag/releases/tag/stable) tag's page, -downloading it's precompiled package and following the instructions in the -package. diff --git a/example.conf b/example.conf @@ -0,0 +1,14 @@ +[bond-A] +interface = eth0 +interface = eth1 +tick = 100 # Tick every 100ms, allows fast link drop +# pizza calzone +# pizza = yum + # pizza = yum +a # b = c +d e = f + +[bond-B] +interface = eth2 +interface = eth3 +tick = 0 # No tick = only "normal" auto discovery diff --git a/install.sh b/install.sh @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -if [ "$EUID" -ne 0 ]; then - echo "This command must run as root" >&2 - exit 1 -fi - -if [ -z "$TARGET" ]; then - export TARGET=linux-amd64 -fi - -case "${TARGET}" in - - openwrt-*) - curl -sSL https://github.com/finwo/pmlag/releases/download/edge/pmlag-${TARGET}.tar.gz -o - | \ - gzip -d | \ - tar xv --strip-components=1 --directory=/ - ;; - - linux-*) - echo -n "Downloading tarball..." - curl -sSL https://github.com/finwo/pmlag/releases/download/edge/pmlag-${TARGET}.tar.gz -o - | \ - gzip -d | \ - tar x - echo "done" - echo "Installing binaries..." - cd pmlag-${TARGET} - make install - if [ -d /etc/init.d ]; then - echo "Installing openrc service..." - make svc_openrc - fi - cd .. - rm -rf pmlag-${TARGET} - ;; - - *) - echo "Unknown target type '${TARGET}'" >&2 - exit 1 - ;; -esac - -echo "Finished" diff --git a/manpage.1.md b/manpage.1.md @@ -16,11 +16,10 @@ SYNOPSIS OPTIONS ======= -Option | Description | - --- | --- -&dash;c &lt;file&gt; | Select the config file to use (default: /etc/pmlag/pmlag.ini) -| -&dash;h &#124; &dash;&dash;help | Show basic usage of the utility +| Option | Description | +| ------------------------------- | -------------------------------------------------------------- | +| &dash;c &lt;file&gt; | Select the config file to use (default: /etc/pmlag/pmlag.conf) | +| &dash;h &#124; &dash;&dash;help | Show basic usage of the utility | CONFIGURATION ============= @@ -30,26 +29,8 @@ TODO LICENSE ======= -This program itself is licensed under the MIT license: - -Copyright 2023 finwo - -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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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. +This program itself is licensed under A CUSTOM LICENSE +EDIT THIS --- @@ -108,13 +89,3 @@ own license. Please note that the licenses shown here may be out of date. 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. - -- [finwo/mindex](https://github.com/finwo/c-mindex) - - Copyright 2023 finwo - - 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: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - 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. diff --git a/package.ini b/package.ini @@ -1,7 +0,0 @@ -[dependencies] -benhoyt/inih=https://raw.githubusercontent.com/finwo/dep-repository/main/benhoyt/inih/package-edge.ini -cofyc/argparse=https://raw.githubusercontent.com/finwo/dep-repository/main/cofyc/argparse/package.ini -finwo/mindex=https://github.com/finwo/c-mindex/archive/refs/tags/edge.tar.gz -[package] -deps=lib -name=finwo/pmlag diff --git a/package/common/files/etc/pmlag.ini b/package/common/files/etc/pmlag.ini @@ -1,8 +0,0 @@ -; This is an example configuration, showcasing how you might configure multiple types of bonds - -; [bond0] -; interface=eth2 -; interface=eth3 -; hwaddr=eth2 -; hwaddr=56:45:34:23:12:02 -; hwaddr=random diff --git a/package/generic/build.sh b/package/generic/build.sh @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -NAM=pmlag-generic-${TARGET} - -cd $(dirname $0)/../../ -mkdir -p ${NAM} -cp Makefile.pkg ${NAM}/Makefile -cp pmlag ${NAM}/pmlag -cp pmlag.1 ${NAM}/pmlag.1 -mkdir -p ${NAM}/svc -cp -rT package/common/files ${NAM}/svc/common -cp -rT package/openrc/files ${NAM}/svc/openrc -cp -rT package/procd/files ${NAM}/svc/procd -tar c ${NAM} | gzip -9 > ${NAM}.tar.gz -rm -rf ${NAM} diff --git a/package/openrc/build.sh b/package/openrc/build.sh @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -NAM=pmlag-openrc-${TARGET} - -cd $(dirname $0)/../../ -mkdir -p ${NAM}/usr/bin -mkdir -p ${NAM}/usr/share/man/man1 -mkdir -p ${NAM}/etc/init.d -install pmlag ${NAM}/usr/bin -install pmlag.1 ${NAM}/usr/share/man/man1 -cp -rT package/common/files ${NAM}/ -cp -rT package/openrc/files ${NAM}/ -DESTDIR=/usr envsubst '${DESTDIR}' < package/openrc/files/etc/init.d/pmlag > ${NAM}/etc/init.d/pmlag -tar c ${NAM} | gzip -9 > ${NAM}.tar.gz -rm -rf ${NAM} diff --git a/package/openrc/files/etc/init.d/pmlag b/package/openrc/files/etc/init.d/pmlag @@ -1,11 +0,0 @@ -#!/sbin/openrc-run - -name=$RC_SVCNAME -command=${DESTDIR}/bin/pmlag -command_args="-c /etc/pmlag/pmlag.ini" -pidfile="/var/run/$RC_SVCNAME/$RC_SVCNAME.pid" -command_background="no" - -depend() { - need net -} diff --git a/package/procd/build.sh b/package/procd/build.sh @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -NAM=pmlag-procd-${TARGET} - -cd $(dirname $0)/../../ -mkdir -p ${NAM}/usr/bin -mkdir -p ${NAM}/usr/share/man/man1 -mkdir -p ${NAM}/etc/init.d -install pmlag ${NAM}/usr/bin -install pmlag.1 ${NAM}/usr/share/man/man1 -cp -rT package/common/files ${NAM}/ -cp -rT package/procd/files ${NAM}/ -DESTDIR=/usr envsubst '${DESTDIR}' < package/procd/files/etc/init.d/pmlag > ${NAM}/etc/init.d/pmlag -tar c ${NAM} | gzip -9 > ${NAM}.tar.gz -rm -rf ${NAM} diff --git a/package/procd/files/etc/init.d/pmlag b/package/procd/files/etc/init.d/pmlag @@ -1,10 +0,0 @@ -#!/bin/sh /etc/rc.common - -USE_PROCD=1 -START=95 -STOP=01 -start_service() { - procd_open_instance - procd_set_param command ${DESTDIR}/bin/pmlag -c /etc/pmlag.ini - procd_close_instance -} diff --git a/src/main.c b/src/main.c @@ -1,220 +1,18 @@ -#include <linux/if_ether.h> -#include <linux/if_packet.h> -#include <netinet/in.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/time.h> -#include <unistd.h> #include "cofyc/argparse.h" #include "util/config.h" -#include "util/routing-table.h" -#include "util/socket.h" -#define RCVBUFSIZ 131072 - -#define TICK 100 -#define PMLAG_RT_FAST 64 - -#define PMLAG_COMMAND_STREE_BROADCAST 0 - -unsigned char *rcvbuf = NULL; static const char *const usage[] = { __NAME " [options]", NULL }; -int64_t millis() { - struct timeval tv; - gettimeofday(&tv, NULL); - return (tv.tv_sec * ((int64_t)1000)) + (tv.tv_usec / 1000); -} - -void handle_packet_bond(struct pmlag_bond *bond) { - struct sockaddr_ll saddr_ll; - int i, buflen; - - // Prepare sending filter - saddr_ll.sll_halen = ETH_ALEN; - memcpy(saddr_ll.sll_addr, bond->hwaddr, ETH_ALEN); - - buflen = read(bond->sockfd, rcvbuf, RCVBUFSIZ); - - /* // Get interface to send the packet from */ - /* struct pmlag_iface *iface = (memcmp(rcvbuf, "\xFF\xFF\xFF\xFF\xFF\xFF", ETH_ALEN) == 0) */ - /* ? NULL */ - /* : rt_find(bond->rt, rcvbuf); */ - - struct pmlag_iface *iface = bond->iface[0]; - - /* if (iface) { */ - /* printf( */ - /* "Sending to iface %s for %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", */ - /* iface->name, */ - /* rcvbuf[0], */ - /* rcvbuf[1], */ - /* rcvbuf[2], */ - /* rcvbuf[3], */ - /* rcvbuf[4], */ - /* rcvbuf[5] */ - /* ); */ - /* } else { */ - /* printf( */ - /* "No iface for %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", */ - /* rcvbuf[0], */ - /* rcvbuf[1], */ - /* rcvbuf[2], */ - /* rcvbuf[3], */ - /* rcvbuf[4], */ - /* rcvbuf[5] */ - /* ); */ - /* } */ - - /* // Broadcast on ALL interfaces if no rt entry OR broadcast packet */ - /* if (!iface) { */ - /* for( i = 0 ; i < bond->iface_count ; i++ ) { */ - /* saddr_ll.sll_ifindex = bond->iface[i]->ifidx; */ - /* if (sendto(bond->iface[i]->sockfd, rcvbuf, buflen, 0, (const struct sockaddr*)&saddr_ll, sizeof(struct sockaddr_ll)) != buflen) { */ - /* perror("sendto"); */ - /* printf("Error during sendto on %d\n", __LINE__); */ - /* } */ - /* } */ - /* return; */ - /* } */ - - // Forward packet to iface as-is - saddr_ll.sll_ifindex = iface->ifidx; - if (sendto(iface->sockfd, rcvbuf, buflen, 0, (const struct sockaddr*)&saddr_ll, sizeof(struct sockaddr_ll)) != buflen) { - perror("sendto"); - printf("Error during sendto on %d\n", __LINE__); - } -} - -void handle_packet_iface(struct pmlag_iface *iface) { - struct pmlag_bond *bond = iface->bond; - /* struct pmlag_rt_entry *rt_entry; */ - int send_len, buflen, idx; - uint16_t ethtype; - uint16_t command; - uint16_t bcid; - - // Read the packet - buflen = recvfrom(iface->sockfd, rcvbuf, RCVBUFSIZ, 0, NULL, 0); - if (buflen < 0) { - perror("recvfrom"); - return; - } - -/* // Basic error checking */ -/* // ethernet says minimum of 60, we just need 18 */ -/* if (buflen < 18) return; */ - - /* // Always add mac to routing table */ - /* rt_upsert( */ - /* bond->rt, */ - /* iface, */ - /* rcvbuf + ETH_ALEN, */ - /* bond->bc_id */ - /* ); */ - - // Get the ethtype - memcpy(&ethtype, rcvbuf + (2*ETH_ALEN), sizeof(ethtype)); - ethtype = ntohs(ethtype); - - if (ethtype == 0x0666) { - return; - } - - // Don't touch packets that don't use our described protocol - /* if (ethtype != 0x0666) { */ - /* printf("Read %d bytes (%d) -- rt = %ld\n", buflen, ethtype, bond->rt->length); */ - send_len = write(iface->bond->sockfd, rcvbuf, buflen); - if (send_len != buflen) { - perror("write"); - return; - } - return; - /* } */ - - /* // Ignore our own packets (loop detected, may do something with it later) */ - /* if (!memcmp(rcvbuf + ETH_ALEN, iface->bond->hwaddr, ETH_ALEN)) { */ - /* printf("Loop detected\n"); */ - /* return; */ - /* } */ - - /* // Fetch the command that was called */ - /* memcpy(&command, rcvbuf + (2*ETH_ALEN) + sizeof(ethtype), sizeof(command)); */ - /* command = ntohs(command); */ - - /* switch(command) { */ - /* case PMLAG_COMMAND_STREE_BROADCAST: */ - - /* // Stop if we already triggered this round */ - /* if (bond->state & PMLAG_STATE_TRIGGERED) break; */ - /* bond->state |= PMLAG_STATE_TRIGGERED; */ - - /* // Have the network fight for leader status */ - /* if (bond->bc_timer) { */ - /* // Demote to follower if we were waiting */ - /* bond->bc_timer = 3; */ - /* } else { */ - /* // Random between now leader and pensive */ - /* bond->bc_timer = (rand() % 2) * 2; */ - /* } */ - - /* // Fetch the broadcast id */ - /* memcpy(&bcid, rcvbuf + (2*ETH_ALEN) + sizeof(ethtype) + sizeof(command), sizeof(bcid)); */ - /* bcid = ntohs(bcid); */ - /* bond->bc_id = bcid; */ - - /* // Remove old entries from routing table */ - /* for( idx = 0; idx < bond->rt->length ; idx++ ) { */ - /* rt_entry = bond->rt->items[idx]; */ - /* if ((bcid - rt_entry->bcidx) > PMLAG_RT_FAST) { */ - /* printf( */ - /* "Removing rt_entry for %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", */ - /* rt_entry->mac[0], */ - /* rt_entry->mac[1], */ - /* rt_entry->mac[2], */ - /* rt_entry->mac[3], */ - /* rt_entry->mac[4], */ - /* rt_entry->mac[5] */ - /* ); */ - /* mindex_delete(bond->rt, rt_entry); // rt_entry is now unsafe */ - /* continue; */ - /* } */ - /* // More things here? */ - /* } */ - - /* break; */ - /* default: */ - /* // Unknown command */ - /* return; */ - /* } */ -} - -void handle_packet(void *entity) { - struct pmlag_iface *iface = entity; - struct pmlag_bond *bond = entity; - if (iface->type == PMLAG_ENTITY_TYPE_IFACE) { - return handle_packet_iface(iface); - } - if (bond->type == PMLAG_ENTITY_TYPE_BOND) { - return handle_packet_bond(bond); - } -} - int main(int argc, const char **argv) { - char *config_file="/etc/pmlag/pmlag.ini"; - int epfd; - struct pmlag_bond *bond; - struct pmlag_iface *iface; - struct sockaddr_ll saddr_ll; - saddr_ll.sll_halen = ETH_ALEN; + char *config_file="/etc/pmlag/pmlag.conf"; // Seed random uint32_t seed; @@ -241,168 +39,10 @@ int main(int argc, const char **argv) { ); argc = argparse_parse(&argparse, argc, argv); - // Load configuration file - struct pmlag_configuration *config = config_load(config_file, NULL); - if (!config) { - return 1; - } - - // Initialize packet buffer - rcvbuf = (unsigned char *)malloc(RCVBUFSIZ); - - // Initialize epoll - epfd = epoll_create1(0); - - // DEBUG: Display bonds - int b = 0; - int i = 0; - for( b = 0 ; b < config->bond_count ; b++ ) { - printf("%s\n", config->bond[b]->name); - for( i = 0 ; i < config->bond[b]->iface_count ; i++ ) { - printf(" - %s\n", config->bond[b]->iface[i]->name); - } - } - - // Prepare announce buffer - uint16_t ethtype = htons(0x0666); - size_t anc_buflen = (ETH_ALEN*2) + sizeof(ethtype) + sizeof(uint16_t) + sizeof(uint16_t); // mac + ethertype + command + broadcast-index - char *anc_buffer = malloc(anc_buflen); - - // Wait for things to happen & call tick methods periodically - int ev_count; - struct epoll_event events[8]; - int64_t ttime = millis(); - int64_t tdiff = 0; - while(1) { - - // Calculate waiting time & periodic actions - tdiff = ttime - millis(); - if (tdiff <= 0) { - ttime += TICK; - tdiff += TICK; - - // Handle sockets - for( b = 0 ; b < config->bond_count ; b++ ) { - - // (re)open bond tap - bond = config->bond[b]; - if (!bond->sockfd) { - bond->sockfd = tap_alloc(bond->name, bond->hwaddr); - bond->state = 0; - bond->bc_timer = 3; // set to 2 in combination with the decrement below - if (bond->sockfd < 0) { - perror("tap_alloc"); - bond->sockfd = 0; - continue; - } - bond->epev = calloc(1, sizeof(struct epoll_event)); - if (!bond->epev) { - perror("calloc"); - bond->epev = NULL; - continue; - } - bond->epev->events = EPOLLIN; - bond->epev->data.ptr = bond; - if (epoll_ctl(epfd, EPOLL_CTL_ADD, bond->sockfd, bond->epev)) { - free(bond->epev); - close(bond->sockfd); - bond->sockfd = 0; - bond->epev = NULL; - continue; - } - } - - // (re)initialize routing table - if (!bond->rt) { - bond->rt = rt_init(bond); - } - - // Fetch bond's mac address if missing (from random) - if (!bond->hwaddr) { - bond->hwaddr = iface_mac(bond->name); - } - - // Mark the bond as untriggered, decrement timer and increment bc_id - bond->state &= ~PMLAG_STATE_TRIGGERED; - bond->bc_timer--; - - for( i = 0 ; i < bond->iface_count ; i++ ) { - iface = bond->iface[i]; - - // (re)open interface socket - if (!iface->sockfd) { - iface->sockfd = sockraw_open(iface->name); - if (iface->sockfd < 0) { - perror("sockraw_open"); - iface->sockfd = 0; - continue; - } - iface->ifidx = iface_idx(iface->sockfd, iface->name); - /* printf("Opened socket for %s\n", iface->name); */ - } - - // Register it with epoll - if (!iface->epev) { - iface->epev = calloc(1, sizeof(struct epoll_event)); - if (!iface->epev) { - perror("calloc"); - iface->epev = NULL; - continue; - } - iface->epev->events = EPOLLIN; - iface->epev->data.ptr = iface; - if (epoll_ctl(epfd, EPOLL_CTL_ADD, iface->sockfd, iface->epev)) { - free(iface->epev); - close(iface->sockfd); - iface->sockfd = 0; - iface->epev = NULL; - continue; - } - /* printf("Registered %s with epoll\n", iface->name); */ - } - } - - /* // Handle broadcasts */ - /* if (bond->bc_timer < 0) { */ - /* bond->bc_timer = 0; */ - - /* // Increment broadcast id & prepare network orientation */ - /* bond->bc_id++; */ - /* bond->bc_id = htons(bond->bc_id); */ - - /* // Prepare packet */ - /* memset(anc_buffer , 0xFF , ETH_ALEN); // Send to broadcast */ - /* memcpy(anc_buffer + (1*ETH_ALEN) , bond->hwaddr , ETH_ALEN); // From bond */ - /* memcpy(anc_buffer + (2*ETH_ALEN) , &ethtype , sizeof(ethtype)); // ethtype 0x0666 */ - /* memset(anc_buffer + (2*ETH_ALEN) + sizeof(ethtype) , 0x00 , sizeof(uint16_t)); // command (interface detection broadcast) */ - /* memcpy(anc_buffer + (2*ETH_ALEN) + sizeof(ethtype) + sizeof(uint16_t), &(bond->bc_id), sizeof(bond->bc_id)); // broadcast id */ - - /* // Revert bc_id to host-orientation */ - /* bond->bc_id = ntohs(bond->bc_id); */ - - /* // Send packet on all interfaces */ - /* memcpy(saddr_ll.sll_addr, bond->hwaddr, ETH_ALEN); */ - /* for( i=0; i < bond->iface_count ; i++ ) { */ - /* iface = bond->iface[i]; */ - /* saddr_ll.sll_ifindex = iface->ifidx; */ - /* if (sendto(iface->sockfd, anc_buffer, anc_buflen, 0, (const struct sockaddr*)&saddr_ll, sizeof(struct sockaddr_ll)) != anc_buflen) { */ - /* perror("sendto"); */ - /* printf("Error during sendto on %d\n", __LINE__); */ - /* } */ - /* } */ - - /* } */ - } - } - - // Handle incoming data - if (tdiff < 0) tdiff = 0; - ev_count = epoll_wait(epfd, events, 8, tdiff); - for( i=0 ; i<ev_count; i++) { - handle_packet(events[i].data.ptr); - } + struct pmlag_configuration *config = calloc(1, sizeof(struct pmlag_configuration)); - } + printf("Config file: %s\n", config_file); + config_load(config_file, config); return 0; } diff --git a/src/util/config.c b/src/util/config.c @@ -1,12 +1,29 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/epoll.h> + +#include "config.h" #include "benhoyt/inih.h" +#include "finwo/mindex.h" -#include "config.h" -#include "socket.h" +int mindex_bond_cmp(const void *a, const void *b, void *udata) { + (void)udata; + const struct pmlag_configuration_bond *_a = a; + const struct pmlag_configuration_bond *_b = b; + return strcmp(_a->name, _b->name); +} + +void mindex_bond_purge(void *item, void *udata) { + struct pmlag_configuration_bond *bond = item; + (void)udata; + if (!item) return; + if (bond->name) free(bond->name); +} + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif static int config_load_handler( void *user, @@ -14,102 +31,90 @@ static int config_load_handler( const char *name, const char *value ) { - struct pmlag_bond *bond = NULL; - struct pmlag_iface *iface = NULL; - int bond_index = 0; - int iface_index = 0; + // struct pmlag_bond *bond = NULL; + // struct pmlag_iface *iface = NULL; + // int bond_index = 0; + // int iface_index = 0; struct pmlag_configuration* config = (struct pmlag_configuration *) user; - printf("[%s].%s: %s\n", section, name, value); - // Ensure we have a bond list - if (!config->bond) config->bond = calloc(1, sizeof(void*)); - - // Find the bond being configured - for( bond_index = 0 ; bond_index < config->bond_count ; bond_index ++ ) { - if (!strcmp(config->bond[bond_index]->name, section)) { - bond = config->bond[bond_index]; - break; - } - } - - // Create bond if not found - if (!bond) { - config->bond[config->bond_count++] = bond = calloc(1, sizeof(struct pmlag_bond)); - config->bond = realloc(config->bond, (config->bond_count + 1) * sizeof(void*)); - config->bond[config->bond_count ] = NULL; - bond->type = PMLAG_ENTITY_TYPE_BOND; - bond->name = strdup(section); + // Detect invalid entries + int i, l; + for( i = 0, l = strlen(name) ; i < l ; i++ ) { + if (name[i] == 0x5F ) continue; // '_' + if (name[i] >= 0x30 && name[i] <= 0x39) continue; // 0-9 + if (name[i] >= 0x41 && name[i] <= 0x5A) continue; // A-Z + if (name[i] >= 0x61 && name[i] <= 0x7A) continue; // a-z + return 0; // Reject } - if (0) { - // Intentionally empty - - } else if (!strcmp(name, "interface")) { - - // Ensure we have an iface list - if (!bond->iface) bond->iface = calloc(1, sizeof(void*)); - - // Find the interface you're referencing - for( iface_index = 0 ; iface_index < bond->iface_count ; iface_index ++ ) { - if (!strcmp(config->bond[bond_index]->iface[iface_index]->name, value)) { - iface = config->bond[bond_index]->iface[iface_index]; - break; - } - } - - // Create bond if not found - if (!iface) { - bond->iface[bond->iface_count++] = iface = calloc(1, sizeof(struct pmlag_iface)); - bond->iface = realloc(bond->iface, (bond->iface_count + 1) * sizeof(void*)); - bond->iface[bond->iface_count ] = NULL; - iface->type = PMLAG_ENTITY_TYPE_IFACE; - iface->name = strdup(value); - iface->bond = bond; - } - - } else if (!strcmp(name, "hwaddr")) { - - // Find the interface you're referencing - for( iface_index = 0 ; iface_index < bond->iface_count ; iface_index ++ ) { - if (!strcmp(config->bond[bond_index]->iface[iface_index]->name, value)) { - iface = config->bond[bond_index]->iface[iface_index]; - break; - } + // Strip # comments from values + char *found_comment = strstr(value, "#"); + if (found_comment) { + for(; found_comment > (value + 1) ;) { + if ( *(found_comment) >= 0x21 && *(found_comment-1) <= 0x7E ) break; + found_comment--; } + *found_comment = 0x00; + } - if (iface) { - // Got interface by that name = use it's hwaddr - if (bond->hwaddr) free(bond->hwaddr); - bond->hwaddr = iface_mac(iface->name); - } else if (!strcmp(value, "random")) { - // "random" = null, a.k.a. let the kernel generate a random mac - if (bond->hwaddr) free(bond->hwaddr); - bond->hwaddr = NULL; - } else { - // xx:xx:xx:xx:xx:xx - if (bond->hwaddr) free(bond->hwaddr); - bond->hwaddr = (unsigned char *)malloc(6); // ETH_ALEN should be 6, but this is safer - sscanf(value, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", - &(bond->hwaddr[0]), - &(bond->hwaddr[1]), - &(bond->hwaddr[2]), - &(bond->hwaddr[3]), - &(bond->hwaddr[4]), - &(bond->hwaddr[5]) - ); - } + // Find or create bond + struct pmlag_configuration_bond *config_bond = mindex_get(config->bonds, (&(struct pmlag_configuration_bond){ .name = (char *)section })); + if (!config_bond) { + config_bond = calloc(1, sizeof(struct pmlag_configuration_bond)); + config_bond->name = strdup(section); + mindex_set(config->bonds, config_bond); } - return 0; + // // Find the bond + // struct pmlag_configuration_bond *config_bond = NULL; + // for( i = 0 ; i < config->bond_len ; i++ ) { + // if (!strcmp(config->bonds[i].name, section)) { + // config_bond = &(config->bonds[i]); + // break; + // } + // } + // printf("%s:%d\n", __FILE__, __LINE__); + // // Build new bond if missing + // if (!config_bond) { + // // Expand array size if needed + // if ((config->bond_len+1) > config->bond_cap) { + // config->bond_cap = MIN(1, config->bond_cap*2); + // config->bonds = realloc(config->bonds, config->bond_cap * sizeof(struct pmlag_configuration_bond)); + // } + // // Append new entry to list + // memset(&(config->bonds[config->bond_len]), 0, sizeof(struct pmlag_configuration_bond)); + // config_bond = &(config->bonds[config->bond_len]); + // config->bond_len++; + // } + + // Detect # comments and remove them + + return 1; } struct pmlag_configuration * config_load(char * filepath, struct pmlag_configuration *config) { // Load config, entry-by-entry - if (!config) config = calloc(1, sizeof(struct pmlag_configuration)); + if (!config) { + config = calloc(1, sizeof(struct pmlag_configuration)); + } + if (!config->bonds) { + config->bonds = mindex_init(mindex_bond_cmp, mindex_bond_purge, NULL); + } + if (ini_parse(filepath, config_load_handler, config) < 0) { fprintf(stderr, "Can not load %s\n", filepath); return NULL; } + + printf("bond_len: %ld\n", mindex_length(config->bonds)); + printf("\n"); + printf("bonds:\n"); + int l = mindex_length(config->bonds); + for(int i = 0 ; i < l ; i++) { + struct pmlag_configuration_bond *bond = mindex_nth(config->bonds, i); + printf(" - %s\n", bond->name); + } + printf("\n"); + return config; } - diff --git a/src/util/config.h b/src/util/config.h @@ -3,39 +3,17 @@ #include <stdint.h> -#define PMLAG_ENTITY_TYPE int -#define PMLAG_ENTITY_TYPE_IFACE 1 -#define PMLAG_ENTITY_TYPE_BOND 2 +#include "finwo/mindex.h" -#define PMLAG_STATE int -#define PMLAG_STATE_TRIGGERED 1 - -struct pmlag_iface { - PMLAG_ENTITY_TYPE type; - char *name; - int ifidx; - struct pmlag_bond *bond; - int sockfd; - struct epoll_event *epev; -}; - -struct pmlag_bond { - PMLAG_ENTITY_TYPE type; - char *name; - struct epoll_event *epev; - PMLAG_STATE state; - int bc_timer; - uint16_t bc_id; - struct pmlag_iface **iface; - int iface_count; - int sockfd; - unsigned char *hwaddr; // hwaddr to use for the interface (null = random) - struct mindex_t *rt; +struct pmlag_configuration_bond { + char *name; + char **interface; + uint16_t interface_count; + uint16_t tick_rate; }; struct pmlag_configuration { - struct pmlag_bond **bond; - int bond_count; + struct mindex_t *bonds; }; struct pmlag_configuration * config_load(char * filepath, struct pmlag_configuration *config); diff --git a/src/util/routing-table.c b/src/util/routing-table.c @@ -1,96 +0,0 @@ -#include <linux/if_ether.h> -/* #include <stdio.h> */ -/* #include <stdlib.h> */ -#include <string.h> - -#include "finwo/mindex.h" - -#include "config.h" -#include "routing-table.h" - -static int rt_compare(const void *a, const void *b, void *udata) { - struct pmlag_rt_entry *ta = (struct pmlag_rt_entry *)a; - struct pmlag_rt_entry *tb = (struct pmlag_rt_entry *)b; - if (a == b) return 0; - return memcmp(ta->mac, tb->mac, ETH_ALEN); -} - -static void rt_purge(void *item, void *udata) { - struct pmlag_rt_entry *rt_entry = (struct pmlag_rt_entry *)item; - if (!rt_entry) return; - if (rt_entry->iface) free(rt_entry->iface); - if (rt_entry->mac) free(rt_entry->mac); - free(rt_entry); -} - -struct mindex_t * rt_init(void *udata) { - return mindex_init(rt_compare, rt_purge, udata); -} - -int rt_upsert( - struct mindex_t *rt, - struct pmlag_iface *iface, - unsigned char *mac, - int16_t bcidx -) { - struct pmlag_rt_entry *rt_entry; - int i, isnew = 0; - int iface_found = 0; - - // Attempt to fetch the rt entry - rt_entry = mindex_get(rt, &((struct pmlag_rt_entry){ .mac = mac })); - - // None given, build new one - if (!rt_entry) { - rt_entry = calloc(1, sizeof(struct pmlag_rt_entry)); - rt_entry->mac = malloc(ETH_ALEN); - rt_entry->bcidx = bcidx; - rt_entry->iface = calloc(iface->bond->iface_count, sizeof(struct pmlag_iface *)); - rt_entry->iface_count = 0; - memcpy(rt_entry->mac, mac, ETH_ALEN); - isnew = 1; - } - - // Clear interface list if bcidx is different - if (rt_entry->bcidx != bcidx) { - rt_entry->iface_count = 0; - rt_entry->bcidx = bcidx; - } - - // Register the interface on the rt_entry - for( i = 0 ; i < rt_entry->iface_count ; i++ ) { - if (rt_entry->iface[i] != iface) continue; - iface_found = 1; - break; - } - if (!iface_found) { - rt_entry->iface[rt_entry->iface_count] = iface; - rt_entry->iface_count++; - } - - // The actual upsert part - if (isnew) { - mindex_set(rt, rt_entry); - // Keep rt size in check - if (rt->length > PMLAG_RT_MAX) { - mindex_delete(rt, mindex_rand(rt)); - } - } - - return 0; -} - -struct pmlag_iface * rt_find( - struct mindex_t *rt, - unsigned char *mac -) { - struct pmlag_rt_entry *rt_entry; - - // Attempt to fetch the rt entry - rt_entry = mindex_get(rt, &((struct pmlag_rt_entry){ .mac = mac })); - if (!rt_entry) return NULL; - - // Unlock the routing table again - struct pmlag_iface *iface = rt_entry->iface[rand() % rt_entry->iface_count]; - return iface; -} diff --git a/src/util/routing-table.h b/src/util/routing-table.h @@ -1,31 +0,0 @@ -#ifndef __PMLAG_UTIL_RT_H__ -#define __PMLAG_UTIL_RT_H__ - -#define PMLAG_RT_MAX 4096 - -#include "config.h" - -#include "finwo/mindex.h" - -struct pmlag_rt_entry { - unsigned char *mac; // mac address of the remote entity - uint16_t bcidx; // broadcast index last seen from the mac - struct pmlag_iface **iface; - int iface_count; -}; - -struct mindex_t * rt_init(void *udata); - -int rt_upsert( - struct mindex_t *rt, - struct pmlag_iface *iface, - unsigned char *mac, - int16_t bcidx -); - -struct pmlag_iface * rt_find( - struct mindex_t *rt, - unsigned char *mac -); - -#endif // __PMLAG_UTIL_RT_H__ diff --git a/src/util/socket.c b/src/util/socket.c @@ -1,156 +0,0 @@ -#include <fcntl.h> -#include <linux/if_arp.h> -#include <linux/if_packet.h> -#include <linux/if_tun.h> -#include <linux/if.h> -#include <netinet/in.h> -#include <string.h> -#include <strings.h> -#include <sys/ioctl.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/socket.h> -#include <unistd.h> - -#include "socket.h" - -unsigned char * iface_mac(char * ifname) { - unsigned char *mac = calloc(1, ETH_ALEN); - struct ifreq ifr; - int sockfd = socket(AF_INET, SOCK_DGRAM, 0); - ifr.ifr_addr.sa_family = AF_INET; - strncpy(ifr.ifr_name , ifname , IFNAMSIZ-1); - ioctl(sockfd, SIOCGIFHWADDR, &ifr); - close(sockfd); - memcpy(mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - return mac; -} - -int iface_idx(int sockfd, char * ifname) { - struct ifreq ifr; - bzero(&ifr, sizeof(ifr)); - - // Get interface index - strncpy((char *)ifr.ifr_name, ifname, IFNAMSIZ); - if ((ioctl(sockfd, SIOCGIFINDEX, &ifr)) == -1) { - fprintf(stderr, "Error getting interface index for %s\n", ifname); - perror("Error getting interface index"); - close(sockfd); - return -1; - } - - return ifr.ifr_ifindex; -} - -int if_ioctl(int cmd, struct ifreq* req) { - int ret, sock; - sock = socket(AF_INET, SOCK_PACKET, 0); - if (sock < 0) { - return -1; - } - ret = ioctl(sock, cmd, req); - close(sock); - return ret; -} - -int sockraw_open(char * ifname) { - struct ifreq ifr; - - // Open socket (P_ALL, due to custom ethertype field) - int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (sockfd < 0) { - perror("Error opening socket"); - close(sockfd); - return -1; - } - - // Old bind to socket - struct sockaddr_ll sll; - bzero(&sll, sizeof(sll)); - sll.sll_family = AF_PACKET; - sll.sll_ifindex = iface_idx(sockfd, ifname); - sll.sll_protocol = htons(ETH_P_ALL); - if (sll.sll_ifindex < 0) { - // Error already printed, not reprinting - return -1; - } - if((bind(sockfd, (struct sockaddr *)&sll, sizeof(sll))) == -1) { - perror("Error binding raw socket to interface"); - close(sockfd); - return -1; - } - - // Get current iface configuration - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, ifname); - if (if_ioctl(SIOCGIFFLAGS, &ifr) < 0) { - perror("Get interface flags"); - } - - // Set iface to promiscuous mode - ifr.ifr_flags |= IFF_PROMISC; - if (if_ioctl(SIOCSIFFLAGS, &ifr) < 0) { - perror("Configure interface promiscuous mode"); - } - - // Return the prepared socket - return sockfd; -} - -int tap_alloc(char * ifname, unsigned char * mac) { - struct ifreq ifr; - int fd, err; - - if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) { - perror("Open tun"); - return -1; - } - - // Bring up the interface - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - if( *ifname ) { - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - } - if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){ - perror("Open tun"); - close(fd); - return err; - } - strcpy(ifname, ifr.ifr_name); - - // Set the interface's mac address - if ( mac ) { - // TODO: random, copied or specific mac address pulled from ini - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, ifname); - ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; - memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ALEN); - if ((err = if_ioctl(SIOCSIFHWADDR, &ifr)) < 0) { - perror("Set if hwaddr"); - close(fd); - return err; - } - } - - // Fetch the current flags - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, ifname); - if (if_ioctl(SIOCGIFFLAGS, &ifr) < 0) { - perror("failed to get the interface flags"); - close(fd); - return err; - } - - // Bring up the interface - if (!(ifr.ifr_flags & IFF_UP)) { - ifr.ifr_flags |= IFF_UP; - if (if_ioctl(SIOCSIFFLAGS, &ifr) < 0) { - perror("failed to get the interface flags"); - close(fd); - return err; - } - } - - return fd; -} diff --git a/src/util/socket.h b/src/util/socket.h @@ -1,9 +0,0 @@ -#ifndef __PMLAG_UTIL_SOCKET_H__ -#define __PMLAG_UTIL_SOCKET_H__ - -int iface_idx(int sockfd, char * ifname); -unsigned char * iface_mac(char * ifname); -int tap_alloc(char * ifname, unsigned char * mac); -int sockraw_open(char * ifname); - -#endif // __PMLAG_UTIL_SOCKET_H__ diff --git a/arch/linux-glibc-amd64/.gitkeep b/target/common/.gitkeep diff --git a/target/common/Makefile b/target/common/Makefile @@ -0,0 +1,41 @@ +TARGET?= +# SVC?= + +# Initial config +# LIBS= +SRC:= +SRC+=$(wildcard src/*.c) +SRC+=$(wildcard src/*/*.c) + +CFLAGS?=-Wall -s -O2 + +INCLUDES:= +INCLUDES+=-I src + +include lib/.dep/config.mk + +CFLAGS+=$(INCLUDES) + +# Which objects to generate before merging everything together +OBJ:=$(SRC:.c=.o) + +BIN=pmlag + +.PHONY: default +default: $(BIN) $(BIN).1 + +# include Makefile.pkg + +%.o: %.c $(LIBS) + $(CC) $(CFLAGS) $(@:.o=.c) -D__NAME=\"$(BIN)\" -c -o $@ + +$(BIN): $(OBJ) + $(CC) $(CFLAGS) $(OBJ) -o $@ + +# Build manpage +$(BIN).1: manpage.1.md + env NAME=$(BIN) envsubst < manpage.1.md | pandoc --standalone --from markdown --to man -o $(BIN).1 + +# # ${BIN}-${SVC}-${TARGET}.tar.gz: $(BIN) +# ${BIN}-${TARGET}.tar.gz: $(BIN) +# package/${SVC}/build.sh diff --git a/arch/linux-musl-amd64/.gitkeep b/target/linux-glibc-amd64/.gitkeep diff --git a/test.ini b/test.ini @@ -1,12 +0,0 @@ -; This is an example configuration, showcasing how you might configure multiple types of bonds - -; eth0 = backup, eth1 = primary -[bond0] -; interface=enp0s13f0u1c2 -; interface=enp0s20f0u3 -interface=virbr1 -interface=virbr2 -interface=virbr2 -hwaddr=random -hwaddr=virbr1 -hwaddr=52:54:00:0B:97:66