/*---------------------------------------------------------------
 * Copyright (c) 1999,2000,2001,2002,2003
 * The Board of Trustees of the University of Illinois
 * All Rights Reserved.
 *---------------------------------------------------------------
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software (Iperf) 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:
 *
 *
 * Redistributions of source code must retain the above
 * copyright notice, this list of conditions and
 * the following disclaimers.
 *
 *
 * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimers in the documentation and/or other materials
 * provided with the distribution.
 *
 *
 * Neither the names of the University of Illinois, NCSA,
 * nor the names of its contributors may be used to endorse
 * or promote products derived from this Software without
 * specific prior written permission.
 *
 * 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 CONTIBUTORS 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.
 * ________________________________________________________________
 * National Laboratory for Applied Network Research
 * National Center for Supercomputing Applications
 * University of Illinois at Urbana-Champaign
 * http://www.ncsa.uiuc.edu
 * ________________________________________________________________
 *
 * Socket.cpp
 * by       Ajay Tirumala <tirumala@ncsa.uiuc.edu>
 * and      Mark Gates <mgates@nlanr.net>
 * ------------------------------------------------------------------- */

#define HEADERS()

#include "headers.h"

#include "SocketAddr.h"
#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

int SockAddr_getAFdomain (iperf_sockaddr *inSockAddr) {
    return (SockAddr_isIPv6(inSockAddr) ?
#if HAVE_IPV6
            AF_INET6
#else
            AF_INET
#endif
            : AF_INET);
}

/* -------------------------------------------------------------------
 * Create a socket address. If inHostname is not null, resolve that
 * address and fill it in. Fill in the port number. Use IPv6 ADDR_ANY
 * if that is what is desired.
 * ------------------------------------------------------------------- */
void SockAddr_remoteAddr (struct thread_Settings *inSettings) {
    if (SockAddr_isZeroAddress(&inSettings->peer) == 0) {
        if (inSettings->mHost != NULL) {
            SockAddr_setHostname(inSettings->mHost, &inSettings->peer, &inSettings->size_peer, isIPV6(inSettings));
            if (inSettings->incrdstip)
                SockAddr_incrAddress(&inSettings->peer, inSettings->incrdstip);
        } else {
#if HAVE_IPV6
            if (isIPV6(inSettings)) {
                ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET6;
            } else {
                ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
            }
        }
        if (SockAddr_isIPv6(&inSettings->peer)) {
            inSettings->size_peer = sizeof(struct sockaddr_in6);
        } else {
            inSettings->size_peer = sizeof(struct sockaddr_in);
        }
#else
	}
    ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
	inSettings->size_peer = sizeof(struct sockaddr_in);
#endif
	SockAddr_setPort(&inSettings->peer, inSettings->mPort);
}
}
// end SocketAddr

void SockAddr_localAddr (struct thread_Settings *inSettings) {
    SockAddr_zeroAddress(&inSettings->local);

    if (inSettings->mLocalhost != NULL) {
        SockAddr_setHostname(inSettings->mLocalhost, &inSettings->local, &inSettings->size_local,
			     isIPV6(inSettings));
	if (inSettings->incrsrcip)
	    SockAddr_incrAddress(&inSettings->local, inSettings->incrsrcip);
    } else {
#if HAVE_IPV6
        if (isIPV6(inSettings)) {
            ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET6;
        } else {
            ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
        }
    }

    if (SockAddr_isIPv6(&inSettings->local)) {
        inSettings->size_local = sizeof(struct sockaddr_in6);
    } else {
        inSettings->size_local = sizeof(struct sockaddr_in);
    }
#else
        ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
    }
        inSettings->size_local = sizeof(struct sockaddr_in);
#endif
     /*
      *  This section handles the *local* port binding (which is messy)
      *  Quintuple is Proto:LocalIP:LocalPort:DstIP:DstPort
      *
      *  There are three threads being Client, Listener and Server
      *  mPort comes from the -p command (which defaults to 5001)
      *  mLocalhost indicates -B set requesting a local binding
      *  mBindPort comes from -B IP:<port> (where port defaults to 0)
      *  Multicast IP address, e.g. 239.1.1.1, is set per a -B
      *  Zero will cause the OS to auto assign a LocalPort
      *  For iperf -s; Windows uses listener thread, *nix a server thread
      *  (so, effectively, Listener and Server threads are the same)
      *  Client threads support either auto assignment (default) or
      *  user specified (via -B)
      */
     if (inSettings->mLocalhost == NULL) {
         if (inSettings->mThreadMode == kMode_Client) {
             /*
              * Client thread, -p and no -B,
              * OS will auto assign a free local port
              */
             SockAddr_setPortAny (&inSettings->local);
         } else {
             /* Server or Listener thread, -p and no -B */
             SockAddr_setPort(&inSettings->local, inSettings->mPort);
         }
     } else {
         // -B was set
         if (inSettings->mThreadMode == kMode_Client) {
             /* Client thread */
             if (inSettings->mBindPort) {
                 /*
                  * User specified port so use it
                  */
                 SockAddr_setPort(&inSettings->local, (inSettings->mBindPort + inSettings->incrsrcport));
             } else {
                 /*
                  * No user specified port, let OS assign a free one
                  */
                 SockAddr_setPortAny (&inSettings->local);
             }
         } else {
             /*
              * Server or Listener thread, both always use -p port
              * any -B port will be ignored
              */
             SockAddr_setPort(&inSettings->local, inSettings->mPort);
         }
     }
}

