pmlag

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

commit 2f6173320ae202a9f2ceb151a9c8b628dae3518d
parent 8293eaaab5b71f4acc6d13028868308c7ceab2bd
Author: Yersa Nordman <yersa@finwo.nl>
Date:   Sun,  8 Oct 2023 22:11:43 +0200

raw sockets opening again; ticking implemented

Diffstat:
Msrc/main.c | 66+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/util/config.c | 18+++++++++++++++---
Msrc/util/config.h | 3+++
Asrc/util/routing-table.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/util/routing-table.h | 31+++++++++++++++++++++++++++++++
Asrc/util/socket.c | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/util/socket.h | 7+++++++
7 files changed, 354 insertions(+), 4 deletions(-)

diff --git a/src/main.c b/src/main.c @@ -1,17 +1,31 @@ +#include <stdint.h> #include <stdio.h> #include <stdlib.h> +#include <sys/epoll.h> +#include <sys/time.h> +#include <unistd.h> #include "cofyc/argparse.h" #include "util/config.h" +#include "util/socket.h" 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); +} + int main(int argc, const char **argv) { char *config_file="/etc/pmlag/pmlag.ini"; + struct epoll_event *epev; + int epfd; + struct pmlag_iface *iface; // Seed random unsigned int seed; @@ -44,15 +58,65 @@ int main(int argc, const char **argv) { return 1; } - // Display bonds + // Initialize epoll + epfd = epoll_create1(0); int b = 0; int i = 0; for( b = 0 ; b < config->bond_count ; b++ ) { + for( i = 0 ; i < config->bond[b]->iface_count ; i++ ) { + iface = config->bond[b]->iface[i]; + + // Start a socket on the interface + iface->sockfd = sockraw_open(iface->name); + if (iface->sockfd < 0) { + return 2; + } + /* printf("%s: %d\n", */ + /* iface->name, */ + /* iface->sockfd */ + /* ); */ + + epev = malloc(sizeof(struct epoll_event)); + if (!epev) return 3; + epev->events = EPOLLIN; + epev->data.ptr = iface; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, iface->sockfd, epev)) { + free(epev); + close(iface->sockfd); + iface->sockfd = 0; + continue; + } + iface->epev = epev; + } + } + + // DEBUG: Display bonds + 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); } } + // 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) { + ev_count = epoll_wait(epfd, events, 8, tdiff); + for( i=0 ; i<ev_count; i++) { + // TODO: process(events[i].data.ptr); + } + + tdiff = ttime - millis(); + if (tdiff <= 0) { + ttime += 1000; + tdiff += 1000; + // TODO: time tick method (for announce handling) + printf("ttime: %ld\n", ttime); + } + } + return 69; } diff --git a/src/util/config.c b/src/util/config.c @@ -1,10 +1,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/epoll.h> #include "benhoyt/inih.h" #include "config.h" +#include "socket.h" static int config_load_handler( void *user, @@ -60,6 +62,7 @@ static int config_load_handler( bond->iface = realloc(bond->iface, (bond->iface_count + 1) * sizeof(void*)); bond->iface[bond->iface_count ] = NULL; iface->name = strdup(value); + iface->bond = bond; } } else if (!strcmp(name, "hwaddr")) { @@ -72,12 +75,21 @@ static int config_load_handler( } } + if (bond->hwaddr) { + printf("Already had MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + bond->hwaddr[0], + bond->hwaddr[1], + bond->hwaddr[2], + bond->hwaddr[3], + bond->hwaddr[4], + bond->hwaddr[5] + ); + } + if (iface) { // Got interface by that name = use it's hwaddr 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); + 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); diff --git a/src/util/config.h b/src/util/config.h @@ -3,6 +3,9 @@ struct pmlag_iface { char *name; + struct pmlag_bond *bond; + int sockfd; + struct epoll_event *epev; /* /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 */ diff --git a/src/util/routing-table.c b/src/util/routing-table.c @@ -0,0 +1,135 @@ +/* #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 @@ -0,0 +1,31 @@ +/* #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 @@ -0,0 +1,98 @@ +#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; +} diff --git a/src/util/socket.h b/src/util/socket.h @@ -0,0 +1,7 @@ +#ifndef __PMLAG_UTIL_SOCKET_H__ +#define __PMLAG_UTIL_SOCKET_H__ + +unsigned char * iface_mac(char * ifname); +int sockraw_open(char * ifname); + +#endif // __PMLAG_UTIL_SOCKET_H__