pmlag

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

commit 8293eaaab5b71f4acc6d13028868308c7ceab2bd
parent e6e4fe3bfec1f4d5793bca8670f9e18cbf6cf231
Author: Yersa Nordman <yersa@finwo.nl>
Date:   Sun,  8 Oct 2023 21:05:03 +0200

Code, basic config loading working again

Diffstat:
MMakefile | 30+-----------------------------
March/common/Makefile | 20--------------------
Msrc/main.c | 47+++++++++++------------------------------------
Dsrc/task/announce.c | 62--------------------------------------------------------------
Dsrc/task/announce.h | 6------
Dsrc/task/bond.c | 146-------------------------------------------------------------------------------
Dsrc/task/bond.h | 7-------
Dsrc/task/iface.c | 96-------------------------------------------------------------------------------
Dsrc/task/iface.h | 7-------
Msrc/util/config.c | 104++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/util/config.h | 73++++++++++++++++++++++++-------------------------------------------------
Dsrc/util/linked-list.h | 6------
Dsrc/util/routing-table.c | 135-------------------------------------------------------------------------------
Dsrc/util/routing-table.h | 31-------------------------------
Dsrc/util/socket.c | 161-------------------------------------------------------------------------------
Dsrc/util/socket.h | 17-----------------
Mtest.ini | 1+
17 files changed, 84 insertions(+), 865 deletions(-)