// end SocketAddr

/* -------------------------------------------------------------------
 * Resolve the hostname address and fill it in.
 * ------------------------------------------------------------------- */
void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, Socklen_t *addr_size, int isIPv6) {
    // ..I think this works for both ipv6 & ipv4... we'll see
    bool found = false;
    int ret_ga;
    struct addrinfo *res = NULL, *itr;
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    if (!isIPv6) {
        hints.ai_family = AF_INET;
        ret_ga = getaddrinfo(inHostname, NULL, &hints, &res);
        if (ret_ga == 0) {
            if (res && res->ai_addr) {
                itr = res;
                // Now search for a IPv4 Address
                while (itr != NULL) {
                    if (itr->ai_family == AF_INET) {
                        memcpy(inSockAddr, (itr->ai_addr), (itr->ai_addrlen));
                        *addr_size = (Socklen_t) sizeof(struct sockaddr_in);
                        freeaddrinfo(res);
                        found = true;
                        break;
                    } else {
                        itr = itr->ai_next;
                    }
                }
            }
        }
    }
#if HAVE_IPV6
    if (!found) {
        hints.ai_family = AF_INET6;
        ret_ga = getaddrinfo(inHostname, NULL, &hints, &res);
        if (ret_ga == 0) {
            if (res && res->ai_addr) {
                // Now search for a IPv6 Address
                itr = res;
                while (itr != NULL) {
                    if (itr->ai_family == AF_INET6) {
                        memcpy(inSockAddr, (itr->ai_addr), (itr->ai_addrlen));
                        freeaddrinfo(res);
                        *addr_size = (Socklen_t) sizeof(struct sockaddr_in6);
                        found = true;
                        break;
                    } else {
                        itr = itr->ai_next;
                    }
                }
            }
        }
    }
#endif // IPV6
    // getaddrinfo didn't find an address, fallback to gethostbyname for v4
    if (!found && !isIPv6) {
        // first try just converting dotted decimal
        // on Windows gethostbyname doesn't understand dotted decimal
        struct sockaddr_in *sockaddr = (struct sockaddr_in *)inSockAddr;
        int rc = inet_pton(AF_INET, inHostname, &sockaddr->sin_addr);
        sockaddr->sin_family = AF_INET;
        if (rc == 0) {
            struct hostent *hostP = gethostbyname(inHostname);
            if (hostP == NULL) {
                /* this is the same as herror() but works on more systems */
                const char* format;
                switch (h_errno) {
                case HOST_NOT_FOUND:
                    format = "%s: Unknown host\n";
                    break;
                case NO_ADDRESS:
                    format = "%s: No address associated with name\n";
                    break;
                case NO_RECOVERY:
                    format = "%s: Unknown server error\n";
                    break;
                case TRY_AGAIN:
                    format = "%s: Host name lookup failure\n";
                    break;
                default:
                    format = "%s: Unknown resolver error\n";
                    break;
                }
                fprintf(stderr, format, inHostname);
                exit(1);
                return; // TODO throw
            }
            found = true;
            memcpy(&sockaddr->sin_addr, *(hostP->h_addr_list), (hostP->h_length));
        }
    }
    if (!found) {
        if (!isIPv6) {
            fprintf(stderr, "ERROR: failed to find an ip address for host '%s'\n", inHostname);
        } else {
            fprintf(stderr, "ERROR: failed to find an ipv6 address for host '%s'\n", inHostname);
        }
        exit(1);
    }
}
// end setHostname

/* -------------------------------------------------------------------
 * Copy the IP address into the string.
 * ------------------------------------------------------------------- */
void SockAddr_getHostAddress (iperf_sockaddr *inSockAddr, char* outAddress,
                                size_t len) {
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) {
        inet_ntop(AF_INET, &(((struct sockaddr_in*) inSockAddr)->sin_addr),
                  outAddress, len);
    }
#if HAVE_IPV6
    else {
        inet_ntop(AF_INET6, &(((struct sockaddr_in6*) inSockAddr)->sin6_addr),
                  outAddress, len);
    }
#endif
}
// end getHostAddress

/* -------------------------------------------------------------------
 * Set the address to any (generally all zeros).
 * ------------------------------------------------------------------- */
