pmlag

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

commit 4bb71722241bba47b8369dbb8dcb585c87f3b577
parent f458e825654f7e7f4fdeaa1af0c5985c34c3b55c
Author: Robin Bron <robin@finwo.nl>
Date:   Mon, 27 Feb 2023 22:22:37 +0100

Track src mac addresses in routing table again

Diffstat:
Msrc/main.c | 93+------------------------------------------------------------------------------
Msrc/task/iface.c | 34+++++++++++++++++++++-------------
Msrc/util/config.h | 2+-
Msrc/util/routing-table.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/util/routing-table.h | 10+++++++++-
Mtest.ini | 4++--
6 files changed, 102 insertions(+), 109 deletions(-)

diff --git a/src/main.c b/src/main.c @@ -27,97 +27,6 @@ static const char *const usage[] = { NULL }; -/* void trim_rt(struct pmlag_bond *bond, size_t limit) { */ - -/* } */ - -/* void iface_add_rt(struct pmlag_iface *iface, unsigned char *mac, uint16_t bcidx) { */ -/* struct pmlag_rt_entry *rt_entry; */ -/* int iface_list_len; */ -/* struct pmlag_iface *iface_list_entry; */ - -/* pthread_mutex_lock(&(iface->bond->mtx_rt)); */ -/* printf("\nRCV bc: %s\n", iface->name); */ - -/* // Fetch or build rt entry */ -/* rt_entry = btree_get(iface->bond->rt, &(struct pmlag_rt_entry){ .mac = mac }); */ -/* if (!rt_entry) { */ -/* /1* printf(" new mac\n"); *1/ */ -/* // Build new entry */ -/* rt_entry = calloc(1, sizeof(struct pmlag_rt_entry)); */ -/* /1* bzero(rt_entry, sizeof(struct pmlag_rt_entry)); *1/ */ -/* /1* rt_entry->bcidx = 0; *1/ */ - -/* // Insert the mac address */ -/* rt_entry->mac = malloc(ETH_ALEN); */ -/* memcpy(rt_entry->mac, mac, ETH_ALEN); */ - -/* /1* // And an empty list of interfaces *1/ */ -/* /1* rt_entry->interfaces = NULL; *1/ */ -/* } else { */ -/* /1* printf(" known mac\n"); *1/ */ -/* } */ - -/* // Bail if we receive a regular packet on an interface with bcidx */ -/* if (!bcidx && rt_entry->bcidx) { */ -/* /1* printf(" bail, regular packet on pmlag remote\n"); *1/ */ -/* pthread_mutex_unlock(&(iface->bond->mtx_rt)); */ -/* return; */ -/* } */ - -/* // Clear list of known interfaces if */ -/* pmlag_iface_llist *iface_entry; */ -/* 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 */ -/* ) { */ -/* // Free list 1-by-1 */ -/* while(rt_entry->interfaces) { */ -/* printf("FREE LINE %d (%p)\n", __LINE__, iface_entry); */ -/* printf("FREE LINE %d (%p)\n", __LINE__, rt_entry->interfaces); */ -/* iface_entry = rt_entry->interfaces; */ -/* printf("FREE LINE %d (%p)\n", __LINE__, iface_entry); */ -/* printf("FREE LINE %d (%p)\n", __LINE__, iface_entry->next); */ -/* rt_entry->interfaces = iface_entry->next; */ -/* printf("FREE LINE %d (%p)\n", __LINE__, iface_entry); */ -/* free(iface_entry); */ -/* } */ -/* rt_entry->bcidx = bcidx; */ -/* /1* printf(" clear interface list\n"); *1/ */ -/* } else { */ -/* // Don't track the interface */ -/* /1* printf(" keep interface list\n"); *1/ */ -/* } */ - -/* // Add given interface to the entry's interface list */ -/* iface_entry = calloc(1, sizeof(pmlag_iface_llist)); */ -/* iface_entry->next = rt_entry->interfaces; */ -/* iface_entry->data = iface; */ -/* rt_entry->interfaces = iface_entry; */ - -/* /1* iface_list_len = 0; *1/ */ -/* /1* iface_list_entry = rt_entry->interfaces[iface_list_len]; *1/ */ -/* /1* while(iface_list_entry) { *1/ */ -/* /1* iface_list_entry = rt_entry->interfaces[++iface_list_len]; *1/ */ -/* /1* } *1/ */ - -/* /1* /2* printf(" len before: %d\n", iface_list_len); *2/ *1/ */ -/* /1* rt_entry->interfaces = realloc(rt_entry->interfaces, (iface_list_len+2) * sizeof(struct pm_rt_entry *)); *1/ */ -/* /1* /2* printf(" reallocated\n"); *2/ *1/ */ -/* /1* rt_entry->interfaces[iface_list_len ] = iface; *1/ */ -/* /1* rt_entry->interfaces[iface_list_len+1] = NULL; *1/ */ -/* /1* /2* printf(" len after: %d\n", iface_list_len+1); *2/ *1/ */ - -/* // Save rt entry in the routing table again */ -/* btree_set(iface->bond->rt, rt_entry); */ -/* /1* printf(" saved to tree\n"); *1/ */ -/* /1* printf("\n"); *1/ */ - -/* pthread_mutex_unlock(&(iface->bond->mtx_rt)); */ -/* } */ - - - int main(int argc, const char **argv) { char *config_file="/etc/pmlag/pmlag.ini"; @@ -179,7 +88,7 @@ int main(int argc, const char **argv) { /* printf("FREE LINE %d (%p)\n", __LINE__, mac); */ /* free(mac); */ /* // Set the bcidx */ - /* memcpy(buffer + (ETH_ALEN*2) + sizeof(uint16_t), &(bond->bcidx), sizeof(uint16_t)); */ + /* memcpy(buffer + (ETH_ALEN*2) + sizeof(uint16_t), &(bond->bcidx), sizeof(int16_t)); */ /* /1* printf("Ethernet header\n"); *1/ */ /* /1* printf("\nSending: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x > %.2x:%.2x:%.2x:%.2x:%.2x:%.2x, %.4x (%d)\n\n", *1/ */ diff --git a/src/task/iface.c b/src/task/iface.c @@ -1,3 +1,4 @@ +#include <linux/if_ether.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -5,10 +6,13 @@ #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; // Debug: print eth header printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x < %.2x:%.2x:%.2x:%.2x:%.2x:%.2x, %.4x (%ld)\n", @@ -18,6 +22,23 @@ int task_iface_onpacket(struct pmlag_iface *iface, unsigned char *buffer, size_t buflen ); + // 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) { @@ -76,18 +97,5 @@ void * task_iface_thread(void *arg) { } -/* // Update routing table if our custom protocol is seen */ -/* proto = ((uint16_t)((unsigned char)buffer[(ETH_ALEN*2)+0]) << 8) + buffer[(ETH_ALEN*2)+1]; */ -/* if (proto == 0x0666) { */ -/* bcidx = ((uint16_t)((unsigned char)buffer[(ETH_ALEN*2)+2]) << 8) + buffer[(ETH_ALEN*2)+3]; */ -/* iface_add_rt(iface, buffer+ETH_ALEN, bcidx); */ -/* continue; */ -/* } else { */ -/* iface_add_rt(iface, buffer+ETH_ALEN, 0); */ -/* } */ - -/* /1* trim_rt(iface->bond, *1/ */ -/* rt_len = btree_count(iface->bond->rt); */ -/* printf("Current RT length: %ld\n", rt_len); */ diff --git a/src/util/config.h b/src/util/config.h @@ -35,7 +35,7 @@ struct pmlag_bond { 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 - uint16_t bcidx; // big-endian broadcast index for quickly detecting dead paths + int16_t bcidx; // big-endian broadcast index for quickly detecting dead paths pthread_t tid; // thread id where the bond interface listener recides in pthread_mutex_t mtx_rt; // lock for the routing table of the bond struct btree *rt; // pointer to the routing table diff --git a/src/util/routing-table.c b/src/util/routing-table.c @@ -1,6 +1,8 @@ #include <linux/if_ether.h> +#include <stdlib.h> #include <string.h> +#include "config.h" #include "tidwall/btree.h" #include "routing-table.h" @@ -13,3 +15,69 @@ static int compare_rt_entries(const void *a, const void *b, void *udata) { struct btree * rt_init(void *udata) { return btree_new(sizeof(void*), 0, compare_rt_entries, udata); } + +int rt_upsert( + struct btree *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 = btree_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); + 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 + ((bcidx - rt_entry->bcidx) < 0) // Or the received bcidx is lower than known (old packet) + ) { + pthread_mutex_unlock(&(iface->bond->mtx_rt)); + return 0; + } + + // Clear list of known interfaces if + pmlag_iface_llist *iface_entry; + 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 + ) { + // Free list 1-by-1 + while(rt_entry->interfaces) { + iface_entry = rt_entry->interfaces; + rt_entry->interfaces = iface_entry->next; + free(iface_entry); + } + } + + // Update the rt_entry's broadcast index + rt_entry->bcidx = bcidx; + + // Add our iface to the entry's interface list + iface_entry = malloc(sizeof(pmlag_iface_llist)); + iface_entry->next = rt_entry->interfaces; + iface_entry->data = iface; + rt_entry->interfaces = iface_entry; + + // Save rt entry in the routing table if it's new + // A pre-existing one is already stored (btree holds pointers, no need to re-save) + if (isnew) { + btree_set(iface->bond->rt, rt_entry); + } + + pthread_mutex_unlock(mtx); + return 0; +} diff --git a/src/util/routing-table.h b/src/util/routing-table.h @@ -5,10 +5,18 @@ struct pmlag_rt_entry { unsigned char *mac; // mac address of the remote entity - uint16_t bcidx; // broadcast index last seen from the mac + int16_t bcidx; // broadcast index last seen from the mac pmlag_iface_llist *interfaces; // list of pointers to interfaces }; struct btree * rt_init(void *udata); +int rt_upsert( + struct btree *rt, + pthread_mutex_t *mtx, + struct pmlag_iface *iface, + unsigned char *mac, + int16_t bcidx +); + #endif // __PMLAG_UTIL_RT_H__ diff --git a/test.ini b/test.ini @@ -3,8 +3,8 @@ ; eth0 = backup, eth1 = primary [bond0] interface=virbr1 -interface=virbr2 -; interface=wlp170s0 +; interface=virbr2 +; interface=virbr6 ; interface=docker0 ; ; broadcast everything on all interfaces