diff --git a/Makefile b/Makefile @@ -35,41 +35,13 @@ build/${TARGET}: $(wildcard src/*.c src/*.h src/*/*.c src/*/*.h) cp -rT arch/${TARGET}/ build/${TARGET} cp -rT arch/${TARGET}/ build/${TARGET} -build/${TARGET}/pmlag: build/${TARGET} +build/${TARGET}/pmlag: build/${TARGET} $(wildcard build/${TARGET}/src/*.c build/${TARGET}/src/*.h build/${TARGET}/src/*/*.c build/${TARGET}/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 -# include Makefile.pkg - -# %.o: %.c $(LIBS) -# $(CC) $(CFLAGS) $(@:.o=.c) -D__NAME=\"$(BIN)\" -c -o $@ - -# $(BIN): $(OBJ) -# $(CC) $(CFLAGS) $(OBJ) --static -o $@ - -# # Build manpage -# $(BIN).1: manpage.1.md -# env NAME=$(BIN) envsubst < manpage.1.md | pandoc --standalone --from markdown --to man -o $(BIN).1 -# env NAME=$(BIN) envsubst < manpage.1.md | pandoc --standalone --from markdown --to html -o $(BIN).html - -# package: default -# rm -rf package - - -# ### openwrt-amd64 ### -# mkdir -p package/pmlag-openwrt-amd64/etc -# mkdir -p package/pmlag-openwrt-amd64/etc/init.d -# mkdir -p package/pmlag-openwrt-amd64/usr/bin -# mkdir -p package/pmlag-openwrt-amd64/usr/share/man/man1 -# cp pmlag package/pmlag-openwrt-amd64/usr/bin/pmlag -# cp pmlag.1 package/pmlag-openwrt-amd64/usr/share/man/man1/pmlag.1 -# cp service/procd/etc/init.d/pmlag package/pmlag-openwrt-amd64/etc/init.d/pmlag -# cp service/common/etc/pmlag.ini package/pmlag-openwrt-amd64/etc/pmlag.ini -# (cd package ; tar c pmlag-openwrt-amd64 | gzip -9 > pmlag-openwrt-amd64.tar.gz) - .PHONY: clean clean: rm -rf build diff --git a/arch/common/Makefile b/arch/common/Makefile @@ -38,23 +38,3 @@ $(BIN).1: manpage.1.md ${BIN}-${SVC}-${TARGET}.tar.gz: $(BIN) package/${SVC}/build.sh - -# package: default -# rm -rf package -# ### linux-amd64 ### -# mkdir -p package/pmlag-linux-amd64 -# cp pmlag package/pmlag-linux-amd64/pmlag -# cp pmlag.1 package/pmlag-linux-amd64/pmlag.1 -# cp -r service package/pmlag-linux-amd64/service -# cp Makefile.pkg package/pmlag-linux-amd64/Makefile -# (cd package ; tar c pmlag-linux-amd64 | gzip -9 > pmlag-linux-amd64.tar.gz) -# ### openwrt-amd64 ### -# mkdir -p package/pmlag-openwrt-amd64/etc -# mkdir -p package/pmlag-openwrt-amd64/etc/init.d -# mkdir -p package/pmlag-openwrt-amd64/usr/bin -# mkdir -p package/pmlag-openwrt-amd64/usr/share/man/man1 -# cp pmlag package/pmlag-openwrt-amd64/usr/bin/pmlag -# cp pmlag.1 package/pmlag-openwrt-amd64/usr/share/man/man1/pmlag.1 -# cp service/procd/etc/init.d/pmlag package/pmlag-openwrt-amd64/etc/init.d/pmlag -# cp service/common/etc/pmlag.ini package/pmlag-openwrt-amd64/etc/pmlag.ini -# (cd package ; tar c pmlag-openwrt-amd64 | gzip -9 > pmlag-openwrt-amd64.tar.gz) diff --git a/src/main.c b/src/main.c @@ -1,24 +1,9 @@ -// vim: fdm=marker - -#include <arpa/inet.h> -#include <errno.h> -#include <linux/if_ether.h> -#include <linux/if_packet.h> -#include <net/if.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <pthread.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/socket.h> #include <stdio.h> -#include <unistd.h> +#include <stdlib.h> #include "cofyc/argparse.h" -#include "util/config.h" -#include "task/bond.h" +#include "util/config.h" static const char *const usage[] = { __NAME " [options]", @@ -54,30 +39,20 @@ int main(int argc, const char **argv) { argc = argparse_parse(&argparse, argc, argv); // Load configuration file - struct pmlag_configuration *config = config_load(config_file); + struct pmlag_configuration *config = config_load(config_file, NULL); if (!config) { return 1; } - // Initialize interfaces for all configured bonds - struct pmlag_bond *bond = config->bonds; - while(bond) { - - // Start the bond's thread - if(pthread_create(&(bond->tid_bond), NULL, task_bond_thread, bond)) { - perror("Starting bond thread"); - return 1; + // 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); } - - bond = bond->next; - } - - // Wait for all bonds to finish - bond = config->bonds; - while(bond) { - pthread_join(bond->tid_bond, NULL); - bond = bond->next; } - return 0; + return 69; } diff --git a/src/task/announce.c b/src/task/announce.c @@ -1,62 +0,0 @@ -#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 <unistd.h> - -#include "../util/config.h" -#include "../util/socket.h" -#include "announce.h" - -void * task_announce_thread(void *arg) { - struct pmlag_bond *bond = arg; - struct sockaddr_ll saddr_ll; - saddr_ll.sll_halen = ETH_ALEN; - pmlag_iface_llist *iface_entry; - - uint16_t ethtype = htons(0x0666); - size_t buflen = (ETH_ALEN*2) + sizeof(ethtype) + 46; - size_t sendlen; - - unsigned char *mac = iface_mac(bond->name); - unsigned char *buffer = calloc(1, buflen); - - // Prepare saddr_ll and buffer - memcpy(saddr_ll.sll_addr, mac, ETH_ALEN); - memset(buffer, 0xFF, ETH_ALEN); // Destination = Broadcast - memcpy(buffer+ETH_ALEN, mac, ETH_ALEN); // Source = bond - memcpy(buffer+(ETH_ALEN*2), &ethtype, sizeof(uint16_t)); // EtherType = 0x0666 = custom - - int16_t bcidx = htons(rand() | 1); - - while(1) { - sleep(1); - iface_entry = bond->interfaces; - - // Increment bcidx - bcidx = ntohs(bcidx); - bcidx++; - if (!bcidx) bcidx = 1; - bcidx = htons(bcidx); - - // Store bcidx on buffer - memcpy(buffer + (ETH_ALEN*2) + sizeof(uint16_t), &bcidx, sizeof(int16_t)); - - // Send the buffer as-is over all ifaces - while(iface_entry) { - if (iface_entry->data->sockfd) { - saddr_ll.sll_ifindex = iface_entry->data->ifidx; - sendlen = sendto(iface_entry->data->sockfd, buffer, buflen, 0, (const struct sockaddr*)&saddr_ll, sizeof(struct sockaddr_ll)); - if (sendlen < 0) { - perror("SENDTO"); - } - } - iface_entry = iface_entry->next; - } - } - - return NULL; -} diff --git a/src/task/announce.h b/src/task/announce.h @@ -1,6 +0,0 @@ -#ifndef __PMLAG_TASK_ANNOUNCE_H__ -#define __PMLAG_TASK_ANNOUNCE_H__ - -void * task_announce_thread(void *arg); - -#endif // __PMLAG_TASK_ANNOUNCE_H__ diff --git a/src/task/bond.c b/src/task/bond.c @@ -1,146 +0,0 @@ -#include <linux/if_ether.h> -#include <linux/if_packet.h> -#include <pthread.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> -#include <unistd.h> - -#include "finwo/mindex.h" -#include "../util/config.h" -#include "../util/routing-table.h" -#include "../util/socket.h" - -#include "announce.h" -#include "iface.h" - -int task_bond_onpacket(struct pmlag_bond *bond, unsigned char *buffer, size_t buflen) { - -#ifdef DEBUG - // Debug: print eth header - printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x > %.2x:%.2x:%.2x:%.2x:%.2x:%.2x, %.4x (%ld)\n", - buffer[6],buffer[7],buffer[8],buffer[9],buffer[10],buffer[11], // SRC - buffer[0],buffer[1],buffer[2],buffer[3],buffer[ 4],buffer[ 5], // DST - ((unsigned int)((unsigned char)buffer[12]) << 8) + buffer[13], // PROTO - buflen - ); -#endif // DEBUG - - // Get interface to send the packet from - struct pmlag_iface *iface = (memcmp(buffer, "\xFF\xFF\xFF\xFF\xFF\xFF", ETH_ALEN) == 0) - ? NULL - : rt_find(bond->rt, &(bond->mtx_rt), buffer); - pmlag_iface_llist *iface_entry; - size_t send_len; - -#ifdef DEBUG - printf(" got iface? %s\n", iface ? "yes" : "no"); -#endif - - // Broadcast on ALL interfaces if no rt entry OR broadcast packet - if (!iface) { - iface_entry = bond->interfaces; - while(iface_entry) { - send_len = sendto(iface_entry->data->sockfd, buffer, buflen, 0, NULL, 0); - if(send_len != buflen) { - perror("sendto"); - return 1; - } - iface_entry = iface_entry->next; - } - return 0; - } - - // Forward packet to iface as-is - send_len = sendto(iface->sockfd, buffer, buflen, 0, NULL, 0); - if(send_len != buflen) { - perror("sendto"); - return 1; - } - - return 0; -} - -// Responsible for init + calling task_bond_onpacket -void * task_bond_thread(void *arg) { - struct pmlag_bond *bond = arg; - - // Initialize routing table lock - if (pthread_mutex_init(&(bond->mtx_rt), NULL) != 0) { - perror("Initializing mutex for bond"); - pthread_exit(NULL); - return NULL; - } - - // Initialize routing table - bond->rt = rt_init(bond); - - // Assign bond interface - bond->sockfd = tap_alloc(bond->name, bond->hwaddr); - if (bond->sockfd < 0) { - perror("Allocating bond interface"); - pthread_exit(NULL); - return NULL; - }; - - // Lock this bond's routing table - pthread_mutex_lock(&(bond->mtx_rt)); - - // Start thread for each interface of this bond - pmlag_iface_llist *iface_entry = bond->interfaces; - while(iface_entry) { - if(pthread_create(&(iface_entry->data->tid), NULL, task_iface_thread, iface_entry->data)) { - perror("Starting iface thread"); - pthread_exit((void*)1); - return (void*)1; - } - iface_entry = iface_entry->next; - } - - // Start an announcement thread - if(pthread_create(&(bond->tid_announce), NULL, task_announce_thread, bond)) { - perror("Starting announce thread"); - pthread_exit((void*)1); - return (void*)1; - } - - // Free this bond's routing table - pthread_mutex_unlock(&(bond->mtx_rt)); - - // Predefine things we'll re-use - size_t buflen; - unsigned char *buffer = (unsigned char *) malloc(RCVBUFSIZ); - - // TODO: replace by waiting for the iface to finish initializing - sleep(1); - - // This is our life now - while(1) { - - // Wait for a packet to arrive - buflen = read(bond->sockfd, buffer, RCVBUFSIZ); - if (buflen < 0) { - perror("read(bond)"); - pthread_exit(NULL); - return NULL; - } - - // And let the more specific method handle the packet - if(task_bond_onpacket(bond, buffer, buflen)) { - pthread_exit(NULL); - return NULL; - } - - } - - // Wait for iface threads to finish - iface_entry = bond->interfaces; - while(iface_entry) { - pthread_join(iface_entry->data->tid, NULL); - iface_entry = iface_entry->next; - } - - pthread_exit(NULL); - return NULL; -} diff --git a/src/task/bond.h b/src/task/bond.h @@ -1,7 +0,0 @@ -#ifndef __PMLAG_TASK_BOND_H__ -#define __PMLAG_TASK_BOND_H__ - -void * task_bond_thread(void *arg); - -#endif // __PMLAG_TASK_BOND_H__ - diff --git a/src/task/iface.c b/src/task/iface.c @@ -1,96 +0,0 @@ -#include <linux/if_ether.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <unistd.h> - -#include "../util/config.h" -#include "../util/routing-table.h" -#include "../util/socket.h" - -int task_iface_onpacket(struct pmlag_iface *iface, unsigned char *buffer, size_t buflen) { - size_t send_len; - uint16_t proto; - int16_t bcidx; - -#ifdef DEBUG - // Debug: print eth header - printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x < %.2x:%.2x:%.2x:%.2x:%.2x:%.2x, %.4x (%ld)\n", - buffer[0],buffer[1],buffer[2],buffer[3],buffer[ 4],buffer[ 5], // DST - buffer[6],buffer[7],buffer[8],buffer[9],buffer[10],buffer[11], // SRC - ((unsigned int)((unsigned char)buffer[12]) << 8) + buffer[13], // PROTO - buflen - ); -#endif // DEBUG - - // Update the routing table as-needed - proto = ((uint16_t)((unsigned char)buffer[(ETH_ALEN*2)+0]) << 8) + buffer[(ETH_ALEN*2)+1]; - bcidx = 0; - if (proto == 0x0666) { - bcidx = ((int16_t)((unsigned char)buffer[(ETH_ALEN*2)+2]) << 8) + buffer[(ETH_ALEN*2)+3]; - } - rt_upsert( - iface->bond->rt, - &(iface->bond->mtx_rt), - iface, - buffer+ETH_ALEN, - bcidx - ); - if (proto == 0x0666) { - return 0; - } - - // Redirect packet to bond socket as-is - send_len = write(iface->bond->sockfd, buffer, buflen); - if (buflen != send_len) { - perror("write(bond)"); - return 1; - } - - return 0; -} - -void * task_iface_thread(void *arg) { - struct pmlag_iface *iface = arg; - - // Open socket for the interface in the bond - iface->sockfd = sockraw_open(iface->name); - if (iface->sockfd < 0) { - pthread_exit(NULL); - return NULL; - } - - // Get the interface's idx on the socket - iface->ifidx = iface_idx(iface->sockfd, iface->name); - - // Reserve receive buffer, support 64k packets just in case - size_t buflen; - unsigned char *buffer = (unsigned char *) malloc(RCVBUFSIZ); - - // Wait for the bond thread to finish initializing - pthread_mutex_lock(&(iface->bond->mtx_rt)); - pthread_mutex_unlock(&(iface->bond->mtx_rt)); - - printf("Thread started for iface %s\n", iface->name); - - // This is our life now - while(1) { - - // Zero out buffer, to prevent pollution, & receive packet - buflen = recvfrom(iface->sockfd, buffer, RCVBUFSIZ, 0, NULL, 0); - if (buflen < 0) { - perror("recvfrom"); - pthread_exit(NULL); - return NULL; - } - - // And let the more specific method handle the packet - if(task_iface_onpacket(iface, buffer, buflen)) { - pthread_exit(NULL); - return NULL; - } - } - - return NULL; -} diff --git a/src/task/iface.h b/src/task/iface.h @@ -1,7 +0,0 @@ -#ifndef __PMLAG_TASK_IFACE_H__ -#define __PMLAG_TASK_IFACE_H__ - -void * task_iface_thread(void *arg); - -#endif // __PMLAG_TASK_IFACE_H__ - diff --git a/src/util/config.c b/src/util/config.c @@ -1,11 +1,10 @@ -#include <linux/if_ether.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "benhoyt/inih.h" + #include "config.h" -#include "socket.h" static int config_load_handler( void *user, @@ -13,85 +12,79 @@ static int config_load_handler( const char *name, const char *value ) { + struct pmlag_bond *bond = NULL; + struct pmlag_iface *iface = NULL; + int bond_idx = 0; + int iface_idx = 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*)); - // Get bond being configured - struct pmlag_bond *bond = config->bonds; - while(bond) { - if (!strcmp(bond->name, section)) { + // Find the bond being configured + for( bond_idx = 0 ; bond_idx < config->bond_count ; bond_idx ++ ) { + if (!strcmp(config->bond[bond_idx]->name, section)) { + bond = config->bond[bond_idx]; break; } - bond = bond->next; } // Create bond if not found if (!bond) { - bond = calloc(1, sizeof(struct pmlag_bond)); - bond->next = config->bonds; - bond->name = strdup(section); - config->bonds = 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->name = strdup(section); } - // Get interface being configured - pmlag_iface_llist *iface_entry = bond->interfaces; - if (0) { // Intentionally empty - } else if (!strcmp(name, "mode")) { - // Set the mode according to found value - if (0) { - // Intentionally empty - } else if (!strcmp(value, "active-backup")) { - bond->mode = PMLAG_MODE_ACTIVE_BACKUP; - } else if (!strcmp(value, "balanced-rr")) { - bond->mode = PMLAG_MODE_BALANCED_RR; - } else if (!strcmp(value, "broadcast")) { - bond->mode = PMLAG_MODE_BROADCAST; - } else { - return 0; - } } else if (!strcmp(name, "interface")) { - // Select the right interface - while(iface_entry) { - if (!strcmp(iface_entry->data->name, value)) { + // Ensure we have an iface list + if (!bond->iface) bond->iface = calloc(1, sizeof(void*)); + + // Find the interface you're referencing + for( iface_idx = 0 ; iface_idx < bond->iface_count ; iface_idx ++ ) { + if (!strcmp(config->bond[bond_idx]->iface[iface_idx]->name, value)) { + iface = config->bond[bond_idx]->iface[iface_idx]; break; } - iface_entry = iface_entry->next; } - // Create iface if not found - if (!iface_entry) { - // Create the list entry - iface_entry = calloc(1, sizeof(pmlag_iface_llist)); - iface_entry->next = bond->interfaces; - bond->interfaces = iface_entry; - // Create the iface - iface_entry->data = calloc(1, sizeof(struct pmlag_iface)); - iface_entry->data->name = strdup(value); - iface_entry->data->bond = bond; - bond->iface_cnt++; + // 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->name = strdup(value); } } else if (!strcmp(name, "hwaddr")) { - // Find iface with name == value - iface_entry = bond->interfaces; - while(iface_entry) { - if (!strcmp(iface_entry->data->name, value)) break; - iface_entry = iface_entry->next; + // Find the interface you're referencing + for( iface_idx = 0 ; iface_idx < bond->iface_count ; iface_idx ++ ) { + if (!strcmp(config->bond[bond_idx]->iface[iface_idx]->name, value)) { + iface = config->bond[bond_idx]->iface[iface_idx]; + break; + } } - if (iface_entry) { + if (iface) { // Got interface by that name = use it's hwaddr - bond->hwaddr = iface_mac(iface_entry->data->name); + if (bond->hwaddr) free(bond->hwaddr); + bond->hwaddr = NULL; + /* bond->hwaddr = iface_mac(iface_entry->data->name); */ + printf("TODO: get mac from %s\n", 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]), @@ -102,21 +95,18 @@ static int config_load_handler( &(bond->hwaddr[5]) ); } - - } else { - // Unknown key - return 0; } return 1; } -struct pmlag_configuration * config_load(const char * filename) { +struct pmlag_configuration * config_load(char * filepath, struct pmlag_configuration *config) { // Load config, entry-by-entry - struct pmlag_configuration *config = calloc(1, sizeof(struct pmlag_configuration)); - if (ini_parse(filename, config_load_handler, config) < 0) { - fprintf(stderr, "Can not load %s\n", filename); + if (!config) config = calloc(1, sizeof(struct pmlag_configuration)); + if (ini_parse(filepath, config_load_handler, config) < 0) { + fprintf(stderr, "Can not load %s\n", filepath); return NULL; } return config; } + diff --git a/src/util/config.h b/src/util/config.h @@ -1,61 +1,36 @@ -#ifndef __PMLAG_CONFIG_H__ -#define __PMLAG_CONFIG_H__ - -#include <pthread.h> -#include <stdint.h> - -#include "finwo/mindex.h" - -#include "linked-list.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define PMLAG_MODE_NONE 0 -#define PMLAG_MODE_ACTIVE_BACKUP 1 -#define PMLAG_MODE_BROADCAST 2 -#define PMLAG_MODE_BALANCED_RR 3 - -#define RCVBUFSIZ 65536 - -#ifndef RT_MAX_ENTRIES -#define RT_MAX_ENTRIES 64 -#endif - -typedef LLIST(struct pmlag_iface) pmlag_iface_llist; +#ifndef __PMLAG_UTIL_CONFIG_H__ +#define __PMLAG_UTIL_CONFIG_H__ struct pmlag_iface { - char *name; // name of the interface this object represents - /* int weight; // weight of this interface within the bond */ - int sockfd; // file descriptor for the iface raw socket - int ifidx; // index of the interface within the socket - pthread_t tid; // thread id where the iface listener recides in - struct pmlag_bond *bond; // reference to the bond this iface belongs to + char *name; + /* /1* int weight; // weight of this interface within the bond *1/ */ + /* int sockfd; // file descriptor for the iface raw socket */ + /* int ifidx; // index of the interface within the socket */ + /* pthread_t tid; // thread id where the iface listener recides in */ + /* struct pmlag_bond *bond; // reference to the bond this iface belongs to */ }; struct pmlag_bond { - void *next; // linked-list next reference - char *name; // name of the bond interface - int mode; // which mode to run pmlag in for this bond - int sockfd; // file descriptor for the bond socket interface + char *name; + struct pmlag_iface **iface; + int iface_count; unsigned char *hwaddr; // hwaddr to use for the interface (null = random) - pthread_t tid_bond; // thread id where the bond interface listener recides in - pthread_t tid_announce; // thread id that'll output announces for the bond - pthread_mutex_t mtx_rt; // lock for the routing table of the bond - struct mindex_t *rt; // pointer to the routing table - pmlag_iface_llist *interfaces; // linked-list of interfaces contained in the bond - int iface_cnt; // track the number of interfaces in this bond + +/* int mode; // which mode to run pmlag in for this bond */ +/* int sockfd; // file descriptor for the bond socket interface */ +/* pthread_t tid_bond; // thread id where the bond interface listener recides in */ +/* pthread_t tid_announce; // thread id that'll output announces for the bond */ +/* pthread_mutex_t mtx_rt; // lock for the routing table of the bond */ +/* struct mindex_t *rt; // pointer to the routing table */ +/* pmlag_iface_llist *interfaces; // linked-list of interfaces contained in the bond */ +/* int iface_cnt; // track the number of interfaces in this bond */ }; struct pmlag_configuration { - struct pmlag_bond *bonds; + struct pmlag_bond **bond; + int bond_count; }; -struct pmlag_configuration * config_load(const char * filename); - -#ifdef __cplusplus -} // extern "C" -#endif +struct pmlag_configuration * config_load(char * filepath, struct pmlag_configuration *config); -#endif // __PMLAG_CONFIG_H__ +#endif // __PMLAG_UTIL_CONFIG_H__ diff --git a/src/util/linked-list.h b/src/util/linked-list.h @@ -1,6 +0,0 @@ -#ifndef __PMLAG_UTIL_LL_H__ -#define __PMLAG_UTIL_LL_H__ - -#define LLIST(x) struct { void *next; x *data; } - -#endif // __PMLAG_UTIL_LL_H__ diff --git a/src/util/routing-table.c b/src/util/routing-table.c @@ -1,135 +0,0 @@ -#include <linux/if_ether.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "config.h" -#include "finwo/mindex.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; - int result; - - // Same pointer = match - if (a == b) return 0; - - // Check for ETH_ALEN, unrolled memcmp - if (( result = ((int)ta->mac[0] - (int)tb->mac[0]) )) return result; - if (( result = ((int)ta->mac[1] - (int)tb->mac[1]) )) return result; - if (( result = ((int)ta->mac[2] - (int)tb->mac[2]) )) return result; - if (( result = ((int)ta->mac[3] - (int)tb->mac[3]) )) return result; - if (( result = ((int)ta->mac[4] - (int)tb->mac[4]) )) return result; - return (int)ta->mac[5] - (int)tb->mac[5]; -} - -static void rt_purge(const void *item, void *udata) { - struct pmlag_rt_entry *rt_entry = (struct pmlag_rt_entry *)item; - free(rt_entry->interfaces); - 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, - pthread_mutex_t *mtx, - struct pmlag_iface *iface, - unsigned char *mac, - int16_t bcidx -) { - int isnew = 0; - struct pmlag_rt_entry *rt_entry; - - // Lock the routing table - pthread_mutex_lock(mtx); - - // 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 = 0; - rt_entry->interfaces = calloc(iface->bond->iface_cnt, sizeof(struct pmlag_iface *)); - memcpy(rt_entry->mac, mac, ETH_ALEN); - isnew = 1; - } - - // Bail if - if ( - (!bcidx && rt_entry->bcidx) || // We receive a regular packet on bcidx-tracked entry - (rt_entry->bcidx && ((bcidx - rt_entry->bcidx) < 0)) // Or the received bcidx is lower than known (old packet) - ) { - if (isnew) { - rt_purge(rt_entry, NULL); - } - pthread_mutex_unlock(mtx); - return 0; - } - - // Clear list of known interfaces if - if ( - (bcidx && (rt_entry->bcidx != bcidx)) || // We got a NEW broadcast index - (!bcidx && (rt_entry->bcidx == 0)) // Or we're updating a non-pmlag remote - ) { - rt_entry->iface_cnt = 0; - } - - // Update the rt_entry's broadcast index - rt_entry->bcidx = bcidx; - - // Bail if rt_entry->iface_cnt+1 lg bond->iface_cnt - if ((rt_entry->iface_cnt + 1) > iface->bond->iface_cnt) { - if (isnew) { - rt_purge(rt_entry, NULL); - } - pthread_mutex_unlock(mtx); - return 0; - } - - // Add our iface to the entry's interface list - rt_entry->interfaces[rt_entry->iface_cnt] = iface; - rt_entry->iface_cnt++; - - // Ensure the entry is in the rt - if (isnew) { - mindex_set(rt, rt_entry); - } - - if (mindex_length(rt) > RT_MAX_ENTRIES) { - rt_entry = mindex_rand(rt); - if (rt_entry) { - mindex_delete(rt, rt_entry); - } - } - - pthread_mutex_unlock(mtx); - return 0; -} - -struct pmlag_iface * rt_find( - struct mindex_t *rt, - pthread_mutex_t *mtx, - unsigned char *mac -) { - struct pmlag_rt_entry *rt_entry; - - // Attempt to fetch the rt entry - pthread_mutex_lock(mtx); - rt_entry = mindex_get(rt, &((struct pmlag_rt_entry){ .mac = mac })); - if (!rt_entry) { - pthread_mutex_unlock(mtx); - return NULL; - } - - // Unlock the routing table again - struct pmlag_iface *iface = rt_entry->interfaces[rand() % rt_entry->iface_cnt]; - pthread_mutex_unlock(mtx); - 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__ - -#include "config.h" - -#include "finwo/mindex.h" - -struct pmlag_rt_entry { - unsigned char *mac; // mac address of the remote entity - int16_t bcidx; // broadcast index last seen from the mac - int16_t iface_cnt; // amount of interfaces this mac is available on - struct pmlag_iface **interfaces; // list of pointers to interfaces -}; - -struct mindex_t * rt_init(void *udata); - -int rt_upsert( - struct mindex_t *rt, - pthread_mutex_t *mtx, - struct pmlag_iface *iface, - unsigned char *mac, - int16_t bcidx -); - -struct pmlag_iface * rt_find( - struct mindex_t *rt, - pthread_mutex_t *mtx, - unsigned char *mac -); - -#endif // __PMLAG_UTIL_RT_H__ diff --git a/src/util/socket.c b/src/util/socket.c @@ -1,161 +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) { - struct ifreq ifr; - unsigned char *mac = calloc(1, ETH_ALEN); - int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - ifr.ifr_addr.sa_family = AF_PACKET; - 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; - } - - /* // Bind socket to interface */ - /* if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0) { */ - /* perror("Error binding raw socket to interface"); */ - /* } */ - - // 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_MULTI_QUEUE | 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,17 +0,0 @@ -#ifndef __PMLAG_SOCKET_H__ -#define __PMLAG_SOCKET_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -unsigned char * iface_mac(char * ifname); -int tap_alloc(char * ifname, unsigned char * mac); -int sockraw_open(char * ifname); -int iface_idx(int sockfd, char * ifname); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // __PMLAG_SOCKET_H__ diff --git a/test.ini b/test.ini @@ -6,6 +6,7 @@ ; interface=enp0s20f0u3 interface=virbr1 interface=virbr2 +interface=virbr2 hwaddr=random hwaddr=virbr1 hwaddr=52:54:00:0B:97:66