void SockAddr_setAddressAny (iperf_sockaddr *inSockAddr) {
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
        memset(&(((struct sockaddr_in*) inSockAddr)->sin_addr), 0,
               sizeof(struct in_addr));
#if HAVE_IPV6
    else
        memset(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr), 0,
               sizeof(struct in6_addr));
#endif
}
// end setAddressAny

/* -------------------------------------------------------------------
 * Incr the address by value
 * ------------------------------------------------------------------- */
void SockAddr_incrAddress (iperf_sockaddr *inSockAddr, int value) {
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
	((struct sockaddr_in *)inSockAddr)->sin_addr.s_addr += htonl(value);
#if HAVE_IPV6
    else {
	uint32_t *lower = (uint32_t *)&((struct sockaddr_in6 *)inSockAddr)->sin6_addr.s6_addr[12];
	*lower += htonl(value);
    }
#endif
}
// end setAddressAny


/* -------------------------------------------------------------------
 * Set the port to the given port. Handles the byte swapping.
 * ------------------------------------------------------------------- */
void SockAddr_setPort (iperf_sockaddr *inSockAddr, unsigned short inPort) {
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
        ((struct sockaddr_in*) inSockAddr)->sin_port = htons(inPort);
#if HAVE_IPV6
    else
        ((struct sockaddr_in6*) inSockAddr)->sin6_port = htons(inPort);
#endif

}
// end setPort

/* -------------------------------------------------------------------
 * Set the port to zero, which lets the OS pick the port.
 * ------------------------------------------------------------------- */
void SockAddr_setPortAny (iperf_sockaddr *inSockAddr) {
    SockAddr_setPort(inSockAddr, 0);
}
// end setPortAny

/* -------------------------------------------------------------------
 * Return the port. Handles the byte swapping.
 * ------------------------------------------------------------------- */
unsigned short SockAddr_getPort (iperf_sockaddr *inSockAddr) {
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
        return ntohs(((struct sockaddr_in*) inSockAddr)->sin_port);
#if HAVE_IPV6
    else
        return ntohs(((struct sockaddr_in6*) inSockAddr)->sin6_port);
#endif
    return 0;

}
// end getPort

/* -------------------------------------------------------------------
 * Return the IPv4 Internet Address from the sockaddr_in structure
 * ------------------------------------------------------------------- */
struct in_addr* SockAddr_get_in_addr (iperf_sockaddr *inSockAddr) {
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
        return &(((struct sockaddr_in*) inSockAddr)->sin_addr);

    fprintf(stderr, "FATAL: get_in_addr called on IPv6 address\n");
    return NULL;
}

/* -------------------------------------------------------------------
 * Return the IPv6 Internet Address from the sockaddr_in6 structure
 * ------------------------------------------------------------------- */
#if HAVE_IPV6
struct in6_addr* SockAddr_get_in6_addr (iperf_sockaddr *inSockAddr) {
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6)
        return &(((struct sockaddr_in6*) inSockAddr)->sin6_addr);

    fprintf(stderr, "FATAL: get_in6_addr called on IPv4 address\n");
    return NULL;
}
#endif


/* -------------------------------------------------------------------
 * Return the size of the appropriate address structure.
 * ------------------------------------------------------------------- */
Socklen_t SockAddr_get_sizeof_sockaddr (iperf_sockaddr *inSockAddr) {
#if HAVE_IPV6
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
        return(sizeof(struct sockaddr_in6));
    }
#endif
    return(sizeof(struct sockaddr_in));
}
// end get_sizeof_sockaddr


/* -------------------------------------------------------------------
 * Return if IPv6 socket
 * ------------------------------------------------------------------- */
int SockAddr_isIPv6 (iperf_sockaddr *inSockAddr) {
#if HAVE_IPV6
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
        return 1;
    }
#endif
    return 0;
}
// end get_sizeof_sockaddr

/* -------------------------------------------------------------------
 * Return true if the address is multicast ip address.
 * ------------------------------------------------------------------- */
int SockAddr_isMulticast (iperf_sockaddr *inSockAddr) {
#if HAVE_IPV6
    if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
        return(IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr)));
    } else
#endif
	{

#ifdef IN_MULTICAST
	    // 224.0.0.0 to 239.255.255.255 (e0.00.00.00 to ef.ff.ff.ff)
	    // convert from network to host byte order
	    uint32_t maddr = ntohl((uint32_t)(((struct sockaddr_in*) inSockAddr)->sin_addr.s_addr));
	    return (IN_MULTICAST(maddr));
#else
	    const unsigned long kClassD_Mask = 0xf0000000L;
	    const unsigned long kMulticast = 0xe0000000L;
	    return((((ntohl(((struct sockaddr_in*) inSockAddr)->sin_addr.s_addr)) & kClassD_Mask) == kMulticast));
#endif
	}
}
// end isMulticast

