pmlag

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

commit 0f4d21da3457854788aec6937e6cfdf0aaf9a00a
parent 2cb07fe9501dea415aad7bfd6facbdc27585672a
Author: Robin Bron <robin@finwo.nl>
Date:   Fri, 17 Feb 2023 00:22:55 +0100

Data to-and-fro single interface

Diffstat:
Msrc/config.h | 1+
Msrc/main.c | 369+++++++++++++++++++++++++------------------------------------------------------
Msrc/socket.c | 8++++----
Mtest.ini | 4+++-
4 files changed, 123 insertions(+), 259 deletions(-)

diff --git a/src/config.h b/src/config.h @@ -17,6 +17,7 @@ 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 }; diff --git a/src/main.c b/src/main.c @@ -29,12 +29,12 @@ static const char *const usage[] = { void * thread_iface(void *arg) { struct pmlag_iface *iface = (struct pmlag_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; */ - /* } */ + // Open socket for the interface in the bond + iface->sockfd = sockraw_open(iface->name); + if (iface->sockfd < 0) { + pthread_exit(NULL); + return NULL; + } // Reserve receive buffer, support 64k packets just in case int buflen; @@ -42,19 +42,23 @@ void * thread_iface(void *arg) { struct sockaddr saddr; int saddr_len = sizeof(saddr); + // Get the interface's idx on the socket + iface->ifidx = iface_idx(iface->sockfd, iface->name); + printf("Thread started for iface: %s->%s(%d)\n", iface->bond->name, iface->name, iface->sockfd); - /* // Wait for the bond thread to finish initializing */ - /* pthread_mutex_lock(&(iface->bond->mtx_rt)); */ - /* pthread_mutex_unlock(&(iface->bond->mtx_rt)); */ + // Wait for the bond thread to finish initializing + pthread_mutex_lock(&(iface->bond->mtx_rt)); + pthread_mutex_unlock(&(iface->bond->mtx_rt)); // Find bond socket iface_idx int send_len; + uint16_t proto; while(1) { // Zero out buffer, to prevent pollution, & receive packet - memset(buffer, 0, RCVBUFSIZ); + /* memset(buffer, 0, RCVBUFSIZ); */ buflen = recvfrom(iface->sockfd, buffer, RCVBUFSIZ, 0, &saddr, (socklen_t *)&saddr_len); if (buflen < 0) { perror("recvfrom"); @@ -62,32 +66,32 @@ void * thread_iface(void *arg) { return NULL; } - // TODO: track received proto=0x0666 packet to update routing table - - printf("\n"); - printf("Got packet: %d bytes\n", buflen); - printf("\n"); - - printf("Ethernet header\n"); - printf("\t|- DST %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",buffer[0],buffer[1],buffer[2],buffer[3],buffer[ 4],buffer[ 5]); - printf("\t|- SRC %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",buffer[6],buffer[7],buffer[8],buffer[9],buffer[10],buffer[11]); - printf("\t|- PROTO %.4X\n", ((int)((char)buffer[12]) << 8) + buffer[13]); - printf("\n"); + // Update routing table if our custom protocol is seen + proto = ((uint16_t)((unsigned char)buffer[12]) << 8) + buffer[13]; + if (proto == 0x0666) { + pthread_mutex_lock(&(iface->bond->mtx_rt)); + // TODO: update routing table + pthread_mutex_unlock(&(iface->bond->mtx_rt)); + continue; // Don't forward the packet + } - /* memset(buffer + 16, 0, buflen - 16); */ + printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x < %.2x:%.2x:%.2x:%.2x:%.2x:%.2x, %.4x (%d)\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 + ); // Redirect packet to bond socket as-is - printf("Bond iface: %d\n\n", iface->bond->sockfd); send_len = write(iface->bond->sockfd, buffer, buflen); - printf("Got %d, sent %d\n\n", buflen, send_len); if (buflen != send_len) { perror("write(bond)"); /* pthread_exit(NULL); */ /* return NULL; */ } + } - /* sleep(3); */ pthread_exit(NULL); return NULL; } @@ -96,37 +100,91 @@ void * thread_bond(void *arg) { struct pmlag_bond *bond = (struct pmlag_bond *)arg; printf("Thread started for bond: %s\n", bond->name); - /* // Lock this bond's routing table */ - /* pthread_mutex_lock(&(bond->mtx_rt)); */ - - /* // Initialize the bond interface */ - /* bond->sockfd = tap_alloc(bond->name); */ - - /* // Start thread for each interface of this bond */ - /* struct pmlag_iface *iface = bond->interfaces; */ - /* while(iface) { */ - /* if(pthread_create(&(iface->tid), NULL, thread_iface, iface)) { */ - /* perror("Starting iface thread"); */ - /* pthread_exit((void*)1); */ - /* return (void*)1; */ - /* } */ - /* iface = iface->next; */ - /* } */ - - /* // Free this bond's routing table */ - /* pthread_mutex_unlock(&(bond->mtx_rt)); */ - - /* // TODO: blocked listen on bond, send through routing table to other ifaces */ - /* // TODO: send broadcasts to all interfaces */ - /* // TODO: timer to broadcast announce our presence to ifaces (vrrp-ish) */ - /* // hint: include sequence id, so tracking can react quickly (seq dist > 1) */ - - /* // Wait for iface threads to finish */ - /* iface = bond->interfaces; */ - /* while(iface) { */ - /* pthread_join(iface->tid, NULL); */ - /* iface = iface->next; */ - /* } */ + // Assign bond interface + unsigned char *mac = iface_mac(bond->interfaces->name); + bond->sockfd = tap_alloc(bond->name, mac); + free(mac); + 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 + struct pmlag_iface *iface = bond->interfaces; + while(iface) { + if(pthread_create(&(iface->tid), NULL, thread_iface, iface)) { + perror("Starting iface thread"); + pthread_exit((void*)1); + return (void*)1; + } + iface = iface->next; + } + + // Give iface threads time to run into the lock + /* sleep(1); */ + + // Free this bond's routing table + pthread_mutex_unlock(&(bond->mtx_rt)); + + int buflen, send_len; + unsigned char *buffer = (unsigned char *) malloc(RCVBUFSIZ); + struct sockaddr_ll saddr_ll; + saddr_ll.sll_halen = ETH_ALEN; + + while(1) { + /* sleep(1); */ + + buflen = read(bond->sockfd, buffer, RCVBUFSIZ); + if (buflen < 0) { + perror("read(bond)"); + pthread_exit(NULL); + return NULL; + } + + /* printf("\n"); */ + /* printf("Got packet: %d bytes\n", buflen); */ + + /* printf("Ethernet header\n"); */ + printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x > %.2x:%.2x:%.2x:%.2x:%.2x:%.2x, %.4x (%d)\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 + ); + + // Select interface from routing table + // DEBUG: only the first iface + iface = bond->interfaces; + + // Prepare saddr_ll for sendto + saddr_ll.sll_ifindex = iface->ifidx; + memcpy(saddr_ll.sll_addr, buffer, ETH_ALEN); + + // Forward packet to iface as-is + send_len = sendto(iface->sockfd, buffer, buflen, 0, (const struct sockaddr*)&saddr_ll, sizeof(struct sockaddr_ll)); + if(send_len != buflen) { + perror("sendto"); + /* pthread_exit(NULL); */ + /* return NULL; */ + } + + } + + // TODO: send through routing table to other ifaces + // TODO: send broadcasts to all interfaces + // TODO: timer to broadcast announce our presence to ifaces (vrrp-ish) + // hint: include sequence id, so tracking can react quickly (seq dist > 1) + + // Wait for iface threads to finish + iface = bond->interfaces; + while(iface) { + pthread_join(iface->tid, NULL); + iface = iface->next; + } pthread_exit(NULL); return NULL; @@ -161,28 +219,6 @@ int main(int argc, const char **argv) { // Initialize interfaces for all configured bonds struct pmlag_bond *bond = config->bonds; - struct pmlag_iface *iface; - unsigned char *mac; - while(bond) { - - mac = iface_mac(bond->interfaces->name); - bond->sockfd = tap_alloc(bond->name, mac); - free(mac); - if (bond->sockfd < 0) return 3; - - iface = bond->interfaces; - while(iface) { - iface->sockfd = sockraw_open(iface->name); - if (iface->sockfd < 0) return 2; - iface = iface->next; - } - - bond = bond->next; - } - - - // For each bond of config->bonds - bond = config->bonds; while(bond) { // Initialize routing table lock @@ -191,22 +227,13 @@ int main(int argc, const char **argv) { return 1; } + // Start the bond's thread if(pthread_create(&(bond->tid), NULL, thread_bond, bond)) { perror("Starting bond thread"); return 1; } - // Start a thread for each interface - iface = bond->interfaces; - while(iface) { - if(pthread_create(&(iface->tid), NULL, thread_iface, iface)) { - perror("Starting iface thread"); - return 1; - } - iface = iface->next; - } - bond = bond->next; } @@ -214,174 +241,8 @@ int main(int argc, const char **argv) { bond = config->bonds; while(bond) { pthread_join(bond->tid, NULL); - iface = bond->interfaces; - while(iface) { - pthread_join(iface->tid, NULL); - iface = iface->next; - } bond = bond->next; } - - - - - /* int sockfd = sockraw_open(INTERFACE); */ - /* if (sockfd < 0) { */ - /* return 1; */ - /* } */ - - /* if (rx) { */ - /* sockfd = sockraw_open("enp0s13f0u1c2"); */ - /* if (sockfd < 0) { */ - /* return 1; */ - /* } */ - - /* // Prepare ingress buffer */ - /* int buflen; */ - /* unsigned char *buffer = (unsigned char *) malloc(65536); */ - /* memset(buffer, 0, 65536); */ - /* struct sockaddr saddr; */ - /* int saddr_len = sizeof(saddr); */ - - /* struct ethhdr *eth = (struct ethhdr *)(buffer); */ - /* struct sockaddr_in source; */ - /* struct sockaddr_in dest; */ - - /* while(1) { */ - - /* // Fetch a packet */ - /* buflen = recvfrom(sockfd, buffer, 65536, 0, &saddr, (socklen_t *)&saddr_len); */ - /* if (buflen < 0) { */ - /* perror("recvfrom"); */ - /* exit(EXIT_FAILURE); */ - /* } */ - - /* printf("\n"); */ - /* printf("Got packet: %d bytes\n", buflen); */ - /* printf("\n"); */ - - /* printf("Ethernet header\n"); */ - /* printf("\t|- DST %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]); */ - /* printf("\t|- SRC %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]); */ - /* printf("\t|- PROTO %.4X\n",be16toh(eth->h_proto)); */ - - /* printf("\n"); */ - - /* if (be16toh(eth->h_proto) == 0x0800) { */ - /* unsigned short iphdrlen; */ - /* struct iphdr *ip = (struct iphdr*)(buffer + sizeof(struct ethhdr)); */ - /* memset(&source, 0, sizeof(source)); */ - /* source.sin_addr.s_addr = ip->saddr; */ - /* memset(&dest, 0, sizeof(dest)); */ - /* dest.sin_addr.s_addr = ip->daddr; */ - - /* printf("IP header\n"); */ - /* printf("\t|- Version %d\n",(unsigned int)ip->version); */ - /* printf("\t|- Internet hlen %d DWORDS or %d Bytes\n",(unsigned int)ip->ihl,((unsigned int)(ip->ihl))*4); */ - /* printf("\t|- Type Of Service %d\n",(unsigned int)ip->tos); */ - /* printf("\t|- Total Length %d Bytes\n",ntohs(ip->tot_len)); */ - /* printf("\t|- Identification %d\n",ntohs(ip->id)); */ - /* printf("\t|- Time To Live %d\n",(unsigned int)ip->ttl); */ - /* printf("\t|- Protocol %d\n",(unsigned int)ip->protocol); */ - /* printf("\t|- Header Checksum %d\n",ntohs(ip->check)); */ - /* printf("\t|- Source IP %s\n", inet_ntoa(source.sin_addr)); */ - /* printf("\t|- Destination IP %s\n",inet_ntoa(dest.sin_addr)); */ - - /* printf("\n"); */ - /* } */ - - /* } */ - - /* } */ - - /* if (tx) { */ - /* sockfd = sockraw_open(INTERFACE); */ - /* if (sockfd < 0) { */ - /* return 1; */ - /* } */ - - /* // Prepare egress buffer */ - /* int buflen; */ - /* unsigned char *buffer = (unsigned char *) malloc(65536); */ - /* memset(buffer, 0, 65536); */ - /* struct sockaddr_ll saddr; */ - /* int saddr_len = sizeof(saddr); */ - - /* // Get MAC address of the interface */ - /* struct ifreq ifreq_c; */ - /* memset(&ifreq_c,0,sizeof(ifreq_c)); */ - /* strncpy(ifreq_c.ifr_name,INTERFACE,IFNAMSIZ-1);//giving name of Interface */ - /* if((ioctl(sockfd,SIOCGIFHWADDR,&ifreq_c))<0) { */ - /* perror("error in SIOCGIFHWADDR ioctl reading"); */ - /* return 1; */ - /* } */ - - /* // Get ethhdr reference to the buffer */ - /* struct ethhdr *eth = (struct ethhdr *)(buffer); */ - - /* // Fill ethernet source address */ - /* memcpy(eth->h_source, ifreq_c.ifr_hwaddr.sa_data, 6); */ - /* /1* eth->h_source[0] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[0]); *1/ */ - /* /1* eth->h_source[1] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[1]); *1/ */ - /* /1* eth->h_source[2] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[2]); *1/ */ - /* /1* eth->h_source[3] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[3]); *1/ */ - /* /1* eth->h_source[4] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[4]); *1/ */ - /* /1* eth->h_source[5] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[5]); *1/ */ - - /* // Broadcast packet */ - /* eth->h_dest[0] = 0xFF; */ - /* eth->h_dest[1] = 0xFF; */ - /* eth->h_dest[2] = 0xFF; */ - /* eth->h_dest[3] = 0xFF; */ - /* eth->h_dest[4] = 0xFF; */ - /* eth->h_dest[5] = 0xFF; */ - - /* // Custom protocol (not shown as registered on https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml) */ - /* eth->h_proto = htons(0x0666); */ - - /* struct sockaddr_ll sadr_ll; */ - /* sadr_ll.sll_ifindex = iface_idx(sockfd, INTERFACE); */ - /* sadr_ll.sll_halen = ETH_ALEN; // length of destination mac address */ - /* memcpy(sadr_ll.sll_addr, eth->h_dest, ETH_ALEN); */ - - /* /1* sadr_ll.sll_addr[0] = 0xFF; *1/ */ - /* /1* sadr_ll.sll_addr[1] = 0xFF; *1/ */ - /* /1* sadr_ll.sll_addr[2] = 0xFF; *1/ */ - /* /1* sadr_ll.sll_addr[3] = 0xFF; *1/ */ - /* /1* sadr_ll.sll_addr[4] = 0xFF; *1/ */ - /* /1* sadr_ll.sll_addr[5] = 0xFF; *1/ */ - - /* /1* saddr.sll_ifindex = ifreq_i.ifr_ifindex; // index of interface *1/ */ - /* /1* saddr.sll_halen = ETH_ALEN; // length of destination mac address *1/ */ - /* /1* saddr.sll_addr[0] = DESTMAC0; *1/ */ - /* /1* saddr.sll_addr[1] = DESTMAC1; *1/ */ - /* /1* saddr.sll_addr[2] = DESTMAC2; *1/ */ - /* /1* saddr.sll_addr[3] = DESTMAC3; *1/ */ - /* /1* saddr.sll_addr[4] = DESTMAC4; *1/ */ - /* /1* saddr.sll_addr[5] = DESTMAC5; *1/ */ - - - /* int send_len = sendto(sockfd,buffer, 12 + 2 + 46,0,(const struct sockaddr*)&sadr_ll,sizeof(struct sockaddr_ll)); */ - /* if(send_len<0) { */ - /* printf("error in sending....sendlen=%d....errno=%d\n",send_len,errno); */ - /* return -1; */ - /* } */ - - /* printf("\n"); */ - /* printf("Sent packet: %d bytes\n", send_len); */ - /* printf("\n"); */ - - /* printf("Ethernet header\n"); */ - /* printf("\t|- DST %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]); */ - /* printf("\t|- SRC %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]); */ - /* printf("\t|- PROTO %.4X\n",be16toh(eth->h_proto)); */ - - /* } */ - - /* printf("name: %s\n", __NAME); */ - /* printf("conf: %s\n", config_file); */ - /* printf("sock: %d\n", sockfd); */ - return 0; } diff --git a/src/socket.c b/src/socket.c @@ -16,13 +16,13 @@ unsigned char * iface_mac(char * ifname) { struct ifreq ifr; - unsigned char *mac = calloc(1, ETH_HLEN); + 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_HLEN); + memcpy(mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); return mac; } @@ -94,7 +94,7 @@ int tap_alloc(char * ifname, unsigned char * mac) { // Bring up the interface memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TAP | IFF_NAPI | IFF_MULTI_QUEUE; + ifr.ifr_flags = IFF_TAP | IFF_MULTI_QUEUE | IFF_NO_PI; if( *ifname ) { strncpy(ifr.ifr_name, ifname, IFNAMSIZ); } @@ -104,7 +104,7 @@ int tap_alloc(char * ifname, unsigned char * mac) { /* 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_HLEN); */ + /* memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ALEN); */ /* if ((err = if_ioctl(SIOCSIFHWADDR, &ifr)) < 0) { */ /* perror("Set if hwaddr"); */ /* close(fd); */ diff --git a/test.ini b/test.ini @@ -3,7 +3,9 @@ ; eth0 = backup, eth1 = primary [bond0] mode=active-backup -interface=wlp170s0 +interface=virbr3 +; interface=wlp170s0 +; interface=docker0 weight=10 ; ; broadcast everything on all interfaces