/* get default route (gateway) IPv4 address of the named network interface/adapter (like "eth0"). This works on both Linux and windows. */ #include "rawsock.h" #include "util-safefunc.h" #include "util-malloc.h" #include "massip-parse.h" #include "util-logger.h" #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun__) #include #include #include #include #include #include #define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) #if defined(__APPLE__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__NetBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) #elif defined(__FreeBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__OpenBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #else # error unknown platform #endif static struct sockaddr * get_rt_address(struct rt_msghdr *rtm, int desired) { int i; int bitmask = rtm->rtm_addrs; struct sockaddr *sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { if (bitmask & (1 << i)) { if ((1<sa_len) + (char *)sa); } else ; } return NULL; } static void hexdump(const void *v, size_t len) { const unsigned char *p = (const unsigned char *)v; size_t i; for (i=0; irtm_addrs; struct sockaddr *sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { if (bitmask & (1 << i)) { printf("b=%u fam=%u len=%u\n", (1<sa_family, sa->sa_len); hexdump(sa, sa->sa_len + sizeof(sa->sa_family)); sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa); } else ; } } int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4) { int fd; int seq = (int)time(0); ssize_t err; struct rt_msghdr *rtm; size_t sizeof_buffer; /* * Requests/responses from the kernel are done with an "rt_msghdr" * structure followed by an array of "sockaddr" structures. */ sizeof_buffer = sizeof(*rtm) + 512; rtm = calloc(1, sizeof_buffer); /* * Create a socket for querying the kernel */ fd = socket(AF_ROUTE, SOCK_RAW, 0); if (fd < 0) { perror("socket(AF_ROUTE)"); free(rtm); return errno; } /* Needs a timeout. Sometimes it'll hang indefinitely waiting for a * response that will never arrive */ { struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); if (err < 0) LOG(0, "[-] SO_RCVTIMEO: %d %s\n", errno, strerror(errno)); err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); if (err < 0) LOG(0, "[-] SO_SNDTIMEO: %d %s\n", errno, strerror(errno)); } /* * Format and send request to kernel */ rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in); rtm->rtm_version = RTM_VERSION; rtm->rtm_flags = RTF_UP; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = RTA_DST | RTA_IFP; rtm->rtm_pid = getpid(); rtm->rtm_seq = seq; /* * Create an empty address of 0.0.0.0 to query the route */ { struct sockaddr_in *sin; sin = (struct sockaddr_in *)(rtm + 1); sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; sin->sin_addr.s_addr = 0; } err = write(fd, (char *)rtm, rtm->rtm_msglen); if (err <= 0) { LOG(0, "[-] getroute: write(): returned %d %s\n", errno, strerror(errno)); goto fail; } /* * Read responses until we find one that belongs to us */ for (;;) { err = read(fd, (char *)rtm, sizeof_buffer); if (err <= 0) break; if (rtm->rtm_seq != seq) { printf("seq: %u %u\n", rtm->rtm_seq, seq); continue; } if (rtm->rtm_pid != getpid()) { printf("pid: %u %u\n", rtm->rtm_pid, getpid()); continue; } break; } close(fd); /* * Parse our data */ { struct sockaddr_in *sin; struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP); if (sdl) { //hexdump(sdl, sdl->sdl_len); //printf("%.*s\n", sdl->sdl_nlen, sdl->sdl_data); if (memcmp(ifname, sdl->sdl_data, sdl->sdl_nlen) != 0) { fprintf(stderr, "ERROR: ROUTE DOESN'T MATCH INTERFACE\n"); fprintf(stderr, "YOU'LL HAVE TO SET --router-mac MANUALLY\n"); exit(1); } } sin = (struct sockaddr_in *)get_rt_address(rtm, RTA_GATEWAY); if (sin) { *ipv4 = ntohl(sin->sin_addr.s_addr); free(rtm); return 0; } } fail: free(rtm); return -1; } #elif defined(__linux__) #include #include #include #include #include #include #include #include #include #include #include #include #include struct route_info { struct in_addr dstAddr; struct in_addr srcAddr; struct in_addr gateWay; char ifName[IF_NAMESIZE]; }; static int read_netlink(int fd, char *bufPtr, size_t sizeof_buffer, int seqNum, int pId) { struct nlmsghdr *nlHdr; int readLen = 0, msgLen = 0; do { /* Receive response from the kernel */ if ((readLen = recv(fd, bufPtr, sizeof_buffer - msgLen, 0)) < 0) { perror("SOCK READ: "); return -1; } nlHdr = (struct nlmsghdr *) bufPtr; /* Check if the header is valid */ if ((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)) { perror("Error in received packet"); return -1; } /* Check if the its the last message */ if (nlHdr->nlmsg_type == NLMSG_DONE) { break; } else { /* Else move the pointer to buffer appropriately */ bufPtr += readLen; msgLen += readLen; } /* Check if its a multi part message */ if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) { /* return if its not */ break; } } while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId)); return msgLen; } /* For parsing the route info returned */ static int parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo) { struct rtmsg *rtMsg; struct rtattr *rtAttr; int rtLen = 0; rtMsg = (struct rtmsg *) NLMSG_DATA(nlHdr); /* This must be an IPv4 (AF_INET) route */ if (rtMsg->rtm_family != AF_INET) return 1; /* This must be in main routing table */ if (rtMsg->rtm_table != RT_TABLE_MAIN) return 1; /* Attributes field*/ rtAttr = (struct rtattr *)RTM_RTA(rtMsg); rtLen = RTM_PAYLOAD(nlHdr); for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) { switch (rtAttr->rta_type) { case RTA_OIF: if_indextoname(*(int *) RTA_DATA(rtAttr), rtInfo->ifName); break; case RTA_GATEWAY: rtInfo->gateWay.s_addr = *(u_int *)RTA_DATA(rtAttr); break; case RTA_PREFSRC: rtInfo->srcAddr.s_addr = *(u_int *)RTA_DATA(rtAttr); break; case RTA_DST: rtInfo->dstAddr .s_addr = *(u_int *)RTA_DATA(rtAttr); break; } } return 0; } int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4) { int fd; struct nlmsghdr *nlMsg; char msgBuf[16384]; int len; int msgSeq = 0; /* * Set to zero, in case we cannot find it */ *ipv4 = 0; /* * Create 'netlink' socket to query kernel */ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { fprintf(stderr, "%s:%d: socket(NETLINK_ROUTE): %d\n", __FILE__, __LINE__, errno); return errno; } /* * format the netlink buffer */ memset(msgBuf, 0, sizeof(msgBuf)); nlMsg = (struct nlmsghdr *)msgBuf; nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlMsg->nlmsg_type = RTM_GETROUTE; nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; nlMsg->nlmsg_seq = msgSeq++; nlMsg->nlmsg_pid = getpid(); /* * send first request to kernel */ if (send(fd, nlMsg, nlMsg->nlmsg_len, 0) < 0) { fprintf(stderr, "%s:%d: send(NETLINK_ROUTE): %d\n", __FILE__, __LINE__, errno); return errno; } /* * Now read all the responses */ len = read_netlink(fd, msgBuf, sizeof(msgBuf), msgSeq, getpid()); if (len <= 0) { fprintf(stderr, "%s:%d: read_netlink: %d\n", __FILE__, __LINE__, errno); return errno; } /* * Parse the response */ for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) { struct route_info rtInfo[1]; int err; memset(rtInfo, 0, sizeof(struct route_info)); err = parseRoutes(nlMsg, rtInfo); if (err != 0) continue; /* make sure we match the desired network interface */ if (ifname && strcmp(rtInfo->ifName, ifname) != 0) continue; /* make sure destination = 0.0.0.0 for "default route" */ if (rtInfo->dstAddr.s_addr != 0) continue; /* found the gateway! */ *ipv4 = ntohl(rtInfo->gateWay.s_addr); } close(fd); return 0; } #endif #if defined(WIN32) /* From: * https://stackoverflow.com/questions/10972794/undefined-reference-to-getadaptersaddresses20-but-i-included-liphlpapi * I think this fixes issue #734 */ #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #include #include #ifdef _MSC_VER #pragma comment(lib, "IPHLPAPI.lib") #endif int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4) { PIP_ADAPTER_INFO pAdapterInfo; PIP_ADAPTER_INFO pAdapter = NULL; DWORD err; ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); /* * Translate numeric index (if it exists) to real name */ ifname = rawsock_win_name(ifname); //printf("------ %s -----\n", ifname); /* * Allocate a proper sized buffer */ pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO)); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } /* * Query the adapter info. If the buffer is not big enough, loop around * and try again */ again: err = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); if (err == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } goto again; } if (err != NO_ERROR) { fprintf(stderr, "GetAdaptersInfo failed with error: %u\n", (unsigned)err); return EFAULT; } /* * loop through all adapters looking for ours */ for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { if (rawsock_is_adapter_names_equal(pAdapter->AdapterName, ifname)) break; } if (pAdapter) { //printf("\tComboIndex: \t%d\n", pAdapter->ComboIndex); //printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName); //printf("\tAdapter Desc: \t%s\n", pAdapter->Description); //printf("\tAdapter Addr: \t"); /*for (i = 0; i < pAdapter->AddressLength; i++) { if (i == (pAdapter->AddressLength - 1)) printf("%.2X\n", (int) pAdapter->Address[i]); else printf("%.2X-", (int) pAdapter->Address[i]); }*/ //printf("\tIndex: \t%d\n", pAdapter->Index); //printf("\tType: \t"); switch (pAdapter->Type) { case MIB_IF_TYPE_OTHER: //printf("Other\n"); break; case MIB_IF_TYPE_ETHERNET: //printf("Ethernet\n"); break; case MIB_IF_TYPE_TOKENRING: //printf("Token Ring\n"); break; case MIB_IF_TYPE_FDDI: //printf("FDDI\n"); break; case MIB_IF_TYPE_PPP: //printf("PPP\n"); break; case MIB_IF_TYPE_LOOPBACK: //printf("Lookback\n"); break; case MIB_IF_TYPE_SLIP: //printf("Slip\n"); break; default: //printf("Unknown type %ld\n", pAdapter->Type); break; } //printf("\tIP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String); //printf("\tIP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String); /*typedef struct _IP_ADDR_STRING { struct _IP_ADDR_STRING* Next; IP_ADDRESS_STRING IpAddress; IP_MASK_STRING IpMask; DWORD Context; } IP_ADDR_STRING, *PIP_ADDR_STRING;*/ { const IP_ADDR_STRING *addr; for (addr = &pAdapter->GatewayList; addr; addr = addr->Next) { unsigned x = massip_parse_ipv4(addr->IpAddress.String); if (x != 0xFFFFFFFF) { *ipv4 = x; goto end; } } } //printf("\n"); } end: if (pAdapterInfo) free(pAdapterInfo); return 0; } #endif