/* -------------------------------------------------------------------
 * Return true if the address is multicast ip address.
 * ------------------------------------------------------------------- */
int SockAddr_isLinklocal (iperf_sockaddr *inSockAddr) {
#if HAVE_IPV6
  if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
      return(IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr)));
    } else
#endif
    {
      return 0;
    }
}

/* -------------------------------------------------------------------
 * Zero out the address structure.
 * ------------------------------------------------------------------- */
void SockAddr_zeroAddress (iperf_sockaddr *inSockAddr) {
    memset(inSockAddr, 0, sizeof(iperf_sockaddr));
}

int SockAddr_isZeroAddress (iperf_sockaddr *inSockAddr) {
    iperf_sockaddr zeroSockAddr;
    memset(&zeroSockAddr, 0, sizeof(iperf_sockaddr));
    return(memcmp((void *)inSockAddr, (void *)&zeroSockAddr, sizeof(iperf_sockaddr)));
}

/* -------------------------------------------------------------------
 * Compare two sockaddrs and return true if they are equal
 * ------------------------------------------------------------------- */
int SockAddr_are_Equal (iperf_sockaddr *first, iperf_sockaddr *second) {
    if (((struct sockaddr*)first)->sa_family == AF_INET && ((struct sockaddr*)second)->sa_family == AF_INET) {
        // compare IPv4 adresses
        return(((long) ((struct sockaddr_in*)first)->sin_addr.s_addr == (long) ((struct sockaddr_in*)second)->sin_addr.s_addr)
                && (((struct sockaddr_in*)first)->sin_port == ((struct sockaddr_in*)second)->sin_port));
    }
#if HAVE_IPV6
    if (((struct sockaddr*)first)->sa_family == AF_INET6 && ((struct sockaddr*)second)->sa_family == AF_INET6) {
        // compare IPv6 addresses
        return(!memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr, ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr))
                && (((struct sockaddr_in6*)first)->sin6_port == ((struct sockaddr_in6*)second)->sin6_port));
    }
#endif
    return 0;
}

/* -------------------------------------------------------------------
 * Compare two sockaddrs and return true if the hosts are equal
 * ------------------------------------------------------------------- */
int SockAddr_Hostare_Equal (iperf_sockaddr* first, iperf_sockaddr *second) {
    if (((struct sockaddr*)first)->sa_family == AF_INET && ((struct sockaddr*)second)->sa_family == AF_INET) {
        // compare IPv4 adresses
        return((long) ((struct sockaddr_in*)first)->sin_addr.s_addr ==
                (long) ((struct sockaddr_in*)second)->sin_addr.s_addr);
    }
#if HAVE_IPV6
    if (((struct sockaddr*)first)->sa_family == AF_INET6 && ((struct sockaddr*)second)->sa_family == AF_INET6) {
        // compare IPv6 addresses
        return(!memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr,
                        ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr)));
    }
#endif
    return 0;
}

/* -------------------------------------------------------------------
 * Find the interface name of a connected socket (when not already set)
 * Can be forced with -B <ip>%<name> (server), -c <ip>%<name> (client)
 * Note that kernel maps, e.g. via routing tables, to the actual device
 * so these can change.  Assume they won't change during the life
 * of a thread.
 *
 * Store (and cache) the results in the thread settings structure
 * Return 0 if set, -1 if not
 * ------------------------------------------------------------------- */
int SockAddr_Ifrname (struct thread_Settings *inSettings) {
#ifdef HAVE_GETIFADDRS
    if (inSettings->mIfrname == NULL) {
        struct sockaddr_storage myaddr;
        struct ifaddrs* ifaddr;
        struct ifaddrs* ifa;
        socklen_t addr_len;
        addr_len = sizeof(struct sockaddr_storage);
        getsockname(inSettings->mSock, (struct sockaddr*)&myaddr, &addr_len);
        getifaddrs(&ifaddr);

        // look which interface contains the desired IP per getsockname() which sets myaddr
        // When found, ifa->ifa_name contains the name of the interface (eth0, eth1, ppp0...)
        if (myaddr.ss_family == AF_INET) {
            // v4 socket family (supports v4 only)
            struct sockaddr_in* addr = (struct sockaddr_in*)&myaddr;
            for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
                if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_INET)) {
                    struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr;
                    if ((inaddr->sin_addr.s_addr == addr->sin_addr.s_addr) && (ifa->ifa_name)) {
                        // Found v4 address in v4 addr family, copy it to thread settings structure
                        inSettings->mIfrname = calloc (strlen(ifa->ifa_name) + 1, sizeof(char));
                        strcpy(inSettings->mIfrname, ifa->ifa_name);
                        break;
                    }
                }
            }
        } else if (myaddr.ss_family == AF_INET6) {
            // v6 socket family (supports both v4 and v6)
            struct sockaddr_in6* addr = (struct sockaddr_in6*)&myaddr;
            // Link local address are shared amongst all devices
            // Try to pull the interface from the destination
            if ((inSettings->mThreadMode == kMode_Client) && (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))) {
                char *results;
                char *copy = (char *)malloc(strlen(inSettings->mHost)+1);
                strcpy(copy,(const char *)inSettings->mHost);
                if (((results = strtok(copy, "%")) != NULL) && ((results = strtok(NULL, "%")) != NULL)) {
                    inSettings->mIfrname = calloc (strlen(results) + 1, sizeof(char));
                    strcpy(inSettings->mIfrname, results);
                }
                free(copy);
            } else if ((inSettings->mThreadMode == kMode_Server) && (IN6_IS_ADDR_V4MAPPED (&addr->sin6_addr))) {
                for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
                    if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_INET)) {
                        struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr;
                        uint32_t v4;
                        memcpy(&v4, &addr->sin6_addr.s6_addr[12], 4);
                        if ((ifa->ifa_name) && (inaddr->sin_addr.s_addr == v4)) {
                            // Found v4 address in v4 addr family, copy it to thread settings structure
                            inSettings->mIfrname = calloc (strlen(ifa->ifa_name) + 1, sizeof(char));
                            strcpy(inSettings->mIfrname, ifa->ifa_name);
                            break;
                        }
                    }
                }
            } else {
                // Hunt the v6 interfaces
                for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
                    if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_INET6)) {
                        struct sockaddr_in6* inaddr = (struct sockaddr_in6*)ifa->ifa_addr;
                        if ((ifa->ifa_name) && (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, &inaddr->sin6_addr))) {
                            // Found v6 address in v6 addr family, copy it to thread settings structure
                            inSettings->mIfrname = calloc (strlen(ifa->ifa_name) + 1, sizeof(char));
                            strcpy(inSettings->mIfrname, ifa->ifa_name);
                            break;
                        }
                    }
                }
            }
        }
        freeifaddrs(ifaddr);
    }
#endif
    return ((inSettings->mIfrname == NULL) ? -1 : 0);
}


#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
int SockAddr_Drop_All_BPF (int sock) {
    struct sock_filter udp_filter[] = {
        { 0x6, 0, 0, 0x00000000 },
    };
    struct sock_fprog bpf = {
        .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
        .filter = udp_filter,
    };
    return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
}

//
// [root@ryzen3950 iperf2-code]# tcpdump ip and udp dst port 5001 -e -dd -i any
// tcpdump: data link type LINUX_SLL2
// { 0x28, 0, 0, 0x00000000 },
// { 0x15, 0, 8, 0x00000800 },
// { 0x30, 0, 0, 0x0000001d },
// { 0x15, 0, 6, 0x00000011 },
// { 0x28, 0, 0, 0x0000001a },
// { 0x45, 4, 0, 0x00001fff },
// { 0xb1, 0, 0, 0x00000014 },
// { 0x48, 0, 0, 0x00000016 },
// { 0x15, 0, 1, 0x00001389 },
// { 0x6, 0, 0, 0x00040000 },
// { 0x6, 0, 0, 0x00000000 },

//	{ 0x28, 0, 0, 0x00000014 },
//	{ 0x45, 4, 0, 0x00001fff },
//	{ 0xb1, 0, 0, 0x0000001e },
//	{ 0x48, 0, 0, 0x00000010 },
//	{ 0x15, 0, 1, 0x00001389 },


int SockAddr_v4_Accept_BPF (int sock, uint16_t port) {
    // tcpdump udp dst port 5001 -dd to get c code filter
    // UDP port is the 5 and 13 bytecodes (5001 = 0x1389)
    // see linux/filter.h
    struct sock_filter udp_filter[] = {
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 0, 8, 0x00000800 },
	{ 0x30, 0, 0, 0x00000017 },
	{ 0x15, 0, 6, 0x00000011 },
	{ 0x28, 0, 0, 0x00000014 },
	{ 0x45, 4, 0, 0x00001fff },
	{ 0xb1, 0, 0, 0x0000000e },
	{ 0x48, 0, 0, 0x00000010 },
	{ 0x15, 0, 1, 0x00001389 },
	{ 0x6, 0, 0, 0x00040000 },
	{ 0x6, 0, 0, 0x00000000 },
    };
    udp_filter[8].k = port;
    struct sock_fprog bpf = {
	.len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
	.filter = udp_filter,
    };
    return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
}

//[root@ryzen3950 iperf2-code]# tcpdump  udp dst port 5001 and dst host 1.1.1.1 -dd
//Warning: assuming Ethernet
//{ 0x28, 0, 0, 0x0000000c },
//{ 0x15, 11, 0, 0x000086dd },
//{ 0x15, 0, 10, 0x00000800 },
//{ 0x30, 0, 0, 0x00000017 },
//{ 0x15, 0, 8, 0x00000011 },
//{ 0x28, 0, 0, 0x00000014 },
//{ 0x45, 6, 0, 0x00001fff },
//{ 0xb1, 0, 0, 0x0000000e },
//{ 0x48, 0, 0, 0x00000010 },
//{ 0x15, 0, 3, 0x00001389 },
//{ 0x20, 0, 0, 0x0000001e },
//{ 0x15, 0, 1, 0x01010101 },
//{ 0x6, 0, 0, 0x00040000 },
//{ 0x6, 0, 0, 0x00000000 },
//
// BPF for TAP interaces with explicit v4 IP and UDP dst port
int SockAddr_Accept_V4_TAP_BPF (int sock, uint32_t dstip, uint16_t port) {
    struct sock_filter udp_filter[] = {
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 11, 0, 0x000086dd },
	{ 0x15, 0, 10, 0x00000800 },
	{ 0x30, 0, 0, 0x00000017 },
	{ 0x15, 0, 8, 0x00000011 },
	{ 0x28, 0, 0, 0x00000014 },
	{ 0x45, 6, 0, 0x00001fff },
	{ 0xb1, 0, 0, 0x0000000e },
	{ 0x48, 0, 0, 0x00000010 },
	{ 0x15, 0, 3, 0x00001389 },
	{ 0x20, 0, 0, 0x0000001e },
	{ 0x15, 0, 1, 0x00000000 },
	{ 0x6, 0, 0, 0x00040000 },
	{ 0x6, 0, 0, 0x00000000 },
    };
    udp_filter[12].k = htonl(dstip);
    udp_filter[9].k = htons(port);
    struct sock_fprog bpf = {
	.len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
	.filter = udp_filter,
    };
    return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
}

// Simulate the UDP connect for the AF_PACKET (or PF_PACKET)
//
int SockAddr_v4_Connect_BPF (int sock, uint32_t dstip, uint32_t srcip, uint16_t dstport, uint16_t srcport) {
    // Use full quintuple, proto, src ip, dst ip, src port, dst port
    // ip proto is already set per the PF_PACKET ETH_P_IP
    // tcpdump udp and ip src 127.0.0.1 and ip dst 127.0.0.2 and src port 5001 and dst port 5002  -dd
    //
    // tcpdump udp and ip src 127.0.0.1 and ip dst 127.0.0.2 and src port 5001 and dst port 5002 -d
    //  (000) ldh      [12]
    //  (001) jeq      #0x86dd          jt 17	jf 2
    //  (002) jeq      #0x800           jt 3	jf 17
    //  (003) ldb      [23]
    //  (004) jeq      #0x11            jt 5	jf 17
    //  (005) ld       [26]
    //  (006) jeq      #0x7f000001      jt 7	jf 17
    //  (007) ld       [30]
    //  (008) jeq      #0x7f000002      jt 9	jf 17
    //  (009) ldh      [20]
    //  (010) jset     #0x1fff          jt 17	jf 11
    //  (011) ldxb     4*([14]&0xf)
    //  (012) ldh      [x + 14]
    //  (013) jeq      #0x1389          jt 14	jf 17
    //  (014) ldh      [x + 16]
    //  (015) jeq      #0x138a          jt 16	jf 17
    //  (016) ret      #262144
    //  (017) ret      #0
    //
    struct sock_filter udp_filter[] = {
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 15, 0, 0x000086dd },
	{ 0x15, 0, 14, 0x00000800 },
	{ 0x30, 0, 0, 0x00000017 },
	{ 0x15, 0, 12, 0x00000011 },
	{ 0x20, 0, 0, 0x0000001a },
	{ 0x15, 0, 10, 0x7f000001 },
	{ 0x20, 0, 0, 0x0000001e },
	{ 0x15, 0, 8, 0x7f000002 },
	{ 0x28, 0, 0, 0x00000014 },
	{ 0x45, 6, 0, 0x00001fff },
	{ 0xb1, 0, 0, 0x0000000e },
	{ 0x48, 0, 0, 0x0000000e },
	{ 0x15, 0, 3, 0x00001389 },
	{ 0x48, 0, 0, 0x00000010 },
	{ 0x15, 0, 1, 0x0000138a },
	{ 0x6, 0, 0, 0x00040000 },
	{ 0x6, 0, 0, 0x00000000 },
    };
    udp_filter[6].k = htonl(srcip);
    udp_filter[8].k = htonl(dstip);
    udp_filter[13].k = htons(srcport);
    udp_filter[15].k = htons(dstport);
    struct sock_fprog bpf = {
	.len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
	.filter = udp_filter,
    };
    return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
}
int SockAddr_v4_Connect_TAP_BPF (int sock, uint32_t dstip, uint32_t srcip, uint16_t dstport, uint16_t srcport) {
    // [root@ryzen3950 iperf2-code]# tcpdump ip and src 127.0.0.1 and dst 127.0.0.2 and udp src port 5001 and dst port 5002  -d
    // Warning: assuming Ethernet
    // (000) ldh      [12]
    // (001) jeq      #0x800           jt 2	jf 16
    // (002) ld       [26]
    // (003) jeq      #0x7f000001      jt 4	jf 16
    // (004) ld       [30]
    // (005) jeq      #0x7f000002      jt 6	jf 16
    // (006) ldb      [23]
    // (007) jeq      #0x11            jt 8	jf 16
    // (008) ldh      [20]
    // (009) jset     #0x1fff          jt 16	jf 10
    // (010) ldxb     4*([14]&0xf)
    // (011) ldh      [x + 14]
    // (012) jeq      #0x1389          jt 13	jf 16
    // (013) ldh      [x + 16]
    // (014) jeq      #0x138a          jt 15	jf 16
    // (015) ret      #262144
    // (016) ret      #0
    // [root@ryzen3950 iperf2-code]# tcpdump ip and src 127.0.0.1 and dst 127.0.0.2 and udp src port 5001 and dst port 5002  -dd
    // Warning: assuming Ethernet
    struct sock_filter udp_filter[] = {
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 0, 14, 0x00000800 },
	{ 0x20, 0, 0, 0x0000001a },
	{ 0x15, 0, 12, 0x7f000001 },
	{ 0x20, 0, 0, 0x0000001e },
	{ 0x15, 0, 10, 0x7f000002 },
	{ 0x30, 0, 0, 0x00000017 },
	{ 0x15, 0, 8, 0x00000011 },
	{ 0x28, 0, 0, 0x00000014 },
	{ 0x45, 6, 0, 0x00001fff },
	{ 0xb1, 0, 0, 0x0000000e },
	{ 0x48, 0, 0, 0x0000000e },
	{ 0x15, 0, 3, 0x00001389 },
	{ 0x48, 0, 0, 0x00000010 },
	{ 0x15, 0, 1, 0x0000138a },
	{ 0x6, 0, 0, 0x00040000 },
	{ 0x6, 0, 0, 0x00000000 },
    };
    udp_filter[3].k = htonl(srcip);
    udp_filter[5].k = htonl(dstip);
    udp_filter[12].k = htons(srcport);
    udp_filter[14].k = htons(dstport);
    struct sock_fprog bpf = {
	.len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
	.filter = udp_filter,
    };
    return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
}

int SockAddr_v4_Connect_BPF_Drop (int sock, uint32_t dstip, uint32_t srcip, uint16_t dstport, uint16_t srcport) {
    // Use full quintuple, proto, src ip, dst ip, src port, dst port
    // ip proto is already set per the PF_PACKET ETH_P_IP
    // tcpdump udp and ip src 127.0.0.1 and ip dst 127.0.0.2 and src port 5001 and dst port 5002 -dd

    struct sock_filter udp_filter[] = {
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 15, 0, 0x000086dd },
	{ 0x15, 0, 14, 0x00000800 },
	{ 0x30, 0, 0, 0x00000017 },
	{ 0x15, 0, 12, 0x00000011 },
	{ 0x20, 0, 0, 0x0000001a },
	{ 0x15, 0, 10, 0x7f000001 },
	{ 0x20, 0, 0, 0x0000001e },
	{ 0x15, 0, 8, 0x7f000002 },
	{ 0x28, 0, 0, 0x00000014 },
	{ 0x45, 6, 0, 0x00001fff },
	{ 0xb1, 0, 0, 0x0000000e },
	{ 0x48, 0, 0, 0x0000000e },
	{ 0x15, 0, 3, 0x00001389 },
	{ 0x48, 0, 0, 0x00000010 },
	{ 0x15, 0, 1, 0x0000138a },
	{ 0x6, 0, 0, 0x00000000 },
	{ 0x6, 0, 0, 0x00000000 },
    };
    udp_filter[6].k = htonl(srcip);
    udp_filter[8].k = htonl(dstip);
    udp_filter[13].k = htons(srcport);
    udp_filter[15].k = htons(dstport);
    struct sock_fprog bpf = {
	.len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
	.filter = udp_filter,
    };
    return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
}
#if HAVE_IPV6
//
// v6 Connected BPF, use 32 bit values
//
int SockAddr_v6_Connect_BPF (int sock, struct in6_addr *dst, struct in6_addr *src, uint16_t dstport, uint16_t srcport) {
    // Use full quintuple, proto, src ip, dst ip, src port, dst port
    // tcpdump udp and ip6 src fe80::428d:5cff:fe6a:2d85 and ip6 dst fe80::428d:5cff:fe6a:2d86 and src port 5001 and dst port 5002 -dd
    //
    //tcpdump udp and ip6 src fe80::428d:5cff:fe6a:2d85 and ip6 dst fe80::428d:5cff:fe6a:2d86 and src port 5001 and dst port 5002 -d
    //  (000) ldh      [12]
    //  (001) jeq      #0x86dd          jt 2	jf 32
    //  (002) ldb      [20]
    //  (003) jeq      #0x11            jt 7	jf 4
    //  (004) jeq      #0x2c            jt 5	jf 32
    //  (005) ldb      [54]
    //  (006) jeq      #0x11            jt 7	jf 32
    //  (007) ld       [22]
    //  (008) jeq      #0xfe800000      jt 9	jf 32
    //  (009) ld       [26]
    //  (010) jeq      #0x0             jt 11	jf 32
    //  (011) ld       [30]
    //  (012) jeq      #0x428d5cff      jt 13	jf 32
    //  (013) ld       [34]
    //  (014) jeq      #0xfe6a2d85      jt 15	jf 32
    //  (015) ld       [38]
    //  (016) jeq      #0xfe800000      jt 17	jf 32
    //  (017) ld       [42]
    //  (018) jeq      #0x0             jt 19	jf 32
    //  (019) ld       [46]
    //  (020) jeq      #0x428d5cff      jt 21	jf 32
    //  (021) ld       [50]
    //  (022) jeq      #0xfe6a2d86      jt 23	jf 32
    //  (023) ldb      [20]
    //  (024) jeq      #0x84            jt 27	jf 25
    //  (025) jeq      #0x6             jt 27	jf 26
    //  (026) jeq      #0x11            jt 27	jf 32
    //  (027) ldh      [54]
    //  (028) jeq      #0x1389          jt 29	jf 32
    //  (029) ldh      [56]
    //  (030) jeq      #0x138a          jt 31	jf 32
    //  (031) ret      #262144
    //  (032) ret      #0
    //
    struct sock_filter udp_filter[] = {
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 0, 30, 0x000086dd },
	{ 0x30, 0, 0, 0x00000014 },
	{ 0x15, 3, 0, 0x00000011 },
	{ 0x15, 0, 27, 0x0000002c },
	{ 0x30, 0, 0, 0x00000036 },
	{ 0x15, 0, 25, 0x00000011 },
	{ 0x20, 0, 0, 0x00000016 },
	{ 0x15, 0, 23, 0xfe800000 },
	{ 0x20, 0, 0, 0x0000001a },
	{ 0x15, 0, 21, 0x00000000 },
	{ 0x20, 0, 0, 0x0000001e },
	{ 0x15, 0, 19, 0x428d5cff },
	{ 0x20, 0, 0, 0x00000022 },
	{ 0x15, 0, 17, 0xfe6a2d85 },
	{ 0x20, 0, 0, 0x00000026 },
	{ 0x15, 0, 15, 0xfe800000 },
	{ 0x20, 0, 0, 0x0000002a },
	{ 0x15, 0, 13, 0x00000000 },
	{ 0x20, 0, 0, 0x0000002e },
	{ 0x15, 0, 11, 0x428d5cff },
	{ 0x20, 0, 0, 0x00000032 },
	{ 0x15, 0, 9, 0xfe6a2d86 },
	{ 0x30, 0, 0, 0x00000014 },
	{ 0x15, 2, 0, 0x00000084 },
	{ 0x15, 1, 0, 0x00000006 },
	{ 0x15, 0, 5, 0x00000011 },
	{ 0x28, 0, 0, 0x00000036 },
	{ 0x15, 0, 3, 0x00001389 },
	{ 0x28, 0, 0, 0x00000038 },
	{ 0x15, 0, 1, 0x0000138a },
	{ 0x6, 0, 0, 0x00040000 },
	{ 0x6, 0, 0, 0x00000000 },
    };
    udp_filter[8].k = htonl((*src).s6_addr32[0]);
    udp_filter[10].k = htonl((*src).s6_addr32[1]);
    udp_filter[12].k = htonl((*src).s6_addr32[2]);
    udp_filter[14].k = htonl((*src).s6_addr32[3]);
    udp_filter[16].k = htonl((*dst).s6_addr32[0]);
    udp_filter[18].k = htonl((*dst).s6_addr32[1]);
    udp_filter[20].k = htonl((*dst).s6_addr32[2]);
    udp_filter[22].k = htonl((*dst).s6_addr32[3]);
    udp_filter[28].k = htons(srcport);
    udp_filter[30].k = htons(dstport);
    struct sock_fprog bpf = {
	.len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
	.filter = udp_filter,
    };
    return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
}
#  endif // HAVE_V6
#endif // HAVE_LINUX_FILTER

#ifdef __cplusplus
} /* end extern "C" */
#endif
