diff options
Diffstat (limited to '')
-rw-r--r-- | libnetutil/netutil.cc | 4777 |
1 files changed, 4777 insertions, 0 deletions
diff --git a/libnetutil/netutil.cc b/libnetutil/netutil.cc new file mode 100644 index 0000000..ff7279e --- /dev/null +++ b/libnetutil/netutil.cc @@ -0,0 +1,4777 @@ +/*************************************************************************** + * netutil.cc * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * + * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap + * Project"). Nmap is also a registered trademark of the Nmap Project. + * + * This program is distributed under the terms of the Nmap Public Source + * License (NPSL). The exact license text applying to a particular Nmap + * release or source code control revision is contained in the LICENSE + * file distributed with that version of Nmap or source code control + * revision. More Nmap copyright/legal information is available from + * https://nmap.org/book/man-legal.html, and further information on the + * NPSL license itself can be found at https://nmap.org/npsl/ . This + * header summarizes some key points from the Nmap license, but is no + * substitute for the actual license text. + * + * Nmap is generally free for end users to download and use themselves, + * including commercial use. It is available from https://nmap.org. + * + * The Nmap license generally prohibits companies from using and + * redistributing Nmap in commercial products, but we sell a special Nmap + * OEM Edition with a more permissive license and special features for + * this purpose. See https://nmap.org/oem/ + * + * If you have received a written Nmap license agreement or contract + * stating terms other than these (such as an Nmap OEM license), you may + * choose to use and redistribute Nmap under those terms instead. + * + * The official Nmap Windows builds include the Npcap software + * (https://npcap.com) for packet capture and transmission. It is under + * separate license terms which forbid redistribution without special + * permission. So the official Nmap Windows builds may not be redistributed + * without special permission (such as an Nmap OEM license). + * + * Source is provided to this software because we believe users have a + * right to know exactly what a program is going to do before they run it. + * This also allows you to audit the software for security holes. + * + * Source code also allows you to port Nmap to new platforms, fix bugs, and add + * new features. You are highly encouraged to submit your changes as a Github PR + * or by email to the dev@nmap.org mailing list for possible incorporation into + * the main distribution. Unless you specify otherwise, it is understood that + * you are offering us very broad rights to use your submissions as described in + * the Nmap Public Source License Contributor Agreement. This is important + * because we fund the project by selling licenses with various terms, and also + * because the inability to relicense code has caused devastating problems for + * other Free Software projects (such as KDE and NASM). + * + * The free version of Nmap is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, + * indemnification and commercial support are all available through the + * Npcap OEM program--see https://nmap.org/oem/ + * + ***************************************************************************/ + +/* Since OS X 10.7, we must declare whether we expect RFC 2292 or RFC 3542 + behavior from <netinet6/in6.h>. */ +#define __APPLE_USE_RFC_3542 + +#if HAVE_CONFIG_H +#include "../nmap_config.h" +#endif + +#include "nbase.h" + +#ifndef WIN32 +#include <sys/uio.h> +#include <sys/ioctl.h> +#endif + +#include <assert.h> +#include <errno.h> +#include <sys/types.h> +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#if HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> /* SIOCGIFCONF for Solaris */ +#endif + +/* Define CMSG_* symbols for Solaris 9 and earlier. See + http://wiki.opencsw.org/porting-faq#toc10. */ +#if defined(__sun) || defined(__sun__) +# ifndef CMSG_ALIGN +# ifdef __sun__ +# define CMSG_ALIGN(len) _CMSG_DATA_ALIGN (len) +# else + /* aligning to sizeof (long) is assumed to be portable (fd.o#40235) */ +# define CMSG_ALIGN(len) (((len) + sizeof (long) - 1) & ~(sizeof (long) - 1)) +# endif +# endif +# ifndef CMSG_SPACE +# define CMSG_SPACE(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + CMSG_ALIGN (len)) +# endif +# ifndef CMSG_LEN +# define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +# endif +#endif /* Solaris */ + +#ifdef WIN32 +typedef unsigned __int32 u_int32_t; +typedef unsigned __int16 u_int16_t; +typedef unsigned __int8 u_int8_t; +#endif + +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_LINUX_RTNETLINK_H +#include <linux/rtnetlink.h> +#endif + +#ifndef NETINET_IN_SYSTM_H /* This guarding is needed for at least some versions of OpenBSD */ +#include <netinet/in_systm.h> +#define NETINET_IN_SYSTM_H +#endif + +#include "netutil.h" + +#if HAVE_NET_IF_H +#ifndef NET_IF_H /* This guarding is needed for at least some versions of OpenBSD */ +#include <net/if.h> +#define NET_IF_H +#endif +#endif +#ifndef NETINET_IP_H /* This guarding is needed for at least some versions of OpenBSD */ +#include <netinet/ip.h> +#define NETINET_IP_H +#endif +#include <net/if_arp.h> + +#if HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + +#define NBASE_MAX_ERR_STR_LEN 1024 /* Max length of an error message */ + +#ifndef PCAP_NETMASK_UNKNOWN +/* libpcap before 1.1.1 (e.g. WinPcap) doesn't handle this specially, so just use 0 netmask */ +#define PCAP_NETMASK_UNKNOWN 0 +#endif + +/** Print fatal error messages to stderr and then exits. A newline + character is printed automatically after the supplied text. + * @warning This function does not return because it calls exit() */ +void netutil_fatal(const char *str, ...){ + va_list list; + char errstr[NBASE_MAX_ERR_STR_LEN]; + memset(errstr,0, NBASE_MAX_ERR_STR_LEN); + + va_start(list, str); + + fflush(stdout); + + /* Print error msg to strerr */ + vfprintf(stderr, str, list); + fprintf(stderr,"\n"); + va_end(list); + + exit(EXIT_FAILURE); +} /* End of fatal() */ + +/** Print error messages to stderr and then return. A newline + character is printed automatically after the supplied text.*/ +int netutil_error(const char *str, ...){ + va_list list; + char errstr[NBASE_MAX_ERR_STR_LEN]; + memset(errstr,0, NBASE_MAX_ERR_STR_LEN); + + va_start(list, str); + + fflush(stdout); + + /* Print error msg to strerr */ + vfprintf(stderr, str, list); + fprintf(stderr,"\n"); + va_end(list); + + return 0; + +} /* End of error() */ + +/* This function converts zero-terminated 'txt' string to binary 'data'. + It is used to parse user input for ip options. Some examples of possible input + strings and results: + '\x01*2\xA2' -> [0x01,0x01,0xA2] // with 'x' number is parsed in hex + '\01\01\255' -> [0x01,0x01,0xFF] // without 'x' its in decimal + '\x01\x00*2' -> [0x01,0x00,0x00] // '*' is copying char + 'R' -> Record Route with 9 slots + 'S 192.168.0.1 172.16.0.1' -> Strict Route with 2 slots + 'L 192.168.0.1 172.16.0.1' -> Loose Route with 2 slots + 'T' -> Record Timestamp with 9 slots + 'U' -> Record Timestamp and Ip Address with 4 slots + On success, the function returns the length of the final binary + options stored in "data". In case of error, OP_FAILURE is returned + and the "errstr" buffer is filled with an error message + (unless it's NULL). Note that the returned error message does NOT + contain a newline character at the end. */ +int parse_ip_options(const char *txt, u8 *data, int datalen, int* firsthopoff, int* lasthopoff, char *errstr, size_t errstrlen){ + enum{ + NONE = 0, + SLASH = 1, + MUL = 2, + RR = 3, + TIME = 4, + } s = NONE; + char *n, lc; + const char *c = txt; + u8 *d = data; + int i,j; + int base = 10; + u8 *dataend = &data[datalen]; + u8 *len = NULL; + char buf[32]; + memset(data, 0, datalen); + int sourcerouting = 0; + long strtolbyte = 0; // used to check strtol() return boundaries + + for(;*c;c++){ + switch(s){ + case SLASH: + // parse \x00 string + if(*c == 'x'){// just ignore this char + base = 16; + break; + } + if(isxdigit(*c)){ + strtolbyte = strtol(c, &n, base); + if((strtolbyte < 0) || (strtolbyte > 255)){ + if(errstr) Snprintf(errstr, errstrlen, "invalid ipv4 address format"); + return OP_FAILURE; + } + *d++ = (u8) strtolbyte; + c = n-1; + }else{ + if(errstr) Snprintf(errstr, errstrlen, "not a digit after '\\'"); + return OP_FAILURE; + } + s = NONE; + break; + case MUL: + if(d==data){ + if(errstr) Snprintf(errstr, errstrlen, "nothing before '*' char"); + return OP_FAILURE; + } + i = strtol(c, &n, 10); + if(i<2){ + if(errstr) Snprintf(errstr, errstrlen, "bad number after '*'"); + return OP_FAILURE; + } + c = n-1; // move current txt pointer + lc = *(d-1); // last char, we'll copy this + for(j=1; j<i; j++){ + *d++ = lc; + if(d == dataend) // check for overflow + goto after; + } + s = NONE; + break; + case RR: + if(*c==' ' || *c==',') + break; + n = buf; + while((*c=='.' || (*c>='0' && *c<='9')) && n-buf <= ((int)sizeof(buf)-1)) + *n++ = *c++; + *n = '\0'; c--; + if(d+4>=dataend){ + if(errstr) Snprintf(errstr, errstrlen, "Buffer too small. Or input data too big :)"); + return OP_FAILURE; + } + i = inet_pton(AF_INET, buf, d); + if(i<1){ + if(errstr) Snprintf(errstr, errstrlen, "Not a valid ipv4 address '%s'",buf); + return OP_FAILURE; + } + // remember offset of first hop + if(sourcerouting && !*firsthopoff) + *firsthopoff = d - data; + d+=4; + if(*len<37) + *len += 4; + break; + case TIME: + if(errstr) Snprintf(errstr, errstrlen, "No more arguments allowed!"); + return OP_FAILURE; + default: + switch(*c){ + case '\\':s = SLASH;base=10;break; + case '*':s = MUL;break; + case 'R': + case 'S': + case 'L': + if(d != data){ + if(errstr) Snprintf(errstr, errstrlen, "This option can't be used in that way"); + return OP_FAILURE; + } + *d++ = '\x01';//NOP + switch(*c){ + case 'R':*d++ = 7;break; + case 'S':*d++ = 137; sourcerouting=1; break; + case 'L':*d++ = 131; sourcerouting=1; break; + } + len = d; + *d++ = (*c=='R')? 39 : 3; // length: 3+4*9 bytes + *d++ = 4; //pointer + s = RR; + break; + case 'T': + case 'U': + if(d != data){ + if(errstr) Snprintf(errstr, errstrlen, "This option can't be used in that way"); + return OP_FAILURE; + } + *d++ = 68; // option type + len = d; + *d++ = (*c=='U') ? 36 : 40; // length: 3+4*9 bytes or 4+4*9 bytes + *d++ = 5; // pointer + *d++ = (*c=='U') ? 1 : 0; // flag: address and Time fields + s = TIME; + break; + default://*d++ = *c; + if(errstr) Snprintf(errstr, errstrlen, "Bad character in ip option '%c'",*c); + return OP_FAILURE; + } + } + if(d == dataend) + break; + assert(d<dataend); + } + if(sourcerouting){ + if(*len<37){ + *len+=4; + *lasthopoff = d - data; + *d++ = 0;*d++ = 0;*d++ = 0;*d++ = 0; + }else{ + if(errstr) Snprintf(errstr, errstrlen, "When using source routing you must leave at least one slot for target's ip."); + return OP_FAILURE; + } + } + if(s == RR) + return(*len+1); // because we inject NOP before + if(s == TIME) + return(*len); +after: + return(d - data); +} + +/* Internal helper for resolve and resolve_numeric. addl_flags is ored into + hints.ai_flags, so you can add AI_NUMERICHOST. */ +static int resolve_internal(const char *hostname, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af, int addl_flags) { + struct addrinfo hints; + struct addrinfo *result; + char portbuf[16]; + char *servname = NULL; + int rc; + + assert(hostname); + assert(ss); + assert(sslen); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags |= addl_flags; + + /* Make the port number a string to give to getaddrinfo. */ + if (port != 0) { + rc = Snprintf(portbuf, sizeof(portbuf), "%hu", port); + assert(rc >= 0 && (size_t) rc < sizeof(portbuf)); + servname = portbuf; + } + + rc = getaddrinfo(hostname, servname, &hints, &result); + if (rc != 0) + return rc; + if (result == NULL) + return EAI_NONAME; + assert(result->ai_addrlen > 0 && result->ai_addrlen <= (int) sizeof(struct sockaddr_storage)); + *sslen = result->ai_addrlen; + memcpy(ss, result->ai_addr, *sslen); + freeaddrinfo(result); + + return 0; +} + +/* Resolves the given hostname or IP address with getaddrinfo, and stores the + first result (if any) in *ss and *sslen. The value of port will be set in the + appropriate place in *ss; set to 0 if you don't care. af may be AF_UNSPEC, in + which case getaddrinfo may return e.g. both IPv4 and IPv6 results; which one + is first depends on the system configuration. Returns 0 on success, or a + getaddrinfo return code (suitable for passing to gai_strerror) on failure. + *ss and *sslen are always defined when this function returns 0. */ +int resolve(const char *hostname, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af) { + return resolve_internal(hostname, port, ss, sslen, af, 0); +} + +/* As resolve, but do not do DNS resolution of hostnames; the first argument + must be the string representation of a numeric IP address. */ +int resolve_numeric(const char *ip, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af) { + return resolve_internal(ip, port, ss, sslen, af, AI_NUMERICHOST); +} + +/* + * Returns 1 if this is a reserved IP address, where "reserved" means + * either a private address, non-routable address, or even a non-reserved + * but unassigned address which has an extremely high probability of being + * black-holed. + * + * We try to optimize speed when ordering the tests. This optimization + * assumes that all byte values are equally likely in the input. + * + * Check + * <http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.txt> + * for the most recent assigments and + * <http://www.cymru.com/Documents/bogon-bn-nonagg.txt> for bogon + * netblocks. + */ +int ip_is_reserved(struct in_addr *ip) +{ + char *ipc = (char *) &(ip->s_addr); + unsigned char i1 = ipc[0], i2 = ipc[1], i3 = ipc[2]; /* i4 not currently used - , i4 = ipc[3]; */ + + /* do all the /7's and /8's with a big switch statement, hopefully the + * compiler will be able to optimize this a little better using a jump table + * or what have you + */ + switch (i1) + { + case 0: /* 000/8 is IANA reserved */ + case 10: /* the infamous 10.0.0.0/8 */ + case 127: /* 127/8 is reserved for loopback */ + return 1; + default: + break; + } + + /* 172.16.0.0/12 is reserved for private nets by RFC1918 */ + if (i1 == 172 && i2 >= 16 && i2 <= 31) + return 1; + + /* 192.0.2.0/24 is reserved for documentation and examples (RFC5737) */ + /* 192.88.99.0/24 is used as 6to4 Relay anycast prefix by RFC3068 */ + /* 192.168.0.0/16 is reserved for private nets by RFC1918 */ + if (i1 == 192) { + if (i2 == 0 && i3 == 2) + return 1; + if (i2 == 88 && i3 == 99) + return 1; + if (i2 == 168) + return 1; + } + + /* 198.18.0.0/15 is used for benchmark tests by RFC2544 */ + /* 198.51.100.0/24 is reserved for documentation (RFC5737) */ + if (i1 == 198) { + if (i2 == 18 || i2 == 19) + return 1; + if (i2 == 51 && i3 == 100) + return 1; + } + + /* 169.254.0.0/16 is reserved for DHCP clients seeking addresses - RFC3927 */ + if (i1 == 169 && i2 == 254) + return 1; + + /* 203.0.113.0/24 is reserved for documentation (RFC5737) */ + if (i1 == 203 && i2 == 0 && i3 == 113) + return 1; + + /* 224-239/8 is all multicast stuff */ + /* 240-255/8 is IANA reserved */ + if (i1 >= 224) + return 1; + + return 0; +} + +/* A trivial functon that maintains a cache of IP to MAC Address + entries. If the command is MACCACHE_GET, this func looks for the + IPv4 address in ss and fills in the 'mac' parameter and returns + true if it is found. Otherwise (not found), the function returns + false. If the command is MACCACHE_SET, the function adds an entry + with the given ip (ss) and mac address. An existing entry for the + IP ss will be overwritten with the new MAC address. true is always + returned for the set command. */ +#define MACCACHE_GET 1 +#define MACCACHE_SET 2 +static int do_mac_cache(int command, const struct sockaddr_storage *ss, u8 *mac) { + struct MacCache { + struct sockaddr_storage ip; + u8 mac[6]; + }; + static struct MacCache *Cache = NULL; + static int MacCapacity = 0; + static int MacCacheSz = 0; + int i; + + if (command == MACCACHE_GET) { + for (i = 0; i < MacCacheSz; i++) { + if (sockaddr_storage_cmp(&Cache[i].ip, ss) == 0) { + memcpy(mac, Cache[i].mac, 6); + return 1; + } + } + return 0; + } + assert(command == MACCACHE_SET); + if (MacCacheSz == MacCapacity) { + if (MacCapacity == 0) + MacCapacity = 32; + else + MacCapacity <<= 2; + Cache = (struct MacCache *) safe_realloc(Cache, MacCapacity * sizeof(struct MacCache)); + } + + /* Ensure that it isn't already there ... */ + for (i = 0; i < MacCacheSz; i++) { + if (sockaddr_storage_cmp(&Cache[i].ip, ss) == 0) { + memcpy(Cache[i].mac, mac, 6); + return 1; + } + } + + /* Add it to the end of the list */ + memcpy(&Cache[i].ip, ss, sizeof(struct sockaddr_storage)); + memcpy(Cache[i].mac, mac, 6); + MacCacheSz++; + return 1; +} + +/* A couple of trivial functions that maintain a cache of IP to MAC + * Address entries. Function mac_cache_get() looks for the IPv4 address + * in ss and fills in the 'mac' parameter and returns true if it is + * found. Otherwise (not found), the function returns false. + * Function mac_cache_set() adds an entry with the given ip (ss) and + * mac address. An existing entry for the IP ss will be overwritten + * with the new MAC address. mac_cache_set() always returns true. + * WARNING: The caller must ensure that the supplied "ss" is of family + * AF_INET. Otherwise the function will return 0 and there would be + * no way for the caller to tell tell the difference between an error + * or a cache miss.*/ +int mac_cache_get(const struct sockaddr_storage *ss, u8 *mac){ + return do_mac_cache(MACCACHE_GET, ss, mac); +} +int mac_cache_set(const struct sockaddr_storage *ss, u8 *mac){ + return do_mac_cache(MACCACHE_SET, ss, mac); +} + +/* Standard BSD internet checksum routine. Uses libdnet helper functions. */ +unsigned short in_cksum(u16 *ptr,int nbytes) { + int sum; + + sum = ip_cksum_add(ptr, nbytes, 0); + + return ip_cksum_carry(sum); + + return 0; +} + + +/* Return true iff this Next Header type is an extension header we must skip to + get to the upper-layer header. Types for which neither this function nor + ipv6_is_upperlayer return true are unknown and could be either. */ +static int ipv6_is_extension_header(u8 type) +{ + switch (type) { + case IP_PROTO_HOPOPTS: + case IP_PROTO_DSTOPTS: + case IP_PROTO_ROUTING: + case IP_PROTO_FRAGMENT: + /* + case IP_PROTO_ESP: + case IP_PROTO_AH: + */ + return 1; + default: + return 0; + } +} + +/* Return true iff this Next Header type is a known upper-layer protocol, one + that isn't followed by any more headers. Types for which neither this + function nor ipv6_is_upperlayer return true are unknown and could be + either. */ +static int ipv6_is_upperlayer(u8 type) +{ + switch (type) { + case IP_PROTO_NONE: + case IP_PROTO_TCP: + case IP_PROTO_UDP: + case IP_PROTO_ICMP: + case IP_PROTO_ICMPV6: + case IP_PROTO_SCTP: + return 1; + default: + return 0; + } +} + +/* upperlayer_only controls whether we require a known upper-layer protocol at + the end of the chain, or return the last readable header even if it is not an + upper-layer protocol (may even be another extension header). */ +static const void *ipv6_get_data_primitive(const struct ip6_hdr *ip6, + unsigned int *len, u8 *nxt, bool upperlayer_only) +{ + const unsigned char *p, *end; + + if (*len < sizeof(*ip6)) + return NULL; + + p = (unsigned char *) ip6; + end = p + *len; + + *nxt = ip6->ip6_nxt; + p += sizeof(*ip6); + while (p < end && ipv6_is_extension_header(*nxt)) { + if (p + 2 > end) + return NULL; + *nxt = *p; + p += (*(p + 1) + 1) * 8; + } + + *len = end - p; + if (upperlayer_only && !ipv6_is_upperlayer(*nxt)) + return NULL; + + return (char *) p; +} + +static const void *ip_get_data_primitive(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr, bool upperlayer_only) { + const struct ip *ip; + + ip = (struct ip *) packet; + if (*len >= 20 && ip->ip_v == 4) { + struct sockaddr_in *sin; + + hdr->version = 4; + + sin = (struct sockaddr_in *) &hdr->src; + memset(&hdr->src, 0, sizeof(hdr->src)); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_src.s_addr; + + sin = (struct sockaddr_in *) &hdr->dst; + memset(&hdr->dst, 0, sizeof(hdr->dst)); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_dst.s_addr; + + hdr->proto = ip->ip_p; + hdr->ttl = ip->ip_ttl; + hdr->ipid = ntohs(ip->ip_id); + return ipv4_get_data(ip, len); + } else if (*len >= 40 && ip->ip_v == 6) { + const struct ip6_hdr *ip6 = (struct ip6_hdr *) ip; + struct sockaddr_in6 *sin6; + + hdr->version = 6; + + sin6 = (struct sockaddr_in6 *) &hdr->src; + memset(&hdr->src, 0, sizeof(hdr->src)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, &ip6->ip6_src, IP6_ADDR_LEN); + + sin6 = (struct sockaddr_in6 *) &hdr->dst; + memset(&hdr->dst, 0, sizeof(hdr->dst)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, &ip6->ip6_dst, IP6_ADDR_LEN); + + hdr->ttl = ip6->ip6_hlim; + hdr->ipid = ntohl(ip6->ip6_flow & IP6_FLOWLABEL_MASK); + return ipv6_get_data_primitive(ip6, len, &hdr->proto, upperlayer_only); + } + + return NULL; +} + +/* Find the beginning of the data payload in the IP packet beginning at packet. + Returns the beginning of the payload, updates *len to be the length of the + payload, and fills in hdr if successful. Otherwise returns NULL and *hdr is + undefined. */ +const void *ip_get_data(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr) { + return ip_get_data_primitive(packet, len, hdr, true); +} + +/* As ip_get_data, except that it doesn't insist that the payload be a known + upper-layer protocol. This can matter in IPv6 where the last element of a nh + chain may be a protocol we don't know about. */ +const void *ip_get_data_any(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr) { + return ip_get_data_primitive(packet, len, hdr, false); +} + +/* Get the upper-layer protocol from an IPv4 packet. */ +const void *ipv4_get_data(const struct ip *ip, unsigned int *len) +{ + unsigned int header_len; + + if (*len < 20) + return NULL; + header_len = ip->ip_hl * 4; + if (header_len < sizeof(*ip)) + return NULL; + if (header_len > *len) + return NULL; + *len -= header_len; + + return (char *) ip + header_len; +} + +/* Get the upper-layer protocol from an IPv6 packet. This skips over known + extension headers. The length of the upper-layer payload is stored in *len. + The protocol is stored in *nxt. Returns NULL in case of error. */ +const void *ipv6_get_data(const struct ip6_hdr *ip6, unsigned int *len, u8 *nxt) +{ + return ipv6_get_data_primitive(ip6, len, nxt, true); +} + +/* Get the protocol payload from an IPv6 packet. This skips over known extension + headers. It differs from ipv6_get_data in that it will return a result even + if the final header is not a known upper-layer protocol. */ +const void *ipv6_get_data_any(const struct ip6_hdr *ip6, unsigned int *len, u8 *nxt) +{ + return ipv6_get_data_primitive(ip6, len, nxt, false); +} + +const void *icmp_get_data(const struct icmp_hdr *icmp, unsigned int *len) +{ + unsigned int header_len; + + if (icmp->icmp_type == ICMP_TIMEXCEED || icmp->icmp_type == ICMP_UNREACH) + header_len = 8; + else + netutil_fatal("%s passed ICMP packet with unhandled type %d", __func__, icmp->icmp_type); + if (header_len > *len) + return NULL; + *len -= header_len; + + return (char *) icmp + header_len; +} + +const void *icmpv6_get_data(const struct icmpv6_hdr *icmpv6, unsigned int *len) +{ + unsigned int header_len; + + if (icmpv6->icmpv6_type == ICMPV6_TIMEXCEED || icmpv6->icmpv6_type == ICMPV6_UNREACH) + header_len = 8; + else + netutil_fatal("%s passed ICMPv6 packet with unhandled type %d", __func__, icmpv6->icmpv6_type); + if (header_len > *len) + return NULL; + *len -= header_len; + + return (char *) icmpv6 + header_len; +} + + +/* Calculate the Internet checksum of some given data concatentated with the + IPv4 pseudo-header. See RFC 1071 and TCP/IP Illustrated sections 3.2, 11.3, + and 17.3. */ +unsigned short ipv4_pseudoheader_cksum(const struct in_addr *src, + const struct in_addr *dst, u8 proto, u16 len, const void *hstart) { + struct pseudo { + struct in_addr src; + struct in_addr dst; + u8 zero; + u8 proto; + u16 length; + } hdr; + int sum; + + hdr.src = *src; + hdr.dst = *dst; + hdr.zero = 0; + hdr.proto = proto; + hdr.length = htons(len); + + /* Get the ones'-complement sum of the pseudo-header. */ + sum = ip_cksum_add(&hdr, sizeof(hdr), 0); + /* Add it to the sum of the packet. */ + sum = ip_cksum_add(hstart, len, sum); + + /* Fold in the carry, take the complement, and return. */ + sum = ip_cksum_carry(sum); + /* RFC 768: "If the computed checksum is zero, it is transmitted as all + * ones (the equivalent in one's complement arithmetic). An all zero + * transmitted checksum value means that the transmitter generated no + * checksum" */ + if (proto == IP_PROTO_UDP && sum == 0) + sum = 0xFFFF; + + return sum; +} + +/* Calculate the Internet checksum of some given data concatenated with the + IPv6 pseudo-header. See RFC 2460 section 8.1. */ +u16 ipv6_pseudoheader_cksum(const struct in6_addr *src, + const struct in6_addr *dst, u8 nxt, u32 len, const void *hstart) { + struct { + struct in6_addr src; + struct in6_addr dst; + u32 length; + u8 z0, z1, z2; + u8 nxt; + } hdr; + int sum; + + hdr.src = *src; + hdr.dst = *dst; + hdr.z0 = hdr.z1 = hdr.z2 = 0; + hdr.length = htonl(len); + hdr.nxt = nxt; + + sum = ip_cksum_add(&hdr, sizeof(hdr), 0); + sum = ip_cksum_add(hstart, len, sum); + sum = ip_cksum_carry(sum); + /* RFC 2460: "Unlike IPv4, when UDP packets are originated by an IPv6 node, + the UDP checksum is not optional. That is, whenever originating a UDP + packet, an IPv6 node must compute a UDP checksum over the packet and the + pseudo-header, and, if that computation yields a result of zero, it must be + changed to hex FFFF for placement in the UDP header." */ + if (nxt == IP_PROTO_UDP && sum == 0) + sum = 0xFFFF; + + return sum; +} + +void sethdrinclude(int sd) { +#ifdef IP_HDRINCL + int one = 1; + setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (const char *) &one, sizeof(one)); +#endif +} + +void set_ipoptions(int sd, void *opts, size_t optslen) { +#ifdef IP_OPTIONS + if (sd == -1) + return; + + setsockopt(sd, IPPROTO_IP, IP_OPTIONS, (const char *) opts, optslen); +#endif +} + +void set_ttl(int sd, int ttl) { +#ifdef IP_TTL + if (sd == -1) + return; + + setsockopt(sd, IPPROTO_IP, IP_TTL, (const char *) &ttl, sizeof ttl); +#endif +} + +/* Other than WIN32, what these systems have in common is that they use BPF for + packet capture. (Solaris 10 and earlier used DLPI and had valid selectable + fds.) */ +#if defined(WIN32) || defined(MACOSX) || (defined(FREEBSD) && (__FreeBSD_version < 500000)) || defined(SOLARIS_BPF_PCAP_CAPTURE) || defined(OPENBSD) +/* Returns whether the system supports pcap_get_selectable_fd() properly */ +int pcap_selectable_fd_valid() { + return 0; +} + +/* Call this instead of pcap_get_selectable_fd directly (or your code + won't compile on Windows). On systems which don't seem to support + the pcap_get_selectable_fd() function properly, returns -1, + otherwise simply calls pcap_selectable_fd and returns the + results. If you just want to test whether the function is supported, + use pcap_selectable_fd_valid() instead. */ +int my_pcap_get_selectable_fd(pcap_t *p) { + return -1; +} +#else +int pcap_selectable_fd_valid() { + return 1; +} +int my_pcap_get_selectable_fd(pcap_t *p) { + return pcap_get_selectable_fd(p); +} +#endif + +/* Are we guaranteed to be able to read exactly one frame for each time the pcap + fd is selectable? If not, it's possible for the fd to become selectable, then + for pcap_dispatch to buffer two or more frames, and return only the first one + Because select doesn't know about pcap's buffer, the fd does not become + selectable again, even though another pcap_next_ex would succeed. On these + platforms, we must do a non-blocking read from the fd before doing a select + on the fd. + + It is guaranteed that if pcap_selectable_fd_valid() is false, then so is the + return value of this function. */ +int pcap_selectable_fd_one_to_one() { +#ifdef SOLARIS + return 0; +#endif + return pcap_selectable_fd_valid(); +} + + +/* returns -1 if we can't use select() on the pcap device, 0 for timeout, and + * >0 for success. If select() fails we bail out because it couldn't work with + * the file descriptor we got from my_pcap_get_selectable_fd() + */ +int pcap_select(pcap_t *p, struct timeval *timeout) { + int ret; +#ifdef WIN32 + DWORD msec_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + HANDLE event = pcap_getevent(p); + DWORD result = WaitForSingleObject(event, msec_timeout); + + switch(result) { + case WAIT_OBJECT_0: + ret = 1; + break; + case WAIT_TIMEOUT: + ret = 0; + break; + case WAIT_FAILED: + ret = -1; + netutil_error("%s: WaitForSingleObject failed: %d", __func__, GetLastError()); + break; + default: + ret = -1; + netutil_fatal("%s: WaitForSingleObject returned unknown result: %x", __func__, result); + break; + } + +#else + int fd; + fd_set rfds; + + if ((fd = my_pcap_get_selectable_fd(p)) == -1) + return -1; + + FD_ZERO(&rfds); + checked_fd_set(fd, &rfds); + + do { + errno = 0; + ret = select(fd + 1, &rfds, NULL, NULL, timeout); + if (ret == -1) { + if (errno == EINTR) + netutil_error("%s: %s", __func__, strerror(errno)); + else + netutil_fatal("Your system does not support select()ing on pcap devices (%s). PLEASE REPORT THIS ALONG WITH DETAILED SYSTEM INFORMATION TO THE nmap-dev MAILING LIST!", strerror(errno)); + } + } while (ret == -1); + +#endif + return ret; +} + +int pcap_select(pcap_t *p, long usecs) { + struct timeval tv; + + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + + return pcap_select(p, &tv); +} + + +/* These two are for eth_open_cached() and eth_close_cached() */ +static char etht_cache_device_name[64]; +static eth_t *etht_cache_device = NULL; + +/* A simple function that caches the eth_t from dnet for one device, + to avoid opening, closing, and re-opening it thousands of tims. If + you give a different device, this function will close the first + one. Thus this should never be used by programs that need to deal + with multiple devices at once. In addition, you MUST NEVER + eth_close() A DEVICE OBTAINED FROM THIS FUNCTION. Instead, you can + call eth_close_cached() to close whichever device (if any) is + cached. Returns NULL if it fails to open the device. */ +eth_t *eth_open_cached(const char *device) { + if (!device) + netutil_fatal("%s() called with NULL device name!", __func__); + if (!*device) + netutil_fatal("%s() called with empty device name!", __func__); + + if (strcmp(device, etht_cache_device_name) == 0) { + /* Yay, we have it cached. */ + return etht_cache_device; + } + + if (*etht_cache_device_name) { + eth_close(etht_cache_device); + etht_cache_device_name[0] = '\0'; + etht_cache_device = NULL; + } + + etht_cache_device = eth_open(device); + if (etht_cache_device) + Strncpy(etht_cache_device_name, device, + sizeof(etht_cache_device_name)); + + return etht_cache_device; +} + +/* See the description for eth_open_cached */ +void eth_close_cached() { + if (etht_cache_device) { + eth_close(etht_cache_device); + etht_cache_device = NULL; + etht_cache_device_name[0] = '\0'; + } + return; +} + +/* Takes a protocol number like IPPROTO_TCP, IPPROTO_UDP, IPPROTO_IP, + * etc, and returns an ASCII representation (or the string "unknown" if + * it doesn't recognize the number). If uppercase is non zero, the + * returned value will be in uppercase letters, otherwise it'll be + * in lowercase */ +const char *proto2ascii_case(u8 proto, int uppercase) { + switch (proto) { + + case IPPROTO_TCP: + return uppercase ? "TCP" : "tcp"; + break; + case IPPROTO_UDP: + return uppercase ? "UDP" : "udp"; + break; + case IPPROTO_SCTP: + return uppercase ? "SCTP" : "sctp"; + break; + case IPPROTO_IP: + return uppercase ? "IP" : "ip"; + break; +#ifdef IPPROTO_ICMP + case IPPROTO_ICMP: + return uppercase ? "ICMP" : "icmp"; + break; +#endif +#ifdef IPPROTO_IPV6 + case IPPROTO_IPV6: + return uppercase ? "IPv6" : "ipv6"; + break; +#endif +#ifdef IPPROTO_ICMPV6 + case IPPROTO_ICMPV6: + return uppercase ? "ICMPv6" : "icmpv6"; + break; +#endif +#ifdef IPPROTO_GRE + case IPPROTO_GRE: // Generic Routing Encapsulation + return uppercase ? "GRE" : "gre"; + break; +#endif +#ifdef IPPROTO_ESP + case IPPROTO_ESP: // Encapsulating Security Payload (IPSec) + return uppercase ? "IPSec/ESP" : "ipsec/esp"; + break; +#endif +#ifdef IPPROTO_AH + case IPPROTO_AH: // Authentication Header (IPSec) + return uppercase ? "IPSec/AH" : "ipsec/ah"; + break; +#endif + default: + return uppercase ? "UNKNOWN" : "unknown"; + } + + return NULL; // Unreached +} + +const char *proto2ascii_lowercase(u8 proto) { + return proto2ascii_case(proto, 0); +} +const char *proto2ascii_uppercase(u8 proto) { + return proto2ascii_case(proto, 1); +} + +/* Get an ASCII information about a tcp option which is pointed by + optp, with a length of len. The result is stored in the result + buffer. The result may look like "<mss 1452,sackOK,timestamp + 45848914 0,nop,wscale 7>" */ +void tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize) { + assert(optp); + assert(result); + char *p, ch; + u8 *q; + int opcode; + u16 tmpshort; + u32 tmpword1, tmpword2; + unsigned int i=0; + + p = result; + *p = '\0'; + q = optp; + ch = '<'; + + while (len > 0 && bufsize > 2) { + Snprintf(p, bufsize, "%c", ch); + bufsize--; + p++; + opcode = *q++; + if (!opcode) { /* End of List */ + + Snprintf(p, bufsize, "eol"); + bufsize -= strlen(p); + p += strlen(p); + + len--; + + } else if (opcode == 1) { /* No Op */ + Snprintf(p, bufsize, "nop"); + bufsize -= strlen(p); + p += strlen(p); + + len--; + } else if (opcode == 2) { /* MSS */ + if (len < 4) + break; /* MSS has 4 bytes */ + + q++; + memcpy(&tmpshort, q, 2); + + Snprintf(p, bufsize, "mss %hu", (unsigned short) ntohs(tmpshort)); + bufsize -= strlen(p); + p += strlen(p); + + q += 2; + len -= 4; + } else if (opcode == 3) { /* Window Scale */ + if (len < 3) + break; /* Window Scale option has 3 bytes */ + + q++; + + Snprintf(p, bufsize, "wscale %u", *q); + bufsize -= strlen(p); + p += strlen(p); + + q++; + len -= 3; + } else if (opcode == 4) { /* SACK permitted */ + if (len < 2) + break; /* SACK permitted option has 2 bytes */ + + Snprintf(p, bufsize, "sackOK"); + bufsize -= strlen(p); + p += strlen(p); + + q++; + len -= 2; + } else if (opcode == 5) { /* SACK */ + unsigned sackoptlen = *q; + if ((unsigned) len < sackoptlen) + break; + + /* This would break parsing, so it's best to just give up */ + if (sackoptlen < 2) + break; + + q++; + + if ((sackoptlen - 2) == 0 || ((sackoptlen - 2) % 8 != 0)) { + Snprintf(p, bufsize, "malformed sack"); + bufsize -= strlen(p); + p += strlen(p); + } else { + Snprintf(p, bufsize, "sack %d ", (sackoptlen - 2) / 8); + bufsize -= strlen(p); + p += strlen(p); + for (i = 0; i < sackoptlen - 2; i += 8) { + memcpy(&tmpword1, q + i, 4); + memcpy(&tmpword2, q + i + 4, 4); + Snprintf(p, bufsize, "{%u:%u}", tmpword1, tmpword2); + bufsize -= strlen(p); + p += strlen(p); + } + } + + q += sackoptlen - 2; + len -= sackoptlen; + } else if (opcode == 8) { /* Timestamp */ + if (len < 10) + break; /* Timestamp option has 10 bytes */ + + q++; + memcpy(&tmpword1, q, 4); + memcpy(&tmpword2, q + 4, 4); + + Snprintf(p, bufsize, "timestamp %lu %lu", (unsigned long) ntohl(tmpword1), + (unsigned long) ntohl(tmpword2)); + bufsize -= strlen(p); + p += strlen(p); + + q += 8; + len -= 10; + } + + ch = ','; + } + + if (len > 0) { + *result = '\0'; + return; + } + + Snprintf(p, bufsize, ">"); +} + + + +/* A trivial function used with qsort to sort the routes by netmask and metric */ +static int routecmp(const void *a, const void *b) { + struct sys_route *r1 = (struct sys_route *) a; + struct sys_route *r2 = (struct sys_route *) b; + if (r1->dest.ss_family < r2->dest.ss_family) + return -1; + else if (r1->dest.ss_family > r2->dest.ss_family) + return 1; + + if (r1->netmask_bits < r2->netmask_bits) + return 1; + else if (r1->netmask_bits > r2->netmask_bits) + return -1; + + if (r1->metric < r2->metric) + return -1; + else if (r1->metric > r2->metric) + return 1; + + /* Compare addresses of equal elements to make the sort stable, as suggested + by the Glibc manual. */ + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + + + +/* Convert an address to a string and back again. The first parsing step + eliminates magical OS-specific syntax, for example on OS X, fe80:4::X:X:X:X + becomes "fe80::X:X:X:X" (the "4" in this case is another way of writing the + zone ID, like "%en0"; i.e., in this case en0 is interface number 4). This + must be done before e.g. comparing addresses by netmask. */ +static int canonicalize_address(const struct sockaddr_storage *ss, + struct sockaddr_storage *output) { + char canonical_ip_string[NI_MAXHOST]; + struct addrinfo hints; + struct addrinfo *ai; + int rc; + + /* Convert address to string. */ + rc = getnameinfo((struct sockaddr *) ss, sizeof(*ss), + canonical_ip_string, sizeof(canonical_ip_string), NULL, 0, NI_NUMERICHOST); + if (rc != 0) { + /* Don't care. */ + *output = *ss; + return 0; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ss->ss_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags |= AI_NUMERICHOST; + + rc = getaddrinfo(canonical_ip_string, NULL, &hints, &ai); + if (rc != 0 || ai == NULL) + return -1; + assert(ai->ai_addrlen > 0 && ai->ai_addrlen <= (int) sizeof(*output)); + memcpy(output, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + + return 0; +} + +static int collect_dnet_interfaces(const struct intf_entry *entry, void *arg) { + struct dnet_collector_route_nfo *dcrn = (struct dnet_collector_route_nfo *) arg; + bool primary_done; + unsigned int num_aliases_done; + struct sockaddr_storage tmpss; + int rc; + + primary_done = false; + num_aliases_done = 0; + while (!primary_done || num_aliases_done < entry->intf_alias_num) { + /* Make sure we have room for the new route */ + if (dcrn->numifaces >= dcrn->capacity) { + dcrn->capacity <<= 2; + dcrn->ifaces = (struct interface_info *) safe_realloc(dcrn->ifaces, + dcrn->capacity * sizeof(struct interface_info)); + } + + /* The first time through the loop we add the primary interface record. + After that we add the aliases one at a time. */ + if (!primary_done) { + if ( (addr_ntos(&entry->intf_addr, (struct sockaddr *) &tmpss) == -1) +#ifdef AF_LINK + || (tmpss.ss_family == AF_LINK) +#endif + ) { + dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + } else { + rc = canonicalize_address(&tmpss, &dcrn->ifaces[dcrn->numifaces].addr); + assert(rc == 0); + } + dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_addr.addr_bits; + primary_done = true; + } else if (num_aliases_done < entry->intf_alias_num) { + if ( (addr_ntos(&entry->intf_alias_addrs[num_aliases_done], (struct sockaddr *) &tmpss) == -1) +#ifdef AF_LINK + || (tmpss.ss_family == AF_LINK) +#endif + ) { + dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + } else { + rc = canonicalize_address(&tmpss, &dcrn->ifaces[dcrn->numifaces].addr); + assert(rc == 0); + } + dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_alias_addrs[num_aliases_done].addr_bits; + num_aliases_done++; + } + + /* OK, address/netmask found. Let's get the name */ + Strncpy(dcrn->ifaces[dcrn->numifaces].devname, entry->intf_name, + sizeof(dcrn->ifaces[dcrn->numifaces].devname)); + Strncpy(dcrn->ifaces[dcrn->numifaces].devfullname, entry->intf_name, + sizeof(dcrn->ifaces[dcrn->numifaces].devfullname)); + + /* Interface type */ + if (entry->intf_type == INTF_TYPE_ETH && (entry->intf_flags & INTF_FLAG_NOARP) == 0) { + dcrn->ifaces[dcrn->numifaces].device_type = devt_ethernet; + /* Collect the MAC address since this is ethernet */ + memcpy(dcrn->ifaces[dcrn->numifaces].mac, &entry->intf_link_addr.addr_eth.data, 6); + } else if (entry->intf_type == INTF_TYPE_LOOPBACK) { + dcrn->ifaces[dcrn->numifaces].device_type = devt_loopback; + } else if (entry->intf_type == INTF_TYPE_TUN) { + dcrn->ifaces[dcrn->numifaces].device_type = devt_p2p; + } else { + dcrn->ifaces[dcrn->numifaces].device_type = devt_other; + } + + dcrn->ifaces[dcrn->numifaces].ifindex = entry->intf_index; + + dcrn->ifaces[dcrn->numifaces].mtu = entry->intf_mtu; + + /* Is the interface up and running? */ + dcrn->ifaces[dcrn->numifaces].device_up = (entry->intf_flags & INTF_FLAG_UP) ? true : false; + + /* For the rest of the information, we must open the interface directly ... */ + dcrn->numifaces++; + } + + return 0; +} + +/* Get a list of interfaces using dnet and intf_loop. */ +static struct interface_info *getinterfaces_dnet(int *howmany, char *errstr, size_t errstrlen) { + struct dnet_collector_route_nfo dcrn; + intf_t *it; + + dcrn.routes = NULL; + dcrn.numroutes = 0; + dcrn.numifaces = 0; + + assert(howmany); + + /* Initialize the interface array. */ + dcrn.capacity = 16; + dcrn.ifaces = (struct interface_info *) safe_zalloc(sizeof(struct interface_info) * dcrn.capacity); + + it = intf_open(); + if (!it){ + if(errstr) Snprintf(errstr, errstrlen, "%s: intf_open() failed", __func__); + *howmany=-1; + return NULL; + } + if (intf_loop(it, collect_dnet_interfaces, &dcrn) != 0){ + if(errstr) Snprintf(errstr, errstrlen, "%s: intf_loop() failed", __func__); + *howmany=-1; + return NULL; + } + intf_close(it); + + *howmany = dcrn.numifaces; + return dcrn.ifaces; +} + +static struct interface_info *mydevs = NULL; +/* Returns an allocated array of struct interface_info representing the + available interfaces. The number of interfaces is returned in *howmany. This + function just does caching of results; the real work is done in + getinterfaces_dnet(). + On error, NULL is returned, howmany is set to -1 and the supplied + error buffer "errstr", if not NULL, will contain an error message. */ +struct interface_info *getinterfaces(int *howmany, char *errstr, size_t errstrlen) { + static int numifaces = 0; + + if (mydevs == NULL) { + mydevs = getinterfaces_dnet(&numifaces, errstr, errstrlen); + } + + /* These will propagate any error produced in getinterfaces_xxxx() to + * the caller. */ + if (howmany) + *howmany = numifaces; + return mydevs; +} + +void freeinterfaces(void) { + free(mydevs); + mydevs = NULL; +} + +/* The 'dev' passed in must be at least 32 bytes long. Returns 0 on success. */ +int ipaddr2devname(char *dev, const struct sockaddr_storage *addr) { + struct interface_info *ifaces; + int numifaces; + int i; + + ifaces = getinterfaces(&numifaces, NULL, 0); + + if (ifaces == NULL) + return -1; + + for (i = 0; i < numifaces; i++) { + if (sockaddr_storage_cmp(&ifaces[i].addr, addr) == 0) { + Strncpy(dev, ifaces[i].devname, 32); + return 0; + } + } + + return -1; +} + +int devname2ipaddr(char *dev, struct sockaddr_storage *addr) { + struct interface_info *ifaces; + int numifaces; + int i; + ifaces = getinterfaces(&numifaces, NULL, 0); + + if (ifaces == NULL) + return -1; + + for (i = 0; i < numifaces; i++) { + if (!strcmp(dev, ifaces[i].devfullname)) { + *addr = ifaces[i].addr; + return 0; + } + } + return -1; +} + +/* Looks for an interface with the given name (iname) and address + family type, and returns the corresponding interface_info if found. + Will accept a match of devname or devfullname. Returns NULL if + none found */ +struct interface_info *getInterfaceByName(const char *iname, int af) { + struct interface_info *ifaces; + int numifaces = 0; + int ifnum; + + ifaces = getinterfaces(&numifaces, NULL, 0); + + for (ifnum = 0; ifnum < numifaces; ifnum++) { + if ((strcmp(ifaces[ifnum].devfullname, iname) == 0 || + strcmp(ifaces[ifnum].devname, iname) == 0) && + ifaces[ifnum].addr.ss_family == af) + return &ifaces[ifnum]; + } + + return NULL; +} + + +int sockaddr_equal(const struct sockaddr_storage *a, + const struct sockaddr_storage *b) { + + if (a->ss_family == AF_INET && b->ss_family == AF_INET) { + struct sockaddr_in *sa, *sb; + + sa = (struct sockaddr_in *) a; + sb = (struct sockaddr_in *) b; + + return sa->sin_addr.s_addr == sb->sin_addr.s_addr; + } if (a->ss_family == AF_INET6 && b->ss_family == AF_INET6) { + struct sockaddr_in6 *sa, *sb; + + sa = (struct sockaddr_in6 *) a; + sb = (struct sockaddr_in6 *) b; + + return memcmp(sa->sin6_addr.s6_addr, sb->sin6_addr.s6_addr, sizeof(sa->sin6_addr.s6_addr)) == 0; + } + + return 0; +} + +int sockaddr_equal_netmask(const struct sockaddr_storage *a, + const struct sockaddr_storage *b, u16 nbits) { + unsigned char netmask[IP6_ADDR_LEN]; + + addr_btom(nbits, netmask, sizeof(netmask)); + + if (a->ss_family == AF_INET && b->ss_family == AF_INET) { + struct in_addr *sa, *sb, *sn; + + sa = &((struct sockaddr_in *) a)->sin_addr; + sb = &((struct sockaddr_in *) b)->sin_addr; + sn = (struct in_addr *) netmask; + + return (sa->s_addr & sn->s_addr) == (sb->s_addr & sn->s_addr); + } else if (a->ss_family == AF_INET6 && b->ss_family == AF_INET6) { + struct in6_addr *sa, *sb, *sn; + unsigned int i; + + sa = &((struct sockaddr_in6 *) a)->sin6_addr; + sb = &((struct sockaddr_in6 *) b)->sin6_addr; + sn = (struct in6_addr *) netmask; + + for (i = 0; i < sizeof(sa->s6_addr); i++) { + if ((sa->s6_addr[i] & sn->s6_addr[i]) != (sb->s6_addr[i] & sn->s6_addr[i])) { + return 0; + } + } + + return 1; + } + + return 0; +} + +int sockaddr_equal_zero(const struct sockaddr_storage *s) { + if (s->ss_family == AF_INET) { + const struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) s; + return sin->sin_addr.s_addr == 0; + } if (s->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *) s; + return memcmp(sin6->sin6_addr.s6_addr, IP6_ADDR_UNSPEC, IP6_ADDR_LEN) == 0; + } + + return 0; +} + +/* This is a helper for getsysroutes_dnet. Once the table of routes is in + place, this function assigns each to an interface and removes any routes + that can't be assigned. */ +static struct dnet_collector_route_nfo *sysroutes_dnet_find_interfaces(struct dnet_collector_route_nfo *dcrn) +{ + struct interface_info *ifaces; + int numifaces = 0; + int i, j; + int changed=0; + + if( (ifaces=getinterfaces(&numifaces, NULL, 0))==NULL ) + return NULL; + for (i = 0; i < dcrn->numroutes; i++) { + if (dcrn->routes[i].device != NULL) + continue; + + /* First we match up routes whose gateway or destination address + directly matches the address of an interface. */ + struct sys_route *route = &dcrn->routes[i]; + struct sockaddr_storage *routeaddr; + + /* First see if the gateway was set */ + if (sockaddr_equal_zero(&route->gw)) + routeaddr = &dcrn->routes[i].dest; + else + routeaddr = &dcrn->routes[i].gw; + + for (j = 0; j < numifaces; j++) { + if (sockaddr_equal_netmask(&ifaces[j].addr, routeaddr, ifaces[j].netmask_bits)) { + dcrn->routes[i].device = &ifaces[j]; + break; + } + } + } + + /* Find any remaining routes that don't yet have an interface, and try to + match them up with the interface of another route. This handles "two-step" + routes like sometimes exist with PPP, where the gateway address of the + default route doesn't match an interface address, but the gateway address + goes through another route that does have an interface. */ + + do { + changed = 0; + for (i = 0; i < dcrn->numroutes; i++) { + if (dcrn->routes[i].device != NULL) + continue; + /* Does this route's gateway go through another route with an assigned + interface? */ + for (j = 0; j < dcrn->numroutes; j++) { + if (sockaddr_equal(&dcrn->routes[i].gw, &dcrn->routes[j].dest) + && dcrn->routes[j].device != NULL) { + dcrn->routes[i].device = dcrn->routes[j].device; + changed = 1; + } + } + } + } while (changed); + + /* Cull any routes that still don't have an interface. */ + i = 0; + while (i < dcrn->numroutes) { + if (dcrn->routes[i].device == NULL) { + char destbuf[INET6_ADDRSTRLEN]; + char gwbuf[INET6_ADDRSTRLEN]; + + Strncpy(destbuf, inet_ntop_ez(&dcrn->routes[i].dest, sizeof(dcrn->routes[i].dest)), sizeof(destbuf)); + Strncpy(gwbuf, inet_ntop_ez(&dcrn->routes[i].gw, sizeof(dcrn->routes[i].gw)), sizeof(gwbuf)); + /* + netutil_error("WARNING: Unable to find appropriate interface for system route to %s/%u gw %s", + destbuf, dcrn->routes[i].netmask_bits, gwbuf); + */ + /* Remove this entry from the table. */ + memmove(dcrn->routes + i, dcrn->routes + i + 1, sizeof(dcrn->routes[0]) * (dcrn->numroutes - i - 1)); + dcrn->numroutes--; + } else { + i++; + } + } + + return dcrn; +} + + +/* This is the callback for the call to route_loop in getsysroutes_dnet. It + takes a route entry and adds it into the dnet_collector_route_nfo struct. */ +static int collect_dnet_routes(const struct route_entry *entry, void *arg) { + struct dnet_collector_route_nfo *dcrn = (struct dnet_collector_route_nfo *) arg; + + /* Make sure we have room for the new route */ + if (dcrn->numroutes >= dcrn->capacity) { + dcrn->capacity <<= 2; + dcrn->routes = (struct sys_route *) safe_realloc(dcrn->routes, dcrn->capacity * sizeof(struct sys_route)); + } + + /* Now for the important business */ + addr_ntos(&entry->route_dst, (struct sockaddr *) &dcrn->routes[dcrn->numroutes].dest); + dcrn->routes[dcrn->numroutes].netmask_bits = entry->route_dst.addr_bits; + addr_ntos(&entry->route_gw, (struct sockaddr *) &dcrn->routes[dcrn->numroutes].gw); + dcrn->routes[dcrn->numroutes].metric = entry->metric; + dcrn->routes[dcrn->numroutes].device = getInterfaceByName(entry->intf_name, dcrn->routes[dcrn->numroutes].dest.ss_family); + dcrn->numroutes++; + + return 0; +} + + +/* Read system routes via libdnet. */ +static struct sys_route *getsysroutes_dnet(int *howmany, char *errstr, size_t errstrlen) { + struct dnet_collector_route_nfo dcrn; + + dcrn.capacity = 128; + dcrn.routes = (struct sys_route *) safe_zalloc(dcrn.capacity * sizeof(struct sys_route)); + dcrn.numroutes = 0; + dcrn.ifaces = NULL; + dcrn.numifaces = 0; + assert(howmany); + route_t *dr = route_open(); + + if (!dr){ + if(errstr) Snprintf(errstr, errstrlen, "%s: route_open() failed", __func__); + *howmany=-1; + return NULL; + } + if (route_loop(dr, collect_dnet_routes, &dcrn) != 0) { + if(errstr) Snprintf(errstr, errstrlen, "%s: route_loop() failed", __func__); + *howmany=-1; + return NULL; + } + route_close(dr); + + /* Now match up the routes to interfaces. */ + if( sysroutes_dnet_find_interfaces(&dcrn) == NULL ){ + if(errstr) Snprintf(errstr, errstrlen, "%s: sysroutes_dnet_find_interfaces() failed", __func__); + return NULL; + } + + *howmany = dcrn.numroutes; + return dcrn.routes; +} + + +/* Parse the system routing table, converting each route into a + sys_route entry. Returns an array of sys_routes. numroutes is set + to the number of routes in the array. The routing table is only + read the first time this is called -- later results are cached. + The returned route array is sorted by netmask with the most + specific matches first. + On error, NULL is returned, howmany is set to -1 and the supplied + error buffer "errstr", if not NULL, will contain an error message. */ +struct sys_route *getsysroutes(int *howmany, char *errstr, size_t errstrlen) { + static struct sys_route *routes = NULL; + static int numroutes = 0; + assert(howmany); + + if (routes != NULL) { + /* We have it cached. */ + *howmany = numroutes; + return routes; + } + + routes = getsysroutes_dnet(howmany, errstr, errstrlen); + + /* Check if we managed to get the routes and sort them if we did */ + if(routes==NULL){ + *howmany=-1; + return NULL; + }else{ + numroutes = *howmany; + /* Ensure that the route array is sorted by netmask and metric */ + qsort(routes, numroutes, sizeof(routes[0]), routecmp); + } + return routes; +} + + +/* Tries to determine whether the supplied address corresponds to + * localhost. (eg: the address is something like 127.x.x.x, the address + * matches one of the local network interfaces' address, etc). + * Returns 1 if the address is thought to be localhost and 0 otherwise */ +int islocalhost(const struct sockaddr_storage *ss) { + char dev[128]; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; + + if (ss->ss_family == AF_INET){ + sin = (struct sockaddr_in *) ss; + /* If it is 0.0.0.0 or starts with 127 then it is probably localhost. */ + if ((sin->sin_addr.s_addr & htonl(0xFF000000)) == htonl(0x7F000000)) + return 1; + + if (!(sin->sin_addr.s_addr)) + return 1; + } else { + sin6 = (struct sockaddr_in6 *) ss; + /* If it is ::0 or ::1 then it is probably localhost. */ + if (memcmp(&(sin6->sin6_addr), IP6_ADDR_UNSPEC, IP6_ADDR_LEN) == 0) + return 1; + if (memcmp(&(sin6->sin6_addr), IP6_ADDR_LOOPBACK, IP6_ADDR_LEN) == 0) + return 1; + } + + /* If it is the same addy as a local interface, then it is + probably localhost */ + if (ipaddr2devname(dev, ss) != -1) + return 1; + + /* OK, so to a first approximation, this addy is probably not + localhost */ + return 0; +} + + +/* Determines whether the supplied address corresponds to a private, + * non-Internet-routable address. See RFC1918 for details. + * + * Also checks for link-local addressing per RFC3927. + * + * Returns 1 if the address is private or 0 otherwise. */ +int isipprivate(const struct sockaddr_storage *addr) { + const struct sockaddr_in *sin; + char *ipc; + unsigned char i1, i2; + + if (!addr) + return 0; + if (addr->ss_family != AF_INET) + return 0; + sin = (struct sockaddr_in *) addr; + + ipc = (char *) &(sin->sin_addr.s_addr); + i1 = ipc[0]; + i2 = ipc[1]; + + /* 10.0.0.0/8 */ + if (i1 == 10) + return 1; + + /* 172.16.0.0/12 */ + if (i1 == 172 && i2 >= 16 && i2 <= 31) + return 1; + + /* 169.254.0.0/16 - RFC 3927 */ + if (i1 == 169 && i2 == 254) + return 1; + + /* 192.168.0.0/16 */ + if (i1 == 192 && i2 == 168) + return 1; + + return 0; +} + + +char *nexthdrtoa(u8 nextheader, int acronym){ + +static char buffer[129]; +memset(buffer, 0, 129); + +#define HDRTOA(num, short_name, long_name) \ + case num: \ + strncpy(buffer, acronym ? short_name : long_name, 128);\ + break; + +switch(nextheader){ + /* Generate these lines from nmap-protocols using the following perl command: + perl -lne'if(/^(\S+)\s*(\d+)\s*\#?\s*(.*)/){my$l=$3||$1;print qq{HDRTOA($2, "$1", "$l")}}' + */ + HDRTOA(0, "hopopt", "IPv6 Hop-by-Hop Option") + HDRTOA(1, "icmp", "Internet Control Message") + HDRTOA(2, "igmp", "Internet Group Management") + HDRTOA(3, "ggp", "Gateway-to-Gateway") + HDRTOA(4, "ipv4", "IP in IP (encapsulation)") + HDRTOA(5, "st", "Stream") + HDRTOA(6, "tcp", "Transmission Control") + HDRTOA(7, "cbt", "CBT") + HDRTOA(8, "egp", "Exterior Gateway Protocol") + HDRTOA(9, "igp", "any private interior gateway") + HDRTOA(10, "bbn-rcc-mon", "BBN RCC Monitoring") + HDRTOA(11, "nvp-ii", "Network Voice Protocol") + HDRTOA(12, "pup", "PARC universal packet protocol") + HDRTOA(13, "argus", "ARGUS") + HDRTOA(14, "emcon", "EMCON") + HDRTOA(15, "xnet", "Cross Net Debugger") + HDRTOA(16, "chaos", "Chaos") + HDRTOA(17, "udp", "User Datagram") + HDRTOA(18, "mux", "Multiplexing") + HDRTOA(19, "dcn-meas", "DCN Measurement Subsystems") + HDRTOA(20, "hmp", "Host Monitoring") + HDRTOA(21, "prm", "Packet Radio Measurement") + HDRTOA(22, "xns-idp", "XEROX NS IDP") + HDRTOA(23, "trunk-1", "Trunk-1") + HDRTOA(24, "trunk-2", "Trunk-2") + HDRTOA(25, "leaf-1", "Leaf-1") + HDRTOA(26, "leaf-2", "Leaf-2") + HDRTOA(27, "rdp", "Reliable Data Protocol") + HDRTOA(28, "irtp", "Internet Reliable Transaction") + HDRTOA(29, "iso-tp4", "ISO Transport Protocol Class 4") + HDRTOA(30, "netblt", "Bulk Data Transfer Protocol") + HDRTOA(31, "mfe-nsp", "MFE Network Services Protocol") + HDRTOA(32, "merit-inp", "MERIT Internodal Protocol") + HDRTOA(33, "dccp", "Datagram Congestion Control Protocol") + HDRTOA(34, "3pc", "Third Party Connect Protocol") + HDRTOA(35, "idpr", "Inter-Domain Policy Routing Protocol") + HDRTOA(36, "xtp", "XTP") + HDRTOA(37, "ddp", "Datagram Delivery Protocol") + HDRTOA(38, "idpr-cmtp", "IDPR Control Message Transport Proto") + HDRTOA(39, "tp++", "TP+") + HDRTOA(40, "il", "IL Transport Protocol") + HDRTOA(41, "ipv6", "Ipv6") + HDRTOA(42, "sdrp", "Source Demand Routing Protocol") + HDRTOA(43, "ipv6-route", "Routing Header for IPv6") + HDRTOA(44, "ipv6-frag", "Fragment Header for IPv6") + HDRTOA(45, "idrp", "Inter-Domain Routing Protocol") + HDRTOA(46, "rsvp", "Reservation Protocol") + HDRTOA(47, "gre", "General Routing Encapsulation") + HDRTOA(48, "dsp", "Dynamic Source Routing Protocol. Historically MHRP") + HDRTOA(49, "bna", "BNA") + HDRTOA(50, "esp", "Encap Security Payload") + HDRTOA(51, "ah", "Authentication Header") + HDRTOA(52, "i-nlsp", "Integrated Net Layer Security TUBA") + HDRTOA(53, "swipe", "IP with Encryption") + HDRTOA(54, "narp", "NBMA Address Resolution Protocol") + HDRTOA(55, "mobile", "IP Mobility") + HDRTOA(56, "tlsp", "Transport Layer Security Protocol using Kryptonet key management") + HDRTOA(57, "skip", "SKIP") + HDRTOA(58, "ipv6-icmp", "ICMP for IPv6") + HDRTOA(59, "ipv6-nonxt", "No Next Header for IPv6") + HDRTOA(60, "ipv6-opts", "Destination Options for IPv6") + HDRTOA(61, "anyhost", "any host internal protocol") + HDRTOA(62, "cftp", "CFTP") + HDRTOA(63, "anylocalnet", "any local network") + HDRTOA(64, "sat-expak", "SATNET and Backroom EXPAK") + HDRTOA(65, "kryptolan", "Kryptolan") + HDRTOA(66, "rvd", "MIT Remote Virtual Disk Protocol") + HDRTOA(67, "ippc", "Internet Pluribus Packet Core") + HDRTOA(68, "anydistribfs", "any distributed file system") + HDRTOA(69, "sat-mon", "SATNET Monitoring") + HDRTOA(70, "visa", "VISA Protocol") + HDRTOA(71, "ipcv", "Internet Packet Core Utility") + HDRTOA(72, "cpnx", "Computer Protocol Network Executive") + HDRTOA(73, "cphb", "Computer Protocol Heart Beat") + HDRTOA(74, "wsn", "Wang Span Network") + HDRTOA(75, "pvp", "Packet Video Protocol") + HDRTOA(76, "br-sat-mon", "Backroom SATNET Monitoring") + HDRTOA(77, "sun-nd", "SUN ND PROTOCOL-Temporary") + HDRTOA(78, "wb-mon", "WIDEBAND Monitoring") + HDRTOA(79, "wb-expak", "WIDEBAND EXPAK") + HDRTOA(80, "iso-ip", "ISO Internet Protocol") + HDRTOA(81, "vmtp", "VMTP") + HDRTOA(82, "secure-vmtp", "SECURE-VMTP") + HDRTOA(83, "vines", "VINES") + HDRTOA(84, "iptm", "Internet Protocol Traffic Manager. Historically TTP") + HDRTOA(85, "nsfnet-igp", "NSFNET-IGP") + HDRTOA(86, "dgp", "Dissimilar Gateway Protocol") + HDRTOA(87, "tcf", "TCF") + HDRTOA(88, "eigrp", "EIGRP") + HDRTOA(89, "ospfigp", "OSPFIGP") + HDRTOA(90, "sprite-rpc", "Sprite RPC Protocol") + HDRTOA(91, "larp", "Locus Address Resolution Protocol") + HDRTOA(92, "mtp", "Multicast Transport Protocol") + HDRTOA(93, "ax.25", "AX.") + HDRTOA(94, "ipip", "IP-within-IP Encapsulation Protocol") + HDRTOA(95, "micp", "Mobile Internetworking Control Pro.") + HDRTOA(96, "scc-sp", "Semaphore Communications Sec.") + HDRTOA(97, "etherip", "Ethernet-within-IP Encapsulation") + HDRTOA(98, "encap", "Encapsulation Header") + HDRTOA(99, "anyencrypt", "any private encryption scheme") + HDRTOA(100, "gmtp", "GMTP") + HDRTOA(101, "ifmp", "Ipsilon Flow Management Protocol") + HDRTOA(102, "pnni", "PNNI over IP") + HDRTOA(103, "pim", "Protocol Independent Multicast") + HDRTOA(104, "aris", "ARIS") + HDRTOA(105, "scps", "SCPS") + HDRTOA(106, "qnx", "QNX") + HDRTOA(107, "a/n", "Active Networks") + HDRTOA(108, "ipcomp", "IP Payload Compression Protocol") + HDRTOA(109, "snp", "Sitara Networks Protocol") + HDRTOA(110, "compaq-peer", "Compaq Peer Protocol") + HDRTOA(111, "ipx-in-ip", "IPX in IP") + HDRTOA(112, "vrrp", "Virtual Router Redundancy Protocol") + HDRTOA(113, "pgm", "PGM Reliable Transport Protocol") + HDRTOA(114, "any0hop", "any 0-hop protocol") + HDRTOA(115, "l2tp", "Layer Two Tunneling Protocol") + HDRTOA(116, "ddx", "D-II Data Exchange") + HDRTOA(117, "iatp", "Interactive Agent Transfer Protocol") + HDRTOA(118, "stp", "Schedule Transfer Protocol") + HDRTOA(119, "srp", "SpectraLink Radio Protocol") + HDRTOA(120, "uti", "UTI") + HDRTOA(121, "smp", "Simple Message Protocol") + HDRTOA(122, "sm", "Simple Multicast Protocol") + HDRTOA(123, "ptp", "Performance Transparency Protocol") + HDRTOA(124, "isis-ipv4", "ISIS over IPv4") + HDRTOA(125, "fire", "fire") + HDRTOA(126, "crtp", "Combat Radio Transport Protocol") + HDRTOA(127, "crudp", "Combat Radio User Datagram") + HDRTOA(128, "sscopmce", "sscopmce") + HDRTOA(129, "iplt", "iplt") + HDRTOA(130, "sps", "Secure Packet Shield") + HDRTOA(131, "pipe", "Private IP Encapsulation within IP") + HDRTOA(132, "sctp", "Stream Control Transmission Protocol") + HDRTOA(133, "fc", "Fibre Channel") + HDRTOA(134, "rsvp-e2e-ignore", "rsvp-e2e-ignore") + HDRTOA(135, "mobility-hdr", "Mobility Header") + HDRTOA(136, "udplite", "UDP-Lite [RFC3828]") + HDRTOA(137, "mpls-in-ip", "MPLS-in-IP [RFC4023]") + HDRTOA(138, "manet", "MANET Protocols [RFC5498]") + HDRTOA(139, "hip", "Host Identity Protocol") + HDRTOA(140, "shim6", "Shim6 Protocol [RFC5533]") + HDRTOA(141, "wesp", "Wrapped Encapsulating Security Payload") + HDRTOA(142, "rohc", "Robust Header Compression") + HDRTOA(143, "ethernet", "RFC 8986 Ethernet next-header") + HDRTOA(144, "aggfrag", "AGGFRAG encapsulation payload for ESP [draft-ietf-ipsecme-iptfs-18]") + HDRTOA(253, "experimental1", "Use for experimentation and testing") + HDRTOA(254, "experimental2", "Use for experimentation and testing") + default: + strncpy(buffer, acronym ? "unknown" : "Unknown protocol", 128);\ + break; + + } /* End of switch */ + + + return buffer; + +} /* End of nexthdrtoa() */ + + +/* TODO: Needs refactoring */ +static inline char* STRAPP(const char *fmt, ...) { + static char buf[256]; + static int bp; + int left = (int)sizeof(buf)-bp; + if(!fmt){ + bp = 0; + return(buf); + } + if (left <= 0) + return buf; + va_list ap; + va_start(ap, fmt); + bp += Vsnprintf (buf+bp, left, fmt, ap); + va_end(ap); + + return(buf); +} + +/* TODO: Needs refactoring */ +#define HEXDUMP -2 +#define UNKNOWN -1 + +#define BREAK() \ + {option_type = HEXDUMP; break;} +#define CHECK(tt) \ + if(tt >= option_end) \ + {option_type = HEXDUMP; break;} + +/* Takes binary data found in the IP Options field of an IPv4 packet + * and returns a string containing an ASCII description of the options + * found. The function returns a pointer to a static buffer that + * subsequent calls will overwrite. On error, NULL is returned. */ +char *format_ip_options(const u8* ipopt, int ipoptlen) { + char ipstring[32]; + int option_type = UNKNOWN;// option type + int option_len = 0; // option length + int option_pt = 0; // option pointer + int option_fl = 0; // option flag + const u8 *tptr; // temp pointer + u32 *tint; // temp int + + int option_sta = 0; // option start offset + int option_end = 0; // option end offset + int pt = 0; // current offset + + // clear buffer + STRAPP(NULL,NULL); + + if(!ipoptlen) + return(NULL); + + while(pt<ipoptlen){ // for every char in ipopt + // read ip option header + if(option_type == UNKNOWN) { + option_sta = pt; + option_type = ipopt[pt++]; + if(option_type != 0 && option_type != 1) { // should we be interested in length field? + if(pt >= ipoptlen) // no more chars + {option_type = HEXDUMP;pt--; option_end = 255; continue;} // no length field, hex dump to the end + option_len = ipopt[pt++]; + // end must not be greater than length + option_end = MIN(option_sta + option_len, ipoptlen); + // end must not be smaller than current position + option_end = MAX(option_end, option_sta+2); + } + } + switch(option_type) { + case 0: // IPOPT_END + STRAPP(" EOL", NULL); + option_type = UNKNOWN; + break; + case 1: // IPOPT_NOP + STRAPP(" NOP", NULL); + option_type = UNKNOWN; + break; +/* case 130: // IPOPT_SECURITY + option_type=-1; + break;*/ + case 131: // IPOPT_LSRR -> Loose Source and Record Route + case 137: // IPOPT_SSRR -> Strict Source and Record Route + case 7: // IPOPT_RR -> Record Route + if(pt - option_sta == 2) { + STRAPP(" %s%s{", (option_type==131)?"LS":(option_type==137)?"SS":"", "RR"); + // option pointer + CHECK(pt); + option_pt = ipopt[pt++]; + if(option_pt%4 != 0 || (option_sta + option_pt-1)>option_end || option_pt<4) //bad or too big pointer + STRAPP(" [bad ptr=%02i]", option_pt); + } + if(pt - option_sta > 2) { // ip's + int i, s = (option_pt)%4; + // if pointer is mangled, fix it. it's max 3 bytes wrong + CHECK(pt+3); + for(i=0; i<s; i++) + STRAPP("\\x%02x", ipopt[pt++]); + option_pt -= i; + // okay, now we can start printing ip's + CHECK(pt+3); + tptr = &ipopt[pt]; pt+=4; + if(inet_ntop(AF_INET, (char *) tptr, ipstring, sizeof(ipstring)) == NULL){ + return NULL; + } + STRAPP("%c%s",(pt-3-option_sta)==option_pt?'#':' ', ipstring); + if(pt == option_end) + STRAPP("%s",(pt-option_sta)==(option_pt-1)?"#":""); // pointer in the end? + }else BREAK(); + break; + case 68: // IPOPT_TS -> Internet Timestamp + if(pt - option_sta == 2){ + STRAPP(" TM{"); + // pointer + CHECK(pt); + option_pt = ipopt[pt++]; + // bad or too big pointer + if(option_pt%4 != 1 || (option_sta + option_pt-1)>option_end || option_pt<5) + STRAPP(" [bad ptr=%02i]", option_pt); + // flags + overflow + CHECK(pt); + option_fl = ipopt[pt++]; + if((option_fl&0x0C) || (option_fl&0x03)==2) + STRAPP(" [bad flags=\\x%01hhx]", option_fl&0x0F); + STRAPP("[%i hosts not recorded]", option_fl>>4); + option_fl &= 0x03; + } + if(pt - option_sta > 2) {// ip's + int i, s = (option_pt+3)%(option_fl==0?4:8); + // if pointer is mangled, fix it. it's max 3 bytes wrong + CHECK(pt+(option_fl==0?3:7)); + for(i=0; i<s; i++) + STRAPP("\\x%02x", ipopt[pt++]); + option_pt-=i; + + // print pt + STRAPP("%c",(pt+1-option_sta)==option_pt?'#':' '); + // okay, first grab ip. + if(option_fl!=0){ + CHECK(pt+3); + tptr = &ipopt[pt]; pt+=4; + if(inet_ntop(AF_INET, (char *) tptr, ipstring, sizeof(ipstring)) == NULL){ + return NULL; + } + STRAPP("%s@", ipstring); + } + CHECK(pt+3); + tint = (u32*)&ipopt[pt]; pt+=4; + STRAPP("%lu", (unsigned long) ntohl(*tint)); + + if(pt == option_end) + STRAPP("%s",(pt-option_sta)==(option_pt-1)?"#":" "); + }else BREAK(); + break; + case 136: // IPOPT_SATID -> (SANET) Stream Identifier + if(pt - option_sta == 2){ + u16 *sh; + STRAPP(" SI{",NULL); + // length + if(option_sta+option_len > ipoptlen || option_len!=4) + STRAPP("[bad len %02i]", option_len); + + // stream id + CHECK(pt+1); + sh = (u16*) &ipopt[pt]; pt+=2; + option_pt = ntohs(*sh); + STRAPP("id=%hu", (unsigned short) option_pt); + if(pt != option_end) + BREAK(); + }else BREAK(); + break; + case UNKNOWN: + default: + // we read option_type and option_len, print them. + STRAPP(" ??{\\x%02hhx\\x%02hhx", option_type, option_len); + // check option_end once more: + if(option_len < ipoptlen) + option_end = MIN(MAX(option_sta+option_len, option_sta+2),ipoptlen); + else + option_end = 255; + option_type = HEXDUMP; + break; + case HEXDUMP: + assert(pt<=option_end); + if(pt == option_end){ + STRAPP("}",NULL); + option_type=-1; + break; + } + STRAPP("\\x%02hhx", ipopt[pt++]); + break; + } + if(pt == option_end && option_type != UNKNOWN) { + STRAPP("}",NULL); + option_type = UNKNOWN; + } + } // while + if(option_type != UNKNOWN) + STRAPP("}"); + + return(STRAPP("",NULL)); +} +#undef CHECK +#undef BREAK +#undef UNKNOWN +#undef HEXDUMP + + + +/* Returns a buffer of ASCII information about an IP packet that may + * look like "TCP 127.0.0.1:50923 > 127.0.0.1:3 S ttl=61 id=39516 + * iplen=40 seq=625950769" or "ICMP PING (0/1) ttl=61 id=39516 iplen=40". + * Returned buffer is static so it is NOT safe to call this in + * multi-threaded environments without appropriate sync protection, or + * call it twice in the same sentence (eg: as two printf parameters). + * Obviously, the caller should never attempt to free() the buffer. The + * returned buffer is guaranteed to be NULL-terminated but no + * assumptions should be made concerning its length. + * + * The function knows IPv4, IPv6, TCP, UDP, SCTP, ICMP, and ICMPv6. + * + * The output has three different levels of detail. Parameter "detail" + * determines how verbose the output should be. It should take one of + * the following values: + * + * LOW_DETAIL (0x01): Traditional output. + * MEDIUM_DETAIL (0x02): More verbose than traditional. + * HIGH_DETAIL (0x03): Contents of virtually every field of the + * protocol headers . + */ +const char *ippackethdrinfo(const u8 *packet, u32 len, int detail) { + struct abstract_ip_hdr hdr; + const u8 *data; + unsigned int datalen; + + struct tcp_hdr *tcp = NULL; /* TCP header structure. */ + struct udp_hdr *udp = NULL; /* UDP header structure. */ + struct sctp_hdr *sctp = NULL; /* SCTP header structure. */ + static char protoinfo[1024] = ""; /* Stores final info string. */ + char ipinfo[512] = ""; /* Temp info about IP. */ + char icmpinfo[512] = ""; /* Temp info about ICMP. */ + char icmptype[128] = ""; /* Temp info about ICMP type & code */ + char icmpfields[256] = ""; /* Temp info for various ICMP fields */ + char fragnfo[64] = ""; /* Temp info about fragmentation. */ + char srchost[INET6_ADDRSTRLEN] = ""; /* Src IP in dot-decimal notation. */ + char dsthost[INET6_ADDRSTRLEN] = ""; /* Dst IP in dot-decimal notation. */ + char *p = NULL; /* Aux pointer. */ + int frag_off = 0; /* To compute IP fragment offset. */ + int more_fragments = 0; /* True if IP MF flag is set. */ + int dont_fragment = 0; /* True if IP DF flag is set. */ + int reserved_flag = 0; /* True if IP Reserved flag is set. */ + + datalen = len; + data = (u8 *) ip_get_data_any(packet, &datalen, &hdr); + if (data == NULL) + return "BOGUS! Can't parse supposed IP packet"; + + + /* Ensure we end up with a valid detail number */ + if (detail != LOW_DETAIL && detail != MEDIUM_DETAIL && detail != HIGH_DETAIL) + detail = LOW_DETAIL; + + /* IP INFORMATION ************************************************************/ + if (hdr.version == 4) { /* IPv4 */ + const struct ip *ip; + const struct sockaddr_in *sin; + + ip = (struct ip *) packet; + + /* Obtain IP source and destination info */ + sin = (struct sockaddr_in *) &hdr.src; + inet_ntop(AF_INET, (void *)&sin->sin_addr.s_addr, srchost, sizeof(srchost)); + sin = (struct sockaddr_in *) &hdr.dst; + inet_ntop(AF_INET, (void *)&sin->sin_addr.s_addr, dsthost, sizeof(dsthost)); + + /* Compute fragment offset and check if flags are set */ + frag_off = 8 * (ntohs(ip->ip_off) & 8191) /* 2^13 - 1 */; + more_fragments = ntohs(ip->ip_off) & IP_MF; + dont_fragment = ntohs(ip->ip_off) & IP_DF; + reserved_flag = ntohs(ip->ip_off) & IP_RF; + + /* Is this a fragmented packet? is it the last fragment? */ + if (frag_off || more_fragments) { + Snprintf(fragnfo, sizeof(fragnfo), " frag offset=%d%s", frag_off, more_fragments ? "+" : ""); + } + + /* Create a string with information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ttl=%d id=%hu iplen=%hu%s %s%s%s", + ip->ip_ttl, (unsigned short) ntohs(ip->ip_id), (unsigned short) ntohs(ip->ip_len), fragnfo, + ip->ip_hl==5?"":"ipopts={", + ip->ip_hl==5?"":format_ip_options((u8*) ip + sizeof(struct ip), MIN((unsigned)(ip->ip_hl-5)*4,len-sizeof(struct ip))), + ip->ip_hl==5?"":"}"); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ttl=%d id=%hu proto=%d csum=0x%04x iplen=%hu%s %s%s%s", + ip->ip_ttl, (unsigned short) ntohs(ip->ip_id), + ip->ip_p, ntohs(ip->ip_sum), + (unsigned short) ntohs(ip->ip_len), fragnfo, + ip->ip_hl==5?"":"ipopts={", + ip->ip_hl==5?"":format_ip_options((u8*) ip + sizeof(struct ip), MIN((unsigned)(ip->ip_hl-5)*4,len-sizeof(struct ip))), + ip->ip_hl==5?"":"}"); + } else if (detail == HIGH_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ver=%d ihl=%d tos=0x%02x iplen=%hu id=%hu%s%s%s%s foff=%d%s ttl=%d proto=%d csum=0x%04x%s%s%s", + ip->ip_v, ip->ip_hl, + ip->ip_tos, (unsigned short) ntohs(ip->ip_len), + (unsigned short) ntohs(ip->ip_id), + (reserved_flag||dont_fragment||more_fragments) ? " flg=" : "", + (reserved_flag)? "x" : "", + (dont_fragment)? "D" : "", + (more_fragments)? "M": "", + frag_off, (more_fragments) ? "+" : "", + ip->ip_ttl, ip->ip_p, + ntohs(ip->ip_sum), + ip->ip_hl==5?"":" ipopts={", + ip->ip_hl==5?"":format_ip_options((u8*) ip + sizeof(struct ip), MIN((unsigned)(ip->ip_hl-5)*4,len-sizeof(struct ip))), + ip->ip_hl==5?"":"}"); + } + } else { /* IPv6 */ + const struct ip6_hdr *ip6; + const struct sockaddr_in6 *sin6; + + ip6 = (struct ip6_hdr *) packet; + + /* Obtain IP source and destination info */ + sin6 = (struct sockaddr_in6 *) &hdr.src; + inet_ntop(AF_INET6, (void *)sin6->sin6_addr.s6_addr, srchost, sizeof(srchost)); + sin6 = (struct sockaddr_in6 *) &hdr.dst; + inet_ntop(AF_INET6, (void *)sin6->sin6_addr.s6_addr, dsthost, sizeof(dsthost)); + + /* Obtain flow label and traffic class */ + u32 flow = ntohl(ip6->ip6_flow); + u32 ip6_fl = flow & 0x000fffff; + u32 ip6_tc = (flow & 0x0ff00000) >> 20; + + /* Create a string with information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "hopl=%d flow=%x payloadlen=%hu", + ip6->ip6_hlim, ip6_fl, (unsigned short) ntohs(ip6->ip6_plen)); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "hopl=%d tclass=%d flow=%x payloadlen=%hu", + ip6->ip6_hlim, ip6_tc, ip6_fl, (unsigned short) ntohs(ip6->ip6_plen)); + } else if (detail==HIGH_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ver=6, tclass=%x flow=%x payloadlen=%hu nh=%s hopl=%d ", + ip6_tc, ip6_fl, (unsigned short) ntohs(ip6->ip6_plen), + nexthdrtoa(ip6->ip6_nxt, 1), ip6->ip6_hlim); + } + } + + + /* TCP INFORMATION ***********************************************************/ + if (hdr.proto == IPPROTO_TCP) { + char tflags[10]; + char tcpinfo[64] = ""; + char buf[32]; + char tcpoptinfo[256] = ""; + tcp = (struct tcp_hdr *) data; + + /* Let's parse the TCP header. The following code is very ugly because we + * have to deal with a lot of different situations. We don't want to + * segfault so we have to check every length and every bound to ensure we + * don't read past the packet. We cannot even trust the contents of the + * received packet because, for example, an IPv4 header may state it + * carries a TCP packet but may actually carry nothing at all. + * + * So we distinguish 4 situations. I know the first two are weird but they + * were there when I modified this code so I left them there just in + * case. + * 1. IP datagram is very small or is a fragment where we are missing + * the first part of the TCP header + * 2. IP datagram is a fragment and although we are missing the first + * 8 bytes of the TCP header, we have the rest of it (or some of + * the rest of it) + * 3. IP datagram is NOT a fragment but we don't have the full TCP + * header, we are missing some bytes. + * 4. IP datagram is NOT a fragment and we have at least a full 20 + * byte TCP header. + */ + + /* CASE 1: where we don't have the first 8 bytes of the TCP header because + * either the fragment belongs to somewhere past that or the IP contains + * less than 8 bytes. This also includes empty IP packets that say they + * contain a TCP packet. */ + if (frag_off > 8 || datalen < 8) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? ?? %s (incomplete)", + srchost, dsthost, ipinfo); + } + /* For all cases after this, datalen is necessarily >= 8 and frag_off is <= 8 */ + + /* CASE 2: where we are missing the first 8 bytes of the TCP header but we + * have, at least, the next 8 bytes so we can see the ACK number, the + * flags and window size. */ + else if (frag_off > 0) { + /* Fragmentation is on 8-byte boundaries, so 8 is the only legal value here. */ + assert(frag_off == 8); + tcp = (struct tcp_hdr *)((u8 *) tcp - frag_off); // ugly? + + /* TCP Flags */ + p = tflags; + /* These are basically in tcpdump order */ + if (tcp->th_flags & TH_SYN) + *p++ = 'S'; + if (tcp->th_flags & TH_FIN) + *p++ = 'F'; + if (tcp->th_flags & TH_RST) + *p++ = 'R'; + if (tcp->th_flags & TH_PUSH) + *p++ = 'P'; + if (tcp->th_flags & TH_ACK) { + *p++ = 'A'; + Snprintf(tcpinfo, sizeof(tcpinfo), " ack=%lu", + (unsigned long) ntohl(tcp->th_ack)); + } + if (tcp->th_flags & TH_URG) + *p++ = 'U'; + if (tcp->th_flags & TH_ECE) + *p++ = 'E'; /* rfc 2481/3168 */ + if (tcp->th_flags & TH_CWR) + *p++ = 'C'; /* rfc 2481/3168 */ + *p++ = '\0'; + + /* TCP Options */ + if ((u32) tcp->th_off * 4 > sizeof(struct tcp_hdr)) { + if (datalen < (u32) tcp->th_off * 4 - frag_off) { + Snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); + } else { + tcppacketoptinfo((u8*) tcp + sizeof(struct tcp_hdr), + tcp->th_off*4 - sizeof(struct tcp_hdr), + tcpoptinfo, sizeof(tcpoptinfo)); + } + } + + /* Create a string with TCP information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s %s %s %s", + srchost, dsthost, tflags, ipinfo, tcpinfo, tcpoptinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s ack=%lu win=%hu %s IP [%s]", + srchost, dsthost, tflags, + (unsigned long) ntohl(tcp->th_ack), (unsigned short) ntohs(tcp->th_win), + tcpoptinfo, ipinfo); + } else if (detail == HIGH_DETAIL) { + if (datalen >= 12) { /* We have at least bytes 8-20 */ + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:?? > %s:?? %s seq=%lu ack=%lu off=%d res=%d win=%hu csum=0x%04X urp=%hu%s%s] IP [%s]", + srchost, dsthost, tflags, + (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (u8)tcp->th_off, (u8)tcp->th_x2, (unsigned short) ntohs(tcp->th_win), + ntohs(tcp->th_sum), (unsigned short) ntohs(tcp->th_urp), + (tcpoptinfo[0]!='\0') ? " " : "", + tcpoptinfo, ipinfo); + } else { /* We only have bytes 8-16 */ + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s ack=%lu win=%hu %s IP [%s]", + srchost, dsthost, tflags, + (unsigned long) ntohl(tcp->th_ack), (unsigned short) ntohs(tcp->th_win), + tcpoptinfo, ipinfo); + } + } + } + /* For all cases after this, frag_off is necessarily 0 */ + + /* CASE 3: where the IP packet is not a fragment but for some reason, we + * don't have the entire TCP header, just part of it.*/ + else if (datalen < 20) { + /* We know we have the first 8 bytes, so what's left? */ + /* We only have the first 64 bits: ports and seq number */ + if (datalen < 12) { + Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%hu > %s:%hu ?? seq=%lu (incomplete) %s", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, + (unsigned short) ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), ipinfo); + } + + /* We only have the first 96 bits: ports, seq and ack number */ + else if (datalen < 16) { + if (detail == LOW_DETAIL) { /* We don't print ACK in low detail */ + Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%hu > %s:%hu seq=%lu (incomplete), %s", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, + (unsigned short) ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), ipinfo); + } else { + Snprintf(tcpinfo, sizeof(tcpinfo), "TCP [%s:%hu > %s:%hu seq=%lu ack=%lu (incomplete)] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, + (unsigned short) ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), ipinfo); + } + } + + /* We are missing some part of the last 32 bits (checksum and urgent pointer) */ + else { + p = tflags; + /* These are basically in tcpdump order */ + if (tcp->th_flags & TH_SYN) + *p++ = 'S'; + if (tcp->th_flags & TH_FIN) + *p++ = 'F'; + if (tcp->th_flags & TH_RST) + *p++ = 'R'; + if (tcp->th_flags & TH_PUSH) + *p++ = 'P'; + if (tcp->th_flags & TH_ACK) { + *p++ = 'A'; + Snprintf(buf, sizeof(buf), " ack=%lu", + (unsigned long) ntohl(tcp->th_ack)); + strncat(tcpinfo, buf, sizeof(tcpinfo) - strlen(tcpinfo) - 1); + } + if (tcp->th_flags & TH_URG) + *p++ = 'U'; + if (tcp->th_flags & TH_ECE) + *p++ = 'E'; /* rfc 2481/3168 */ + if (tcp->th_flags & TH_CWR) + *p++ = 'C'; /* rfc 2481/3168 */ + *p++ = '\0'; + + + /* Create a string with TCP information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { /* We don't print ACK in low detail */ + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%hu > %s:%hu %s %s seq=%lu win=%hu (incomplete)", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, ipinfo, (unsigned long) ntohl(tcp->th_seq), + (unsigned short) ntohs(tcp->th_win)); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu ack=%lu win=%hu (incomplete)] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (unsigned short) ntohs(tcp->th_win), ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu ack=%lu off=%d res=%d win=%hu (incomplete)] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), + dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (u8)tcp->th_off, (u8)tcp->th_x2, (unsigned short) ntohs(tcp->th_win), + ipinfo); + } + } + } + + /* CASE 4: where we (finally!) have a full 20 byte TCP header so we can + * safely print all fields */ + else { /* if (datalen >= 20) */ + + /* TCP Flags */ + p = tflags; + /* These are basically in tcpdump order */ + if (tcp->th_flags & TH_SYN) + *p++ = 'S'; + if (tcp->th_flags & TH_FIN) + *p++ = 'F'; + if (tcp->th_flags & TH_RST) + *p++ = 'R'; + if (tcp->th_flags & TH_PUSH) + *p++ = 'P'; + if (tcp->th_flags & TH_ACK) { + *p++ = 'A'; + Snprintf(buf, sizeof(buf), " ack=%lu", + (unsigned long) ntohl(tcp->th_ack)); + strncat(tcpinfo, buf, sizeof(tcpinfo) - strlen(tcpinfo) - 1); + } + if (tcp->th_flags & TH_URG) + *p++ = 'U'; + if (tcp->th_flags & TH_ECE) + *p++ = 'E'; /* rfc 2481/3168 */ + if (tcp->th_flags & TH_CWR) + *p++ = 'C'; /* rfc 2481/3168 */ + *p++ = '\0'; + + /* TCP Options */ + if ((u32) tcp->th_off * 4 > sizeof(struct tcp_hdr)) { + if (datalen < (unsigned int) tcp->th_off * 4) { + Snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); + } else { + tcppacketoptinfo((u8*) tcp + sizeof(struct tcp_hdr), + tcp->th_off*4 - sizeof(struct tcp_hdr), + tcpoptinfo, sizeof(tcpoptinfo)); + } + } + + /* Rest of header fields */ + if (detail == LOW_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%hu > %s:%hu %s %s seq=%lu win=%hu %s", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, ipinfo, (unsigned long) ntohl(tcp->th_seq), + (unsigned short) ntohs(tcp->th_win), tcpoptinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu win=%hu csum=0x%04X%s%s] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned short) ntohs(tcp->th_win), (unsigned short) ntohs(tcp->th_sum), + (tcpoptinfo[0]!='\0') ? " " : "", + tcpoptinfo, ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu ack=%lu off=%d res=%d win=%hu csum=0x%04X urp=%hu%s%s] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), + dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (u8)tcp->th_off, (u8)tcp->th_x2, (unsigned short) ntohs(tcp->th_win), + ntohs(tcp->th_sum), (unsigned short) ntohs(tcp->th_urp), + (tcpoptinfo[0]!='\0') ? " " : "", + tcpoptinfo, ipinfo); + } + } + + /* UDP INFORMATION ***********************************************************/ + } else if (hdr.proto == IPPROTO_UDP && frag_off) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP %s:?? > %s:?? fragment %s (incomplete)", + srchost, dsthost, ipinfo); + } else if (hdr.proto == IPPROTO_UDP) { + udp = (struct udp_hdr *) data; + /* TODO: See if we can segfault if we receive a fragmented packet whose IP packet does not say a thing about fragmentation */ + + if (detail == LOW_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP %s:%hu > %s:%hu %s", + srchost, (unsigned short) ntohs(udp->uh_sport), dsthost, (unsigned short) ntohs(udp->uh_dport), + ipinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%hu > %s:%hu csum=0x%04X] IP [%s]", + srchost, (unsigned short) ntohs(udp->uh_sport), dsthost, (unsigned short) ntohs(udp->uh_dport), ntohs(udp->uh_sum), + ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%hu > %s:%hu len=%hu csum=0x%04X] IP [%s]", + srchost, (unsigned short) ntohs(udp->uh_sport), dsthost, (unsigned short) ntohs(udp->uh_dport), + (unsigned short) ntohs(udp->uh_ulen), ntohs(udp->uh_sum), + ipinfo); + } + + /* SCTP INFORMATION **********************************************************/ + } else if (hdr.proto == IPPROTO_SCTP && frag_off) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP %s:?? > %s:?? fragment %s (incomplete)", + srchost, dsthost, ipinfo); + } else if (hdr.proto == IPPROTO_SCTP) { + sctp = (struct sctp_hdr *) data; + + if (detail == LOW_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP %s:%hu > %s:%hu %s", + srchost, (unsigned short) ntohs(sctp->sh_sport), dsthost, (unsigned short) ntohs(sctp->sh_dport), + ipinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP [%s:%hu > %s:%hu csum=0x%08x] IP [%s]", + srchost, (unsigned short) ntohs(sctp->sh_sport), dsthost, (unsigned short) ntohs(sctp->sh_dport), ntohl(sctp->sh_sum), + ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP [%s:%hu > %s:%hu vtag=%lu csum=0x%08x] IP [%s]", + srchost, (unsigned short) ntohs(sctp->sh_sport), dsthost, (unsigned short) ntohs(sctp->sh_dport), + (unsigned long) ntohl(sctp->sh_vtag), ntohl(sctp->sh_sum), + ipinfo); + } + + /* ICMP INFORMATION **********************************************************/ + } else if (hdr.proto == IPPROTO_ICMP && frag_off) { + Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s fragment %s (incomplete)", + srchost, dsthost, ipinfo); + } else if (hdr.proto == IPPROTO_ICMP) { + struct ip *ip2; /* Points to the IP datagram carried by some ICMP messages */ + char *ip2dst; /* Dest IP in caried IP datagram */ + char auxbuff[128]; /* Aux buffer */ + struct icmp_packet{ /* Generic ICMP struct */ + u8 type; + u8 code; + u16 checksum; + u8 data[128]; + }*icmppkt; + struct ppkt { /* Beginning of ICMP Echo/Timestamp header */ + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 seq; + } *ping = NULL; + struct icmp_redir{ + u8 type; + u8 code; + u16 checksum; + u32 addr; + } *icmpredir = NULL; + struct icmp_router{ + u8 type; + u8 code; + u16 checksum; + u8 addrs; + u8 addrlen; + u16 lifetime; + } *icmprouter = NULL; + struct icmp_param{ + u8 type; + u8 code; + u16 checksum; + u8 pnt; + u8 unused; + u16 unused2; + } *icmpparam = NULL; + struct icmp_tstamp{ + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 seq; + u32 orig; + u32 recv; + u32 trans; + } *icmptstamp = NULL; + struct icmp_amask{ + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 seq; + u32 mask; + } *icmpmask = NULL; + + /* Compute the ICMP minimum length. */ + unsigned pktlen = 8; + + /* We need the ICMP packet to be at least 8 bytes long */ + if (pktlen > datalen) + goto icmpbad; + + ping = (struct ppkt *) data; + icmppkt = (struct icmp_packet *) data; + + switch(icmppkt->type) { + /* Echo Reply **************************/ + case 0: + strcpy(icmptype, "Echo reply"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Destination Unreachable *************/ + case 3: + /* Point to the start of the original datagram */ + ip2 = (struct ip *) (data + 8); + + /* Check we have a full IP datagram included in the ICMP message */ + pktlen += MAX( (ip2->ip_hl * 4), 20); + if (pktlen > datalen) { + if (datalen == 8) { + Snprintf(icmptype, sizeof icmptype, "Destination unreachable%s", + (detail!=LOW_DETAIL)? " (original datagram missing)" : ""); + } else { + Snprintf(icmptype, sizeof icmptype, "Destination unreachable%s", + (detail!=LOW_DETAIL)? " (part of original datagram missing)" : ""); + } + goto icmpbad; + } + + /* Basic check to ensure we have an IPv4 datagram attached */ + /* TODO: We should actually check the datagram checksum to + * see if it validates because just checking the version number + * is not enough. On average, if we get random data 1 out of + * 16 (2^4bits) times we will have value 4. */ + if ((ip2->ip_v != 4) || ((ip2->ip_hl * 4) < 20) || ((ip2->ip_hl * 4) > 60)) { + Snprintf(icmptype, sizeof icmptype, "Destination unreachable (bogus original datagram)"); + goto icmpbad; + } else { + /* We have the original datagram + the first 8 bytes of the + * transport layer header */ + if (pktlen + 8 < datalen) { + tcp = (struct tcp_hdr *) ((char *) ip2 + (ip2->ip_hl * 4)); + udp = (struct udp_hdr *) ((char *) ip2 + (ip2->ip_hl * 4)); + sctp = (struct sctp_hdr *) ((char *) ip2 + (ip2->ip_hl * 4)); + } + } + + /* Determine the IP the original datagram was sent to */ + ip2dst = inet_ntoa(ip2->ip_dst); + + /* Determine type of Destination unreachable from the code value */ + switch (icmppkt->code) { + case 0: + Snprintf(icmptype, sizeof icmptype, "Network %s unreachable", ip2dst); + break; + + case 1: + Snprintf(icmptype, sizeof icmptype, "Host %s unreachable", ip2dst); + break; + + case 2: + Snprintf(icmptype, sizeof icmptype, "Protocol %u unreachable", ip2->ip_p); + break; + + case 3: + if (pktlen + 8 < datalen) { + if (ip2->ip_p == IPPROTO_UDP && udp) + Snprintf(icmptype, sizeof icmptype, "Port %hu unreachable", (unsigned short) ntohs(udp->uh_dport)); + else if (ip2->ip_p == IPPROTO_TCP && tcp) + Snprintf(icmptype, sizeof icmptype, "Port %hu unreachable", (unsigned short) ntohs(tcp->th_dport)); + else if (ip2->ip_p == IPPROTO_SCTP && sctp) + Snprintf(icmptype, sizeof icmptype, "Port %hu unreachable", (unsigned short) ntohs(sctp->sh_dport)); + else + Snprintf(icmptype, sizeof icmptype, "Port unreachable (unknown protocol %u)", ip2->ip_p); + } + else + strcpy(icmptype, "Port unreachable"); + break; + + case 4: + strcpy(icmptype, "Fragmentation required"); + Snprintf(icmpfields, sizeof(icmpfields), "Next-Hop-MTU=%d", icmppkt->data[2]<<8 | icmppkt->data[3]); + break; + + case 5: + strcpy(icmptype, "Source route failed"); + break; + + case 6: + Snprintf(icmptype, sizeof icmptype, "Destination network %s unknown", ip2dst); + break; + + case 7: + Snprintf(icmptype, sizeof icmptype, "Destination host %s unknown", ip2dst); + break; + + case 8: + strcpy(icmptype, "Source host isolated"); + break; + + case 9: + Snprintf(icmptype, sizeof icmptype, "Destination network %s administratively prohibited", ip2dst); + break; + + case 10: + Snprintf(icmptype, sizeof icmptype, "Destination host %s administratively prohibited", ip2dst); + break; + + case 11: + Snprintf(icmptype, sizeof icmptype, "Network %s unreachable for TOS", ip2dst); + break; + + case 12: + Snprintf(icmptype, sizeof icmptype, "Host %s unreachable for TOS", ip2dst); + break; + + case 13: + strcpy(icmptype, "Communication administratively prohibited by filtering"); + break; + + case 14: + strcpy(icmptype, "Host precedence violation"); + break; + + case 15: + strcpy(icmptype, "Precedence cutoff in effect"); + break; + + default: + strcpy(icmptype, "Destination unreachable (unknown code)"); + break; + } /* End of ICMP Code switch */ + break; + + + /* Source Quench ***********************/ + case 4: + strcpy(icmptype, "Source quench"); + break; + + /* Redirect ****************************/ + case 5: + if (ping->code == 0) + strcpy(icmptype, "Network redirect"); + else if (ping->code == 1) + strcpy(icmptype, "Host redirect"); + else + strcpy(icmptype, "Redirect (unknown code)"); + icmpredir = (struct icmp_redir *) icmppkt; + inet_ntop(AF_INET, &icmpredir->addr, auxbuff, sizeof(auxbuff)); + Snprintf(icmpfields, sizeof(icmpfields), "addr=%s", auxbuff); + break; + + /* Echo Request ************************/ + case 8: + strcpy(icmptype, "Echo request"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Router Advertisement ****************/ + case 9: + if (icmppkt->code == 16) + strcpy(icmptype, "Router advertisement (Mobile Agent Only)"); + else + strcpy(icmptype, "Router advertisement"); + icmprouter = (struct icmp_router *) icmppkt; + Snprintf(icmpfields, sizeof(icmpfields), "addrs=%u addrlen=%u lifetime=%hu", + icmprouter->addrs, + icmprouter->addrlen, + (unsigned short) ntohs(icmprouter->lifetime)); + break; + + /* Router Solicitation *****************/ + case 10: + strcpy(icmptype, "Router solicitation"); + break; + + /* Time Exceeded ***********************/ + case 11: + if (icmppkt->code == 0) + strcpy(icmptype, "TTL=0 during transit"); + else if (icmppkt->code == 1) + strcpy(icmptype, "TTL=0 during reassembly"); + else + strcpy(icmptype, "TTL exceeded (unknown code)"); + break; + + /* Parameter Problem *******************/ + case 12: + if (ping->code == 0) + strcpy(icmptype, "Parameter problem (pointer indicates error)"); + else if (ping->code == 1) + strcpy(icmptype, "Parameter problem (option missing)"); + else if (ping->code == 2) + strcpy(icmptype, "Parameter problem (bad length)"); + else + strcpy(icmptype, "Parameter problem (unknown code)"); + icmpparam = (struct icmp_param *) icmppkt; + Snprintf(icmpfields, sizeof(icmpfields), "pointer=%d", icmpparam->pnt); + break; + + /* Timestamp Request/Reply *************/ + case 13: + case 14: + Snprintf(icmptype, sizeof(icmptype), "Timestamp %s", (icmppkt->type == 13)? "request" : "reply"); + icmptstamp = (struct icmp_tstamp *) icmppkt; + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu orig=%lu recv=%lu trans=%lu", + (unsigned short) ntohs(icmptstamp->id), (unsigned short) ntohs(icmptstamp->seq), + (unsigned long) ntohl(icmptstamp->orig), + (unsigned long) ntohl(icmptstamp->recv), + (unsigned long) ntohl(icmptstamp->trans)); + break; + + /* Information Request *****************/ + case 15: + strcpy(icmptype, "Information request"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Information Reply *******************/ + case 16: + strcpy(icmptype, "Information reply"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Netmask Request/Reply ***************/ + case 17: + case 18: + Snprintf(icmptype, sizeof(icmptype), "Address mask %s", (icmppkt->type == 17)? "request" : "reply"); + icmpmask = (struct icmp_amask *) icmppkt; + inet_ntop(AF_INET, &icmpmask->mask, auxbuff, sizeof(auxbuff)); + Snprintf(icmpfields, sizeof(icmpfields), "id=%u seq=%u mask=%s", + (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq), auxbuff); + break; + + /* Traceroute **************************/ + case 30: + strcpy(icmptype, "Traceroute"); + break; + + /* Domain Name Request *****************/ + case 37: + strcpy(icmptype, "Domain name request"); + break; + + /* Domain Name Reply *******************/ + case 38: + strcpy(icmptype, "Domain name reply"); + break; + + /* Security ****************************/ + case 40: + strcpy(icmptype, "Security failures"); /* rfc 2521 */ + break; + + default: + strcpy(icmptype, "Unknown type"); break; + break; + } /* End of ICMP Type switch */ + + if (pktlen > datalen) { +icmpbad: + if (ping) { + /* We still have this information */ + Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s %s (type=%d/code=%d) %s", + srchost, dsthost, icmptype, ping->type, ping->code, ipinfo); + } else { + Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s [??] %s", + srchost, dsthost, ipinfo); + } + } else { + if (ping) + sprintf(icmpinfo,"type=%d/code=%d", ping->type, ping->code); + else + strncpy(icmpinfo,"type=?/code=?", sizeof(icmpinfo)); + + Snprintf(protoinfo, sizeof(protoinfo), "ICMP [%s > %s %s (%s) %s] IP [%s]", + srchost, dsthost, icmptype, icmpinfo, icmpfields, ipinfo); + } + + } else if (hdr.proto == IPPROTO_ICMPV6) { + if (datalen > sizeof(struct icmpv6_hdr)) { + const struct icmpv6_hdr *icmpv6; + + icmpv6 = (struct icmpv6_hdr *) data; + Snprintf(protoinfo, sizeof(protoinfo), "ICMPv6 (%d) %s > %s (type=%d/code=%d) %s", + hdr.proto, srchost, dsthost, + icmpv6->icmpv6_type, icmpv6->icmpv6_code, ipinfo); + } + else { + Snprintf(protoinfo, sizeof(protoinfo), "ICMPv6 (%d) %s > %s (type=?/code=?) %s", + hdr.proto, srchost, dsthost, ipinfo); + } + } else { + /* UNKNOWN PROTOCOL **********************************************************/ + const char *hdrstr; + + hdrstr = nexthdrtoa(hdr.proto, 1); + if (hdrstr == NULL || *hdrstr == '\0') { + Snprintf(protoinfo, sizeof(protoinfo), "Unknown protocol (%d) %s > %s: %s", + hdr.proto, srchost, dsthost, ipinfo); + } else { + Snprintf(protoinfo, sizeof(protoinfo), "%s (%d) %s > %s: %s", + hdrstr, hdr.proto, srchost, dsthost, ipinfo); + } + } + + return protoinfo; +} + + +#ifdef HAVE_LINUX_RTNETLINK_H +/* Fill in a sockaddr_storage given an address family and raw address. */ +static int set_sockaddr(struct sockaddr_storage *ss, int af, void *data) { + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + ss->ss_family = af; + if (af == AF_INET) { + sin = (struct sockaddr_in *) ss; + memcpy(&sin->sin_addr.s_addr, data, IP_ADDR_LEN); + } else if (af == AF_INET6) { + sin6 = (struct sockaddr_in6 *) ss; + memcpy(sin6->sin6_addr.s6_addr, data, IP6_ADDR_LEN); + } else { + return -1; + } + + return 0; +} + +/* Add rtattrs to a netlink message specifying a source or destination address. + rta_type must be RTA_SRC or RTA_DST. This function adds either 1 or 2 + rtattrs: it always adds either an RTA_SRC or RTA_DST, depending on rta_type. + If ifindex is not 0, it is the index of the interface to use. The function + adds either RTA_OIF if rta_type is RTA_DST, and either of ifindex and + sin6_scope_id is nonzero. */ +static void add_rtattr_addr(struct nlmsghdr *nlmsg, + struct rtattr **rtattr, unsigned int *len, + unsigned char rta_type, + const struct sockaddr_storage *ss, + int ifindex) { + struct rtmsg *rtmsg; + const void *addr; + size_t addrlen; + + assert(rta_type == RTA_SRC || rta_type == RTA_DST); + + if (rta_type == RTA_SRC) { + /* Ignore the interface specification if we are setting an RTA_SRC attribute + (it may still get set by the scope_id below). */ + ifindex = 0; + } + + if (ss->ss_family == AF_INET) { + addr = &((struct sockaddr_in *) ss)->sin_addr.s_addr; + addrlen = IP_ADDR_LEN; + } else if (ss->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ss; + + addr = sin6->sin6_addr.s6_addr; + addrlen = IP6_ADDR_LEN; + if (ifindex == 0) + ifindex = sin6->sin6_scope_id; + } else { + netutil_fatal("%s: unknown address family %d", __func__, ss->ss_family); + } + + rtmsg = (struct rtmsg *) (nlmsg + 1); + if (rta_type == RTA_SRC) + rtmsg->rtm_src_len = addrlen * 8; + else + rtmsg->rtm_dst_len = addrlen * 8; + + /* Add an rtattr for the address. */ + (*rtattr)->rta_type = rta_type; + (*rtattr)->rta_len = RTA_LENGTH(addrlen); + assert(RTA_OK(*rtattr, *len)); + memcpy(RTA_DATA(*rtattr), addr, addrlen); + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + (*rtattr)->rta_len; + *rtattr = RTA_NEXT(*rtattr, *len); + + /* Specific interface (sin6_scope_id) requested? */ + if (ifindex > 0) { + /* Add an rtattr for the interface. */ + if (rta_type == RTA_SRC) + (*rtattr)->rta_type = RTA_IIF; + else + (*rtattr)->rta_type = RTA_OIF; + (*rtattr)->rta_len = RTA_LENGTH(sizeof(uint32_t)); + assert(RTA_OK(*rtattr, *len)); + *(uint32_t *) RTA_DATA(*rtattr) = ifindex; + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + (*rtattr)->rta_len; + *rtattr = RTA_NEXT(*rtattr, *len); + } +} + +/* Does route_dst using the Linux-specific rtnetlink interface. See rtnetlink(3) + and rtnetlink(7). */ +static int route_dst_netlink(const struct sockaddr_storage *dst, + struct route_nfo *rnfo, const char *device, + const struct sockaddr_storage *spoofss) { + struct sockaddr_nl snl; + struct msghdr msg; + struct iovec iov; + struct nlmsghdr *nlmsg; + struct rtmsg *rtmsg; + struct rtattr *rtattr; + int intf_index; + unsigned char buf[512]; + unsigned int len; + int fd, rc; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd == -1) + netutil_fatal("%s: cannot create AF_NETLINK socket: %s", __func__, strerror(errno)); + + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + + rc = bind(fd, (struct sockaddr *) &snl, sizeof(snl)); + if (rc == -1) + netutil_fatal("%s: cannot bind AF_NETLINK socket: %s", __func__, strerror(errno)); + + struct interface_info *ii; + ii = NULL; + intf_index = 0; + if (device != NULL && device[0] != '\0') { + ii = getInterfaceByName(device, dst->ss_family); + if (ii == NULL) + netutil_fatal("Could not find interface %s which was specified by -e", device); + intf_index = ii->ifindex; + } + + memset(buf, 0, sizeof(buf)); + + nlmsg = (struct nlmsghdr *) buf; + + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(*rtmsg)); + assert(nlmsg->nlmsg_len <= sizeof(buf)); + nlmsg->nlmsg_flags = NLM_F_REQUEST; + nlmsg->nlmsg_type = RTM_GETROUTE; + + rtmsg = (struct rtmsg *) (nlmsg + 1); + rtmsg->rtm_family = dst->ss_family; + + rtattr = RTM_RTA(rtmsg); + len = sizeof(buf) - ((unsigned char *) RTM_RTA(rtmsg) - buf); + + /* Add rtattrs for destination address and interface. */ + add_rtattr_addr(nlmsg, &rtattr, &len, RTA_DST, dst, intf_index); + if (spoofss != NULL) { + /* Add rtattrs for source address and interface. */ + add_rtattr_addr(nlmsg, &rtattr, &len, RTA_SRC, spoofss, intf_index); + } + + iov.iov_base = nlmsg; + iov.iov_len = nlmsg->nlmsg_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &snl; + msg.msg_namelen = sizeof(snl); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + rc = sendmsg(fd, &msg, 0); + if (rc == -1) + netutil_fatal("%s: cannot sendmsg: %s", __func__, strerror(errno)); + + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + len = recvmsg(fd, &msg, 0); + if (len <= 0) + netutil_fatal("%s: cannot recvmsg: %s", __func__, strerror(errno)); + + close(fd); + + if (nlmsg->nlmsg_len < sizeof(*nlmsg) || (unsigned int) len < NLMSG_LENGTH(sizeof(*nlmsg))) + netutil_fatal("%s: wrong size reply in recvmsg", __func__); + len -= NLMSG_LENGTH(sizeof(*nlmsg)); + + /* See rtnetlink(7). Anything matching this route is actually unroutable. */ + if (rtmsg->rtm_type == RTN_UNREACHABLE || rtmsg->rtm_type == RTN_UNSPEC + || rtmsg->rtm_type == RTN_BLACKHOLE || rtmsg->rtm_type == RTN_PROHIBIT) + return 0; + + /* Default values to be possibly overridden. */ + rnfo->direct_connect = 1; + rnfo->nexthop.ss_family = AF_UNSPEC; + rnfo->srcaddr.ss_family = AF_UNSPEC; + if (spoofss != NULL) + rnfo->srcaddr = *spoofss; + + for (rtattr = RTM_RTA(rtmsg); RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) { + if (rtattr->rta_type == RTA_GATEWAY) { + rc = set_sockaddr(&rnfo->nexthop, dst->ss_family, RTA_DATA(rtattr)); + assert(rc != -1); + /* Don't consider it directly connected if nexthop != dst. */ + if (!sockaddr_storage_equal(dst, &rnfo->nexthop)) + rnfo->direct_connect = 0; + } else if (rtattr->rta_type == RTA_OIF && ii == NULL) { + char namebuf[IFNAMSIZ]; + char *p; + int intf_index; + + intf_index = *(int *) RTA_DATA(rtattr); + p = if_indextoname(intf_index, namebuf); + if (p == NULL) + netutil_fatal("%s: if_indextoname(%d) failed: %d (%s)", __func__, intf_index, errno, strerror(errno)); + ii = getInterfaceByName(namebuf, dst->ss_family); + if (ii == NULL) + ii = getInterfaceByName(namebuf, AF_UNSPEC); + if (ii == NULL) + netutil_fatal("%s: can't find interface \"%s\"", __func__, namebuf); + } else if (rtattr->rta_type == RTA_PREFSRC && rnfo->srcaddr.ss_family == AF_UNSPEC) { + rc = set_sockaddr(&rnfo->srcaddr, dst->ss_family, RTA_DATA(rtattr)); + assert(rc != -1); + } + } + + if (ii != NULL) { + rnfo->ii = *ii; + return 1; + } else { + return 0; + } +} + +#else + +static struct interface_info *find_loopback_iface(struct interface_info *ifaces, + int numifaces) { + int i; + + for (i = 0; i < numifaces; i++) { + if (ifaces[i].device_type == devt_loopback) + return &ifaces[i]; + } + + return NULL; +} + +/* Get the source address for routing to dst by creating a socket and asking the + operating system for the local address. */ +static int get_srcaddr(const struct sockaddr_storage *dst, + struct sockaddr_storage *src) +{ + static const unsigned short DUMMY_PORT = 1234; + struct sockaddr_storage dst_dummy; + size_t dst_dummy_len; + socklen_t len; + int fd, rc; + + fd = socket(dst->ss_family, SOCK_DGRAM, 0); + if (fd == -1) + netutil_fatal("%s: can't create socket: %s", __func__, socket_strerror(socket_errno())); + + dst_dummy = *dst; + if (dst_dummy.ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) &dst_dummy; + sin->sin_port = htons(DUMMY_PORT); + dst_dummy_len = sizeof(*sin); + } else if (dst_dummy.ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &dst_dummy; + sin6->sin6_port = htons(DUMMY_PORT); + dst_dummy_len = sizeof(*sin6); + } else { + goto bail; + } + + rc = connect(fd, (struct sockaddr *) &dst_dummy, dst_dummy_len); + if (rc == -1) { + netutil_error("%s: can't connect socket: %s", __func__, socket_strerror(socket_errno())); + if (dst->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &dst_dummy; + if (sin6->sin6_scope_id == 0) + netutil_error("Do you need an IPv6 zone ID suffix (e.g. %%eth0 or %%1)?"); + } + goto bail; + } + + len = sizeof(*src); + rc = getsockname(fd, (struct sockaddr *) src, &len); + if (rc == -1) + netutil_fatal("%s: can't getsockname: %s", __func__, socket_strerror(socket_errno())); + + close(fd); + return 0; + +bail: + close(fd); + return -1; +} + +static char *lookup_ifindex(unsigned int index, int af, char *namebuf, size_t len) { + intf_t *it; + struct intf_entry entry; + int rc; + + it = intf_open(); + assert(it != NULL); + entry.intf_len = sizeof(entry); + rc = intf_get_index(it, &entry, af, index); + intf_close(it); + if (rc == -1) + return NULL; + + Strncpy(namebuf, entry.intf_name, len); + return namebuf; +} + +static int route_dst_generic(const struct sockaddr_storage *dst, + struct route_nfo *rnfo, const char *device, + const struct sockaddr_storage *spoofss) { + struct interface_info *ifaces; + struct interface_info *iface; + int numifaces = 0; + struct sys_route *routes; + int numroutes = 0; + int i; + char namebuf[32]; + char errstr[256]; + errstr[0]='\0'; + + if (!dst) + netutil_fatal("%s passed a NULL dst address", __func__); + + if(spoofss!=NULL){ + /* Throughout the rest of this function we only change rnfo->srcaddr if the source isn't spoofed */ + memcpy(&rnfo->srcaddr, spoofss, sizeof(rnfo->srcaddr)); + /* The device corresponding to this spoofed address should already have been set elsewhere. */ + assert(device!=NULL && device[0]!='\0'); + } + + if (device == NULL || device[0] == '\0') { + /* Check if there is an interface scope on the address which we must use. */ + if (dst->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) dst; + if (sin6->sin6_scope_id > 0) { + device = lookup_ifindex(sin6->sin6_scope_id, sin6->sin6_family, namebuf, sizeof(namebuf)); + if (device == NULL) { + netutil_error("Could not find interface with index %u", (unsigned int) sin6->sin6_scope_id); + return 0; + } + } + } + } + + if (device!=NULL && device[0]!='\0'){ + iface = getInterfaceByName(device, dst->ss_family); + if (!iface) + netutil_fatal("Could not find interface %s", device); + } else { + iface = NULL; + } + + if((routes=getsysroutes(&numroutes, errstr, sizeof(errstr)))==NULL) + netutil_fatal("%s: Failed to obtain system routes: %s", __func__, errstr); + if((ifaces=getinterfaces(&numifaces, errstr, sizeof(errstr)))==NULL) + netutil_fatal("%s: Failed to obtain system interfaces: %s", __func__, errstr); + + /* First check if dst is one of the localhost's own addresses. We need to use + a localhost device for these. */ + for (i = 0; i < numifaces; i++) { + struct interface_info *loopback; + + if (!sockaddr_equal(dst, &ifaces[i].addr)) + continue; + + if (ifaces[i].device_type == devt_loopback) + loopback = &ifaces[i]; + else + loopback = find_loopback_iface(ifaces, numifaces); + if (loopback == NULL) + /* Hmmm ... no localhost -- move on to the routing table. */ + break; + + if (iface != NULL && strcmp(loopback->devname, iface->devname) != 0) + continue; + + if (iface == NULL && !loopback->device_up) + continue; + + rnfo->ii = *loopback; + rnfo->direct_connect = 1; + /* But the source address we want to use is the target address. */ + if (!spoofss) { + if (get_srcaddr(dst, &rnfo->srcaddr) == -1) + rnfo->srcaddr = rnfo->ii.addr; + } + + return 1; + } + + /* Go through the routing table and take the first match. getsysroutes sorts + so more-specific routes come first. */ + for (i = 0; i < numroutes; i++) { + if (!sockaddr_equal_netmask(dst, &routes[i].dest, routes[i].netmask_bits)) + continue; + /* Ignore routes that aren't on the device we specified. */ + if (iface != NULL && strcmp(routes[i].device->devname, iface->devname) != 0) + continue; + + if (iface == NULL && !routes[i].device->device_up) + continue; + + rnfo->ii = *routes[i].device; + /* At this point we don't whether this route is direct or indirect ("G" flag + in netstat). We guess that a route is direct when the gateway address is + 0.0.0.0 or ::, when it exactly matches the interface address, or when it + exactly matches the destination address. */ + rnfo->direct_connect = (sockaddr_equal_zero(&routes[i].gw) || + sockaddr_equal(&routes[i].gw, &routes[i].device->addr) || + sockaddr_equal(&routes[i].gw, dst)); + if (!spoofss) { + if (get_srcaddr(dst, &rnfo->srcaddr) == -1) + rnfo->srcaddr = rnfo->ii.addr; + } + rnfo->nexthop = routes[i].gw; + + return 1; + } + + /* No match on routes. Try interfaces directly. */ + for (i = 0; i < numifaces; i++) { + if (!sockaddr_equal_netmask(dst, &ifaces[i].addr, ifaces[i].netmask_bits)) + continue; + if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0) + continue; + + if (iface == NULL && !ifaces[i].device_up) + continue; + + rnfo->ii = ifaces[i]; + rnfo->direct_connect = 1; + if (!spoofss) { + if (get_srcaddr(dst, &rnfo->srcaddr) == -1) + rnfo->srcaddr = rnfo->ii.addr; + } + + return 1; + } + + return 0; +} +#endif + +/* Takes a destination address (dst) and tries to determine the + * source address and interface necessary to route to this address. + * If no route is found, 0 is returned and "rnfo" is undefined. If + * a route is found, 1 is returned and "rnfo" is filled in with all + * of the routing details. If the source address needs to be spoofed, + * it should be passed through "spoofss" (otherwise NULL should be + * specified), along with a suitable network device (parameter "device"). + * Even if spoofss is NULL, if user specified a network device with -e, + * it should still be passed. Note that it's OK to pass either NULL or + * an empty string as the "device", as long as spoofss==NULL. */ +int route_dst(const struct sockaddr_storage *dst, struct route_nfo *rnfo, + const char *device, const struct sockaddr_storage *spoofss) { +#ifdef HAVE_LINUX_RTNETLINK_H + return route_dst_netlink(dst, rnfo, device, spoofss); +#else + return route_dst_generic(dst, rnfo, device, spoofss); +#endif +} + +/* Wrapper for system function sendto(), which retries a few times when + * the call fails. It also prints informational messages about the + * errors encountered. It returns the number of bytes sent or -1 in + * case of error. */ +int Sendto(const char *functionname, int sd, + const unsigned char *packet, int len, unsigned int flags, + struct sockaddr *to, int tolen) { + + int res; + int retries = 0; + int sleeptime = 0; + static int numerrors = 0; + + do { + if ((res = sendto(sd, (const char *) packet, len, flags, to, tolen)) == -1) { + int err = socket_errno(); + + numerrors++; + if(numerrors <= 10) { + netutil_error("sendto in %s: sendto(%d, packet, %d, 0, %s, %d) => %s", + functionname, sd, len, inet_ntop_ez((struct sockaddr_storage *) to, sizeof(struct sockaddr_storage)), tolen, + strerror(err)); + netutil_error("Offending packet: %s", ippackethdrinfo(packet, len, LOW_DETAIL)); + if (numerrors == 10) { + netutil_error("Omitting future %s error messages now that %d have been shown. Use -d2 if you really want to see them.", __func__, numerrors); + } + } +#if WIN32 + return -1; +#else + if (retries > 2) + return -1; + /* For these enumerated errors, we sleep and try again. */ + if (!(err == ENOBUFS || err == ENOMEM)) + return -1; + sleeptime = 15 * (1 << (2 * retries)); + netutil_error("Sleeping %d seconds then retrying", sleeptime); + fflush(stderr); + sleep(sleeptime); +#endif + } + retries++; + } while (res == -1); + + return res; +} + + + +/* Send an IP packet over an ethernet handle. */ +int send_ip_packet_eth(const struct eth_nfo *eth, const u8 *packet, unsigned int packetlen) { + eth_t *ethsd; + u8 *eth_frame; + int res; + + eth_frame = (u8 *) safe_malloc(14 + packetlen); + memcpy(eth_frame + 14, packet, packetlen); + eth_pack_hdr(eth_frame, eth->dstmac, eth->srcmac, ETH_TYPE_IP); + if (!eth->ethsd) { + ethsd = eth_open_cached(eth->devname); + if (!ethsd) + netutil_fatal("%s: Failed to open ethernet device (%s)", __func__, eth->devname); + } else { + ethsd = eth->ethsd; + } + res = eth_send(ethsd, eth_frame, 14 + packetlen); + /* No need to close ethsd due to caching */ + free(eth_frame); + + return res; +} + + +/* Send an IP packet over a raw socket. */ +int send_ip_packet_sd(int sd, const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen) { + struct sockaddr_in sock; + struct ip *ip = (struct ip *) packet; + struct tcp_hdr *tcp; + struct udp_hdr *udp; + int res; + + assert(sd >= 0); + sock = *dst; + + /* It is bogus that I need the address and port info when sending a RAW IP + packet, but it doesn't seem to work w/o them */ + if (packetlen >= 20) { + if (ip->ip_p == IPPROTO_TCP + && packetlen >= (unsigned int) ip->ip_hl * 4 + 20) { + tcp = (struct tcp_hdr *) ((u8 *) ip + ip->ip_hl * 4); + sock.sin_port = tcp->th_dport; + } else if (ip->ip_p == IPPROTO_UDP + && packetlen >= (unsigned int) ip->ip_hl * 4 + 8) { + udp = (struct udp_hdr *) ((u8 *) ip + ip->ip_hl * 4); + sock.sin_port = udp->uh_dport; + } + } + + /* Equally bogus is that the IP total len and IP fragment offset + fields need to be in host byte order on certain BSD variants. I + must deal with it here rather than when building the packet, + because they should be in NBO when I'm sending over raw + ethernet */ +#if (defined(FREEBSD) && (__FreeBSD_version < 1100030)) || BSDI || NETBSD || DEC || MACOSX + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); +#endif + + res = Sendto("send_ip_packet_sd", sd, packet, packetlen, 0, + (struct sockaddr *) &sock, + (int) sizeof(struct sockaddr_in)); + + /* Undo the byte order switching. */ +#if (defined(FREEBSD) && (__FreeBSD_version < 1100030)) || BSDI || NETBSD || DEC || MACOSX + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); +#endif + + return res; +} + + + +/* Sends the supplied pre-built IPv4 packet. The packet is sent through + * the raw socket "sd" if "eth" is NULL. Otherwise, it gets sent at raw + * ethernet level. */ +int send_ip_packet_eth_or_sd(int sd, const struct eth_nfo *eth, + const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen) { + if(eth) + return send_ip_packet_eth(eth, packet, packetlen); + else + return send_ip_packet_sd(sd, dst, packet, packetlen); +} + + + +/* Create and send all fragments of a pre-built IPv4 packet + * Minimal MTU for IPv4 is 68 and maximal IPv4 header size is 60 + * which gives us a right to cut TCP header after 8th byte + * (shouldn't we inflate the header to 60 bytes too?) */ +int send_frag_ip_packet(int sd, const struct eth_nfo *eth, + const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen, u32 mtu) { + struct ip *ip = (struct ip *) packet; + int headerlen = ip->ip_hl * 4; // better than sizeof(struct ip) + u32 datalen = packetlen - headerlen; + int fdatalen = 0, res = 0; + int fragment=0; + + assert(headerlen <= (int) packetlen); + assert(headerlen >= 20 && headerlen <= 60); // sanity check (RFC791) + assert(mtu > 0 && mtu % 8 == 0); // otherwise, we couldn't set Fragment offset (ip->ip_off) correctly + + if (datalen <= mtu) { + netutil_error("Warning: fragmentation (mtu=%lu) requested but the payload is too small already (%lu)", (unsigned long)mtu, (unsigned long)datalen); + return send_ip_packet_eth_or_sd(sd, eth, dst, packet, packetlen); + } + + u8 *fpacket = (u8 *) safe_malloc(headerlen + mtu); + memcpy(fpacket, packet, headerlen + mtu); + ip = (struct ip *) fpacket; + + // create fragments and send them + for (fragment = 1; fragment * mtu < datalen + mtu; fragment++) { + fdatalen = (fragment * mtu <= datalen ? mtu : datalen % mtu); + ip->ip_len = htons(headerlen + fdatalen); + ip->ip_off = htons((fragment - 1) * mtu / 8); + if ((fragment - 1) * mtu + fdatalen < datalen) + ip->ip_off |= htons(IP_MF); +#if HAVE_IP_IP_SUM + ip->ip_sum = 0; + ip->ip_sum = in_cksum((unsigned short *) ip, headerlen); +#endif + if (fragment > 1) // copy data payload + memcpy(fpacket + headerlen, + packet + headerlen + (fragment - 1) * mtu, fdatalen); + res = send_ip_packet_eth_or_sd(sd, eth, dst, fpacket, ntohs(ip->ip_len)); + if (res == -1) + break; + } + free(fpacket); + return res; +} + +/* There are three ways to send a raw IPv6 packet. + + send_ipv6_eth works when the device is Ethernet. (Unfortunately IPv6-in-IPv4 + tunnels are not.) We can control all header fields and extension headers. + + send_ipv6_ipproto_raw must be used when IPPROTO_RAW sockets include the IP + header, like IP_HDRINCL for IPv4. This is non-standard but is the case on + Linux. (On other platforms, IPPROTO_RAW has no special meaning and just + stands for protocol 255.) We can control all header fields and extension + headers. This method uses only one raw socket for all sends. + + send_ipv6_ip must be used when IPPROTO_RAW sockets do not include the IP + header. Through standard function calls we can control all header fields + except for the flow label. This method needs one raw socket for every + protocol. (More precisely, one socket per distinct Next Header value.) +*/ + +/* Send an IPv6 packet over an Ethernet handle. */ +static int send_ipv6_eth(const struct eth_nfo *eth, const u8 *packet, unsigned int packetlen) { + eth_t *ethsd; + struct eth_hdr *eth_frame; + u8 *copy; + int res; + + copy = (u8 *) safe_malloc(packetlen + sizeof(*eth_frame)); + memcpy(copy + sizeof(*eth_frame), packet, packetlen); + eth_frame = (struct eth_hdr *) copy; + eth_pack_hdr(eth_frame, eth->dstmac, eth->srcmac, ETH_TYPE_IPV6); + if (!eth->ethsd) { + ethsd = eth_open_cached(eth->devname); + if (!ethsd) + netutil_fatal("%s: Failed to open ethernet device (%s)", __func__, eth->devname); + } else { + ethsd = eth->ethsd; + } + res = eth_send(ethsd, eth_frame, sizeof(*eth_frame) + packetlen); + /* No need to close ethsd due to caching */ + free(eth_frame); + + return res; +} + +#if HAVE_IPV6_IPPROTO_RAW + +/* Send an IPv6 packet over a raw socket, on platforms where IPPROTO_RAW implies + IP_HDRINCL-like behavior. */ +static int send_ipv6_ipproto_raw(const struct sockaddr_in6 *dst, + const unsigned char *packet, unsigned int packetlen) { + int sd, n; + + sd = -1; + n = -1; + + sd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (sd == -1) { + perror("socket"); + goto bail; + } + + n = Sendto(__func__, sd, packet, packetlen, 0, (struct sockaddr *) dst, sizeof(*dst)); + +bail: + if (sd != -1) + close(sd); + + return n; +} + +#elif !WIN32 + +/* Add an ancillary cmsghdr data block to the list of blocks in a msghdr. + The list is stored in msg->msg_control, which is dynamically allocated + and reallocated as needed. It must be freed after this function returns. + msg->msg_controllen is also modified by this function. Returns -1 in case of + error or 0 otherwise. */ +static int add_ancillary(struct msghdr *msg, int level, int type, + const void *data, size_t len) +{ + struct cmsghdr *cm; + void *p; + + p = realloc(msg->msg_control, msg->msg_controllen + CMSG_SPACE(len)); + if (p == NULL) + return -1; + msg->msg_control = p; + + cm = (struct cmsghdr *) ((char *) msg->msg_control + msg->msg_controllen); + msg->msg_controllen += CMSG_SPACE(len); + + cm->cmsg_len = CMSG_LEN(len); + cm->cmsg_level = level; + cm->cmsg_type = type; + + memcpy(CMSG_DATA(cm), data, len); + + return 0; +} + +static int exthdr_type_to_cmsg_type(uint8_t type) { + switch (type) { + /* These are the only extension headers we can set directly through a + msghdr. */ + case 0: + return IPV6_HOPOPTS; + case 43: + return IPV6_RTHDR; + case 60: + return IPV6_DSTOPTS; + default: + return -1; + } +} + +static const unsigned char *add_exthdr_ancillary(struct msghdr *msg, + const unsigned char *p, size_t len, unsigned char *proto) { + unsigned char nxt; + size_t extlen; + int cmsg_type; + + cmsg_type = exthdr_type_to_cmsg_type(*proto); + if (cmsg_type == -1) + return NULL; + + if (len < 2) + return NULL; + nxt = *p; + extlen = (*(p + 1) + 1) * 8; + if (len < extlen) + return NULL; + if (add_ancillary(msg, IPPROTO_IPV6, cmsg_type, p, extlen) == -1) + return NULL; + + *proto = nxt; + + return p + extlen; +} + +/* Send an IPv6 packet over a raw socket. This function can control all header + fields except the flow label (and the payload length can only be controlled + indirectly through the length of the payload). + + For most extension header types, we initialize the socket with the given + protocol, which causes the Next Header field to match when the packet is set. + This allows stuffing arbitrary data into extension headers. However, for a + few well-known headers (like Destination and Routing options), this fails + with EPROTOTYPE because there are specialized functions to add these headers + using the IPv6 socket API. These do not offer as much control because they + are controlled by the OS, and may be reordered, for example. */ +static int send_ipv6_ip(const struct sockaddr_in6 *dst, + const unsigned char *packet, size_t packetlen) { + struct msghdr msg; + struct iovec iov; + + const unsigned char *end; + struct ip6_hdr *hdr; + unsigned char nxt; +#ifdef IPV6_TCLASS + int tclass; +#endif + int hoplimit; + + int sd; + int n; + + sd = -1; + n = -1; + + /* Set up sendmsg data structure. iov is filled in below. */ + msg.msg_name = (void *) dst; + msg.msg_namelen = sizeof(*dst); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (packetlen < sizeof(*hdr)) + return -1; + hdr = (struct ip6_hdr *) packet; + + /* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_TCLASS). */ +#ifdef IPV6_TCLASS + tclass = ntohl(hdr->ip6_flow & IP6_FLOWINFO_MASK) >> 20; + if (add_ancillary(&msg, IPPROTO_IPV6, + IPV6_TCLASS, &tclass, sizeof(tclass)) == -1) { + goto bail; + } +#endif + /* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_UNICAST_HOPS). */ + hoplimit = hdr->ip6_hlim; + if (add_ancillary(&msg, IPPROTO_IPV6, + IPV6_HOPLIMIT, &hoplimit, sizeof(hoplimit)) == -1) { + goto bail; + } + /* The Next Header field is set when the socket is created. The payload + length is set in the call to sendmsg. There's no way to set the flow + label. */ + + /* We must loop until we find a nh value acceptable to the operating system + (one that can be passed as the third parameter to socket). In my tests on + OS X, you get EPROTOTYPE "Protocol wrong type for socket" for + 43 routing + 44 fragment + 50 ESP + 51 AH + 60 DSTOPT + 108 IPcomp + Some of these we are able to handle with ancillary data. When that's + possible, we skip over the header, add the ancillary data, and try again + with the next header. */ + end = packet + packetlen; + packet += sizeof(*hdr); + nxt = hdr->ip6_nxt; + for (;;) { + errno = 0; + sd = socket(AF_INET6, SOCK_RAW, nxt); + if (!(sd == -1 && errno == EPROTOTYPE)) + break; + packet = add_exthdr_ancillary(&msg, packet, end - packet, &nxt); + if (packet == NULL) { + netutil_error("Can't add extension header %u as ancillary data", nxt); + goto bail; + } + } + if (sd == -1) { + perror("socket"); + goto bail; + } + + assert(packet <= end); + iov.iov_base = (unsigned char *) packet; + iov.iov_len = end - packet; + + n = sendmsg(sd, &msg, 0); + if (n == -1) + perror("sendmsg"); + +bail: + free(msg.msg_control); + if (sd != -1) + close(sd); + + return n; +} + +#endif + +/* For now, the sd argument is ignored. */ +int send_ipv6_packet_eth_or_sd(int sd, const struct eth_nfo *eth, + const struct sockaddr_in6 *dst, const u8 *packet, unsigned int packetlen) { + if (eth != NULL) { + return send_ipv6_eth(eth, packet, packetlen); + } else { +#if HAVE_IPV6_IPPROTO_RAW + return send_ipv6_ipproto_raw(dst, packet, packetlen); +#elif !WIN32 + return send_ipv6_ip(dst, packet, packetlen); +#endif + } + + return -1; +} + + + +#ifdef WIN32 +/* Convert a dnet interface name into the long pcap style. This also caches the + data to speed things up. Fills out pcapdev (up to pcapdevlen) and returns + true if it finds anything. Otherwise returns false. This is only necessary + on Windows. */ +int DnetName2PcapName(const char *dnetdev, char *pcapdev, int pcapdevlen) { + static struct NameCorrelationCache { + char dnetd[64]; + char pcapd[128]; + } *NCC = NULL; + static int NCCsz = 0; + static int NCCcapacity = 0; + static struct NameNotFoundCache { + char dnetd[64]; + } *NNFC = NULL; + static int NNFCsz = 0; + static int NNFCcapacity = 0; + int i; + char tmpdev[128]; + + // Init the cache if not done yet + if (!NCC) { + NCCcapacity = 5; + NCC = + (struct NameCorrelationCache *) safe_zalloc(NCCcapacity * + sizeof(*NCC)); + NCCsz = 0; + } + if (!NNFC) { + NNFCcapacity = 5; + NNFC = + (struct NameNotFoundCache *) safe_zalloc(NNFCcapacity * + sizeof(*NNFC)); + NNFCsz = 0; + } + // First check if the name is already in the cache + for (i = 0; i < NCCsz; i++) { + if (strcmp(NCC[i].dnetd, dnetdev) == 0) { + Strncpy(pcapdev, NCC[i].pcapd, pcapdevlen); + return 1; + } + } + // Check if the name is already in the name not found cache + for (i = 0; i < NNFCsz; i++) { + if (strcmp(NNFC[i].dnetd, dnetdev) == 0) { + return 0; + } + } + // OK, so it isn't in the cache. Let's ask dnet for it. + /* Converts a dnet interface name (ifname) to its pcap equivalent, which is stored in + pcapdev (up to a length of pcapdevlen). Returns 1 and fills in pcapdev if successful. */ + if (eth_get_pcap_devname(dnetdev, tmpdev, sizeof(tmpdev)) != 0) { + // We've got it. Let's add it to the not found cache + if (NNFCsz >= NNFCcapacity) { + NNFCcapacity <<= 2; + NNFC = + (struct NameNotFoundCache *) safe_realloc(NNFC, + NNFCcapacity * + sizeof(*NNFC)); + } + Strncpy(NNFC[NNFCsz].dnetd, dnetdev, sizeof(NNFC[0].dnetd)); + NNFCsz++; + return 0; + } + + // We've got it. Let's add it to the cache + if (NCCsz >= NCCcapacity) { + NCCcapacity <<= 2; + NCC = + (struct NameCorrelationCache *) safe_realloc(NCC, + NCCcapacity * + sizeof(*NCC)); + } + Strncpy(NCC[NCCsz].dnetd, dnetdev, sizeof(NCC[0].dnetd)); + Strncpy(NCC[NCCsz].pcapd, tmpdev, sizeof(NCC[0].pcapd)); + NCCsz++; + Strncpy(pcapdev, tmpdev, pcapdevlen); + return 1; +} +#endif + + +/* This function is used to obtain a packet capture handle to look at + * packets on the network. It is actually a wrapper for libpcap's + * pcap_open_live() that takes care of compatibility issues and error + * checking. The function attempts to open the device up to three times. + * If the call does not succeed the third time, NULL is returned. */ +pcap_t *my_pcap_open_live(const char *device, int snaplen, int promisc, int to_ms){ + char err0r[PCAP_ERRBUF_SIZE]; + pcap_t *pt; + char pcapdev[128]; + int failed = 0; + + assert(device != NULL); + +#ifdef WIN32 + /* Nmap normally uses device names obtained through dnet for interfaces, but + Pcap has its own naming system. So the conversion is done here */ + if (!DnetName2PcapName(device, pcapdev, sizeof(pcapdev))) { + /* Oh crap -- couldn't find the corresponding dev apparently. Let's just go + with what we have then ... */ + Strncpy(pcapdev, device, sizeof(pcapdev)); + } +#else + Strncpy(pcapdev, device, sizeof(pcapdev)); +#endif + +#ifdef __amigaos__ + // Amiga doesn't have pcap_create + // TODO: Does Nmap still work on Amiga? + pt = pcap_open_live(pcapdev, snaplen, promisc, to_ms, err0r); + if (!pt) { + netutil_error("pcap_open_live(%s, %d, %d, %d) FAILED. Reported error: %s.", pcapdev, snaplen, promisc, to_ms, err0r); + return NULL; + } +#else + pt = pcap_create(pcapdev, err0r); + if (!pt) { + netutil_error("pcap_create(%s) FAILED: %s.", pcapdev, err0r); + return NULL; + } + +#define MY_PCAP_SET(func, p_t, val) do {\ + failed = func(p_t, val);\ + if (failed) {\ + netutil_error(#func "(%d) FAILED: %d.", val, failed);\ + pcap_close(p_t);\ + return NULL;\ + }\ +} while(0); + + MY_PCAP_SET(pcap_set_snaplen, pt, snaplen); + MY_PCAP_SET(pcap_set_promisc, pt, promisc); + MY_PCAP_SET(pcap_set_timeout, pt, to_ms); +#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE + MY_PCAP_SET(pcap_set_immediate_mode, pt, 1); +#endif + + failed = pcap_activate(pt); + if (failed < 0) { + // PCAP error + netutil_error("pcap_activate(%s) FAILED: %s.", pcapdev, pcap_geterr(pt)); + pcap_close(pt); + return NULL; + } + else if (failed > 0) { + // PCAP warning, report but assume it'll still work + netutil_error("pcap_activate(%s) WARNING: %s.", pcapdev, pcap_geterr(pt)); + } +#endif /* not __amigaos__ */ + +#ifdef WIN32 + /* We want any responses back ASAP */ + /* This is unnecessary with Npcap since libpcap calls PacketSetMinToCopy(0) + * based on immediate mode. Have not determined if it is needed for WinPcap + * or not, but it's not hurting anything. */ + pcap_setmintocopy(pt, 0); + /* Npcap sets kernel buffer size to 1MB, but user buffer size to 256KB. + * Memory is pretty cheap these days, so lets match the kernel buffer size + * for better performance. */ + pcap_setuserbuffer(pt, 1000000); +#endif + + return pt; +} + + +/* Set a pcap filter */ +void set_pcap_filter(const char *device, pcap_t *pd, const char *bpf, ...) { + va_list ap; + int size; + char buf[3072]; + struct bpf_program fcode; + + va_start(ap, bpf); + size = Vsnprintf(buf, sizeof(buf), bpf, ap); + va_end(ap); + if (size >= (int) sizeof(buf)) + netutil_fatal("%s called with too-large filter arg\n", __func__); + + if (pcap_compile(pd, &fcode, buf, 1, PCAP_NETMASK_UNKNOWN) < 0) + netutil_fatal("Error compiling our pcap filter: %s", pcap_geterr(pd)); + if (pcap_setfilter(pd, &fcode) < 0) + netutil_fatal("Failed to set the pcap filter: %s\n", pcap_geterr(pd)); + pcap_freecode(&fcode); +} + + +/* Return the data offset for the given datalink. This function understands the + datalink types DLT_EN10MB and DLT_LINUX_SLL. Returns -1 on error. */ +int datalink_offset(int datalink) +{ + int offset = -1; + /* NOTE: IF A NEW OFFSET EVER EXCEEDS THE CURRENT MAX (24), ADJUST + MAX_LINK_HEADERSZ in libnetutil/netutil.h */ + switch (datalink) { + case DLT_EN10MB: + offset = ETH_HDR_LEN; + break; + case DLT_IEEE802: + offset = 22; + break; +#ifdef __amigaos__ + case DLT_MIAMI: + offset = 16; + break; +#endif +#ifdef DLT_LOOP + case DLT_LOOP: +#endif + case DLT_NULL: + offset = 4; + break; + case DLT_SLIP: +#ifdef DLT_SLIP_BSDOS + case DLT_SLIP_BSDOS: +#endif +#if (FREEBSD || OPENBSD || NETBSD || BSDI || MACOSX) + offset = 16; +#else + offset = 24; /* Anyone use this??? */ +#endif + break; + case DLT_PPP: +#ifdef DLT_PPP_BSDOS + case DLT_PPP_BSDOS: +#endif +#ifdef DLT_PPP_SERIAL + case DLT_PPP_SERIAL: +#endif +#ifdef DLT_PPP_ETHER + case DLT_PPP_ETHER: +#endif +#if (FREEBSD || OPENBSD || NETBSD || BSDI || MACOSX) + offset = 4; +#else +#ifdef SOLARIS + offset = 8; +#else + offset = 24; /* Anyone use this? */ +#endif /* ifdef solaris */ +#endif /* if freebsd || openbsd || netbsd || bsdi */ + break; + case DLT_RAW: + offset = 0; + break; + case DLT_FDDI: + offset = 21; + break; +#ifdef DLT_ENC + case DLT_ENC: + offset = 12; + break; +#endif /* DLT_ENC */ +#ifdef DLT_LINUX_SLL + case DLT_LINUX_SLL: + offset = 16; + break; +#endif +#ifdef DLT_IPNET + case DLT_IPNET: + offset = 24; + break; +#endif + default: + offset = -1; + break; + } + return offset; +} + +/* Common subroutine for reading ARP and NS responses. Input parameters are pd, + to_usec, and accept_callback. If a received frame passes accept_callback, + then the output parameters p, head, rcvdtime, datalink, and offset are filled + in, and the function returns 1. If no frame passes before the timeout, then + the function returns 0 and the output parameters are undefined. */ +int read_reply_pcap(pcap_t *pd, long to_usec, + bool (*accept_callback)(const unsigned char *, const struct pcap_pkthdr *, int, size_t), + const unsigned char **p, struct pcap_pkthdr **head, struct timeval *rcvdtime, + int *datalink, size_t *offset) +{ + static int warning = 0; + int timedout = 0; + int badcounter = 0; + struct timeval tv_start, tv_end; + int ioffset; + + if (!pd) + netutil_fatal("NULL packet device passed to %s", __func__); + + if (to_usec < 0) { + if (!warning) { + warning = 1; + netutil_error("WARNING: Negative timeout value (%lu) passed to %s() -- using 0", to_usec, __func__); + } + to_usec = 0; + } + + /* New packet capture device, need to recompute offset */ + if ((*datalink = pcap_datalink(pd)) < 0) + netutil_fatal("Cannot obtain datalink information: %s", pcap_geterr(pd)); + ioffset = datalink_offset(*datalink); + if (ioffset < 0) + netutil_fatal("datalink_offset failed for type %d (DLT_EN10MB = %d, DLT_LINUX_SLL = %d)", *datalink, DLT_EN10MB, DLT_LINUX_SLL); + *offset = (unsigned int) ioffset; + + if (to_usec > 0) { + gettimeofday(&tv_start, NULL); + } + + do { + + *p = NULL; + int pcap_status = 0; + /* It may be that protecting this with !pcap_selectable_fd_one_to_one is not + necessary, that it is always safe to do a nonblocking read in this way on + all platforms. But I have only tested it on Solaris. */ + if (!pcap_selectable_fd_one_to_one()) { + int rc, nonblock; + + nonblock = pcap_getnonblock(pd, NULL); + assert(nonblock == 0); + rc = pcap_setnonblock(pd, 1, NULL); + assert(rc == 0); + pcap_status = pcap_next_ex(pd, head, p); + rc = pcap_setnonblock(pd, nonblock, NULL); + assert(rc == 0); + } + + if (pcap_status == PCAP_ERROR) { + // TODO: Gracefully end the scan. + netutil_fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd)); + } + + if (pcap_status == 0 || *p == NULL) { + /* Nonblocking pcap_next_ex didn't get anything. */ + if (pcap_select(pd, to_usec) == 0) + timedout = 1; + else + pcap_status = pcap_next_ex(pd, head, p); + } + + if (pcap_status == PCAP_ERROR) { + // TODO: Gracefully end the scan. + netutil_fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd)); + } + + if (pcap_status == 1 && *p != NULL && accept_callback(*p, *head, *datalink, *offset)) { + break; + } else if (pcap_status == 0 || *p == NULL) { + /* Should we timeout? */ + if (to_usec == 0) { + timedout = 1; + } else if (to_usec > 0) { + gettimeofday(&tv_end, NULL); + if (TIMEVAL_SUBTRACT(tv_end, tv_start) >= to_usec) { + timedout = 1; + } + } + } else { + /* We'll be a bit patient if we're getting actual packets back, but + not indefinitely so */ + if (badcounter++ > 50) + timedout = 1; + } + } while (!timedout); + + if (timedout) + return 0; + + if (rcvdtime) { + // TODO: come up with a better way to synchronize pcap with gettimeofday. + // Npcap and WinPcap both suffer from clock drift relative to gettimeofday(). + // We hope to fix this with better time sources for Npcap ( http://issues.nmap.org/1407 ) + // and for Nmap ( http://issues.nmap.org/180 ) + // For now, we use gettimeofday() for Windows in this case. + // Sometimes the time from the pcap header is a + // COUPLE SECONDS before the gettimeofday() results :(. +#if defined(WIN32) || defined(__amigaos__) + gettimeofday(&tv_end, NULL); + *rcvdtime = tv_end; +#else + rcvdtime->tv_sec = (*head)->ts.tv_sec; + rcvdtime->tv_usec = (*head)->ts.tv_usec; + assert((*head)->ts.tv_sec); +#endif + } + + return 1; +} + +static bool accept_arp(const unsigned char *p, const struct pcap_pkthdr *head, + int datalink, size_t offset) +{ + if (head->caplen < offset + 28) + return false; + + /* hw type eth (0x0001), prot ip (0x0800), + hw size (0x06), prot size (0x04) */ + if (memcmp(p + offset, "\x00\x01\x08\x00\x06\x04\x00\x02", 8) != 0) + return false; + + if (datalink == DLT_EN10MB) { + return ntohs(*((u16 *) (p + 12))) == ETH_TYPE_ARP; + } else if (datalink == DLT_LINUX_SLL) { + return ntohs(*((u16 *) (p + 2))) == ARPHRD_ETHER && /* sll_hatype */ + ntohs(*((u16 *) (p + 4))) == 6 && /* sll_halen */ + ntohs(*((u16 *) (p + 14))) == ETH_TYPE_ARP; /* sll_protocol */ + } else { + return false; + } +} + +/* Attempts to read one IPv4/Ethernet ARP reply packet from the pcap + descriptor pd. If it receives one, fills in sendermac (must pass + in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) + and returns 1. If it times out and reads no arp requests, returns + 0. to_usec is the timeout period in microseconds. Use 0 to avoid + blocking to the extent possible. Returns -1 or exits if there is + an error. The last parameter is a pointer to a callback function + that can be used for packet tracing. This is intended to be used + by Nmap only. Any other calling this should pass NULL instead. */ +int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, + struct in_addr *senderIP, long to_usec, + struct timeval *rcvdtime, + void (*trace_callback)(int, const u8 *, u32, struct timeval *)) { + const unsigned char *p; + struct pcap_pkthdr *head; + int datalink; + size_t offset; + int rc; + + rc = read_reply_pcap(pd, to_usec, accept_arp, &p, &head, rcvdtime, &datalink, &offset); + if (rc == 0) + return 0; + + memcpy(sendermac, p + offset + 8, 6); + /* I think alignment should allow this ... */ + memcpy(&senderIP->s_addr, p + offset + 14, 4); + + if (trace_callback != NULL) { + /* TODO: First parameter "2" is a hardcoded value for Nmap's PacketTrace::RECV. */ + trace_callback(2, (u8 *) p + offset, ARP_HDR_LEN + ARP_ETHIP_LEN, rcvdtime); + } + + return 1; +} + +static bool accept_ns(const unsigned char *p, const struct pcap_pkthdr *head, + int datalink, size_t offset) +{ + struct icmpv6_hdr *icmp6_header; + + if (head->caplen < offset + IP6_HDR_LEN + ICMPV6_HDR_LEN) + return false; + + icmp6_header = (struct icmpv6_hdr *)(p + offset + IP6_HDR_LEN); + return icmp6_header->icmpv6_type == ICMPV6_NEIGHBOR_ADVERTISEMENT && + icmp6_header->icmpv6_code == 0; +} + +/* Attempts to read one IPv6/Ethernet Neighbor Solicitation reply packet from the pcap + descriptor pd. If it receives one, fills in sendermac (must pass + in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) + and returns 1. If it times out and reads no Neighbor Advertisement, returns + 0. to_usec is the timeout period in microseconds. Use 0 to avoid + blocking to the extent possible. Returns -1 or exits if there is + an error. The last parameter is a pointer to a callback function + that can be used for packet tracing. This is intended to be used + by Nmap only. Any other calling this should pass NULL instead. */ +int read_ns_reply_pcap(pcap_t *pd, u8 *sendermac, + struct sockaddr_in6 *senderIP, long to_usec, + struct timeval *rcvdtime, bool *has_mac, + void (*trace_callback)(int, const u8 *, u32, struct timeval *)) { + const unsigned char *p; + struct pcap_pkthdr *head; + int datalink; + size_t offset; + int rc; + struct icmpv6_msg_nd *na; + + rc = read_reply_pcap(pd, to_usec, accept_ns, &p, &head, rcvdtime, &datalink, &offset); + if (rc == 0) + return 0; + + na = (struct icmpv6_msg_nd *)(p + offset + IP6_HDR_LEN + ICMPV6_HDR_LEN); + if (head->caplen >= ((unsigned char *)na - p) + sizeof(struct icmpv6_msg_nd) && + na->icmpv6_option_type == 2 && + na->icmpv6_option_length == 1) { + *has_mac = true; + memcpy(sendermac, &na->icmpv6_mac, 6); + } + else { + *has_mac = false; + } + senderIP->sin6_family = AF_INET6; + memcpy(&senderIP->sin6_addr.s6_addr, &na->icmpv6_target, 16); + + if (trace_callback != NULL) { + /* TODO: First parameter "2" is a hardcoded value for Nmap's PacketTrace::RECV. */ + trace_callback(2, (u8 *) p + offset, IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8, rcvdtime); + } + + return 1; +} + + +/* Issues an Neighbor Solicitation for the MAC of targetss (which will be placed + in targetmac if obtained) from the source IP (srcip) and source mac + (srcmac) given. "The request is ussued using device dev to the + multicast MAC address. The transmission is attempted up to 3 + times. If none of these elicit a response, false will be returned. + If the mac is determined, true is returned. The last parameter is + a pointer to a callback function that can be used for packet tracing. + This is intended to be used by Nmap only. Any other calling this + should pass NULL instead. */ +bool doND(const char *dev, const u8 *srcmac, + const struct sockaddr_storage *srcip, + const struct sockaddr_storage *targetip, + u8 *targetmac, + void (*traceND_callback)(int, const u8 *, u32 , struct timeval *) + ) { + /* timeouts in microseconds ... the first ones are retransmit times, while + the final one is when we give up */ + int timeouts[] = { 100000, 400000, 800000 }; + int max_sends = 3; + int num_sends = 0; // How many we have sent so far + eth_t *ethsd; + u8 frame[ETH_HDR_LEN + IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8]; + struct timeval start, now, rcvdtime; + int timeleft; + int listenrounds; + int rc; + bool has_mac; + pcap_t *pd; + struct sockaddr_storage rcvdIP; + rcvdIP.ss_family = AF_INET6; + bool foundit = false; + char filterstr[256]; + struct sockaddr_in6 *target_sin6, *src_sin6; + struct sockaddr_in6 ns_dst_ip6; + + if (targetip->ss_family != AF_INET6 || srcip->ss_family != AF_INET6) + netutil_fatal("%s can only handle IPv6 addresses", __func__); + + target_sin6 = (struct sockaddr_in6 *) targetip; + src_sin6 = (struct sockaddr_in6 *) srcip; + + unsigned char ns_dst_mac[6] = {0x33, 0x33, 0xff}; + ns_dst_mac[3] = target_sin6->sin6_addr.s6_addr[13]; + ns_dst_mac[4] = target_sin6->sin6_addr.s6_addr[14]; + ns_dst_mac[5] = target_sin6->sin6_addr.s6_addr[15]; + + ns_dst_ip6 = *target_sin6; + unsigned char multicast_prefix[13] = {0}; + multicast_prefix[0] = 0xff; + multicast_prefix[1] = 0x02; + multicast_prefix[11] = 0x1; + multicast_prefix[12] = 0xff; + memcpy(ns_dst_ip6.sin6_addr.s6_addr, multicast_prefix, sizeof(multicast_prefix)); + + /* Start listening */ + if((pd=my_pcap_open_live(dev, 100, 1, 25))==NULL) + netutil_fatal("my_pcap_open_live(%s, 50, 1, 25) failed three times.", dev); + /* Libpcap: IPv6 upper-layer protocol is not supported by proto[x] */ + /* Grab the ICMPv6 type using ip6[X:Y] syntax. This works only if there are no + extension headers (top-level nh is IPPROTO_ICMPV6). */ + Snprintf(filterstr, 256, "ether dst %02X%02X%02X%02X%02X%02X and icmp6 and ip6[6:1] = %u and ip6[40:1] = %u", + srcmac[0], srcmac[1], srcmac[2], srcmac[3], srcmac[4], srcmac[5], + IPPROTO_ICMPV6, ICMPV6_NEIGHBOR_ADVERTISEMENT); + set_pcap_filter(dev, pd, filterstr); + + /* Prepare probe and sending stuff */ + ethsd = eth_open_cached(dev); + if (!ethsd) + netutil_fatal("%s: failed to open device %s", __func__, dev); + eth_pack_hdr(frame, *ns_dst_mac, *srcmac, ETH_TYPE_IPV6); + ip6_pack_hdr(frame + ETH_HDR_LEN, 0, 0, 32, 0x3a, 255, *src_sin6->sin6_addr.s6_addr, *ns_dst_ip6.sin6_addr.s6_addr); + icmpv6_pack_hdr_ns_mac(frame + ETH_HDR_LEN + IP6_HDR_LEN, target_sin6->sin6_addr.s6_addr, *srcmac); + ip6_checksum(frame + ETH_HDR_LEN, IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8); + + gettimeofday(&start, NULL); + gettimeofday(&now, NULL); + + while (!foundit && num_sends < max_sends) { + /* Send the sucker */ + rc = eth_send(ethsd, frame, sizeof(frame)); + if (rc != sizeof(frame)) { + netutil_error("WARNING: %s: eth_send of Neighbor Solicitation packet returned %u rather than expected %d bytes", __func__, rc, (int) sizeof(frame)); + } + if(traceND_callback!=NULL){ + /* TODO: First parameter "1" is a hardcoded value for Nmap's PacketTrace::SENT*/ + traceND_callback(1, (u8 *) frame + ETH_HDR_LEN, IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8, &now); + } + num_sends++; + + listenrounds = 0; + while (!foundit) { + gettimeofday(&now, NULL); + timeleft = timeouts[num_sends - 1] - TIMEVAL_SUBTRACT(now, start); + if (timeleft < 0) { + if (listenrounds > 0) + break; + else + timeleft = 25000; + } + listenrounds++; + /* Now listen until we reach our next timeout or get an answer */ + rc = read_ns_reply_pcap(pd, targetmac, (struct sockaddr_in6 *) &rcvdIP, timeleft, + &rcvdtime, &has_mac, traceND_callback); + if (rc == -1) + netutil_fatal("%s: Received -1 response from read_ns_reply_pcap", __func__); + if (rc == 1) { + /* Yay, I got one! But is it the right one? */ + if (sockaddr_storage_cmp(&rcvdIP,targetip) != 0) + continue; /* D'oh! */ + foundit = true; /* WOOHOO! */ + } + } + } + + /* OK - let's close up shop ... */ + pcap_close(pd); + /* No need to close ethsd due to caching */ + return foundit; +} + +/* Issues an ARP request for the MAC of targetss (which will be placed + in targetmac if obtained) from the source IP (srcip) and source mac + (srcmac) given. "The request is ussued using device dev to the + broadcast MAC address. The transmission is attempted up to 3 + times. If none of these elicit a response, false will be returned. + If the mac is determined, true is returned. The last parameter is + a pointer to a callback function that can be used for packet tracing. + This is intended to be used by Nmap only. Any other calling this + should pass NULL instead. */ +bool doArp(const char *dev, const u8 *srcmac, + const struct sockaddr_storage *srcip, + const struct sockaddr_storage *targetip, + u8 *targetmac, + void (*traceArp_callback)(int, const u8 *, u32 , struct timeval *) + ) { + /* timeouts in microseconds ... the first ones are retransmit times, while + the final one is when we give up */ + int timeouts[] = { 100000, 400000, 800000 }; + int max_sends = 3; + int num_sends = 0; // How many we have sent so far + eth_t *ethsd; + u8 frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN]; + const struct sockaddr_in *targetsin = (struct sockaddr_in *) targetip; + const struct sockaddr_in *srcsin = (struct sockaddr_in *) srcip; + struct timeval start, now, rcvdtime; + int timeleft; + int listenrounds; + int rc; + pcap_t *pd; + struct in_addr rcvdIP; + bool foundit = false; + char filterstr[256]; + + if (targetsin->sin_family != AF_INET || srcsin->sin_family != AF_INET) + netutil_fatal("%s can only handle IPv4 addresses", __func__); + + /* Start listening */ + if((pd=my_pcap_open_live(dev, 50, 1, 25))==NULL) + netutil_fatal("my_pcap_open_live(%s, 50, 1, 25) failed three times.", dev); + Snprintf(filterstr, 256, "arp and arp[18:4] = 0x%02X%02X%02X%02X and arp[22:2] = 0x%02X%02X", + srcmac[0], srcmac[1], srcmac[2], srcmac[3], srcmac[4], srcmac[5]); + set_pcap_filter(dev, pd, filterstr); + + /* Prepare probe and sending stuff */ + ethsd = eth_open_cached(dev); + if (!ethsd) + netutil_fatal("%s: failed to open device %s", __func__, dev); + eth_pack_hdr(frame, ETH_ADDR_BROADCAST, *srcmac, ETH_TYPE_ARP); + arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST, *srcmac, + srcsin->sin_addr, ETH_ADDR_BROADCAST, + targetsin->sin_addr); + gettimeofday(&start, NULL); + gettimeofday(&now, NULL); + + while (!foundit && num_sends < max_sends) { + /* Send the sucker */ + rc = eth_send(ethsd, frame, sizeof(frame)); + if (rc != sizeof(frame)) { + netutil_error("WARNING: %s: eth_send of ARP packet returned %u rather than expected %d bytes", __func__, rc, (int) sizeof(frame)); + } + if(traceArp_callback!=NULL){ + /* TODO: First parameter "1" is a hardcoded value for Nmap's PacketTrace::SENT*/ + traceArp_callback(1, (u8 *) frame + ETH_HDR_LEN, ARP_HDR_LEN + ARP_ETHIP_LEN, &now); + } + num_sends++; + + listenrounds = 0; + while (!foundit) { + gettimeofday(&now, NULL); + timeleft = timeouts[num_sends - 1] - TIMEVAL_SUBTRACT(now, start); + if (timeleft < 0) { + if (listenrounds > 0) + break; + else + timeleft = 25000; + } + listenrounds++; + /* Now listen until we reach our next timeout or get an answer */ + rc = read_arp_reply_pcap(pd, targetmac, &rcvdIP, timeleft, + &rcvdtime, traceArp_callback); + if (rc == -1) + netutil_fatal("%s: Received -1 response from readarp_reply_pcap", __func__); + if (rc == 1) { + /* Yay, I got one! But is it the right one? */ + if (rcvdIP.s_addr != targetsin->sin_addr.s_addr) + continue; /* D'oh! */ + foundit = true; /* WOOHOO! */ + } + } + } + + /* OK - let's close up shop ... */ + pcap_close(pd); + /* No need to close ethsd due to caching */ + return foundit; +} + + + +static inline bool is_host_separator(int c) { + return c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '\0'; +} + +/* Read a single host specification from a file, as for -iL and --excludefile. + It returns the length of the string read; an overflow is indicated when the + return value is >= n. Returns 0 if there was no specification to be read. The + buffer is always null-terminated. */ +size_t read_host_from_file(FILE *fp, char *buf, size_t n) +{ + int ch; + size_t i; + + i = 0; + ch = getc(fp); + while (is_host_separator(ch) || ch == '#') { + if (ch == '#') { + /* Skip comments to the end of the line. */ + while ((ch = getc(fp)) != EOF && ch != '\n') + ; + } else { + ch = getc(fp); + } + } + while (ch != EOF && !(is_host_separator(ch) || ch == '#')) { + if (i < n) + buf[i] = ch; + i++; + ch = getc(fp); + } + if (ch != EOF) + ungetc(ch, fp); + if (i < n) + buf[i] = '\0'; + else if (n > 0) + /* Null-terminate even though it was too long. */ + buf[n - 1] = '\0'; + + return i; +} + + +/* Return next target host specification from the supplied stream. + * if parameter "random" is set to true, then the function will + * return a random, non-reserved, IP address in decimal-dot notation */ +const char *grab_next_host_spec(FILE *inputfd, bool random, int argc, const char **argv) { + static char host_spec[1024]; + struct in_addr ip; + size_t n; + + if (random) { + do { + ip.s_addr = get_random_unique_u32(); + } while (ip_is_reserved(&ip)); + Strncpy(host_spec, inet_ntoa(ip), sizeof(host_spec)); + } else if (!inputfd) { + return( (optind < argc)? argv[optind++] : NULL); + } else { + n = read_host_from_file(inputfd, host_spec, sizeof(host_spec)); + if (n == 0) + return NULL; + else if (n >= sizeof(host_spec)) + netutil_fatal("One of the host specifications from your input file is too long (>= %u chars)", (unsigned int) sizeof(host_spec)); + } + return host_spec; +} + + + +/** Tries to increase the open file descriptor limit for this process. + * @param "desired" is the number of desired max open descriptors. Pass a + * negative value to set the maximum allowed. + * @return the number of max open descriptors that could be set, or 0 in case + * of failure. + * @warning if "desired" is less than the current limit, no action is + * performed. This function may only be used to increase the limit, not to + * decrease it. */ +int set_max_open_descriptors(int desired_max) { + #ifndef WIN32 + struct rlimit r; + int maxfds=-1; + int flag=0; + + #if (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) + + #ifdef RLIMIT_NOFILE + flag=RLIMIT_NOFILE; /* Linux */ + #else + flag=RLIMIT_OFILE; /* BSD */ + #endif + + if (!getrlimit(flag, &r)) { + /* If current limit is less than the desired, try to increase it */ + if(r.rlim_cur < (rlim_t)desired_max){ + if(desired_max<0){ + r.rlim_cur=r.rlim_max; /* Set maximum */ + }else{ + r.rlim_cur = MIN( (int)r.rlim_max, desired_max ); + } + if (setrlimit(flag, &r)) + ; // netutil_debug("setrlimit(%d, %p) failed", flag, r); + if (!getrlimit(flag, &r)) { + maxfds = r.rlim_cur; + return maxfds; + }else { + return 0; + } + } + } + + #endif /* (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) */ + #endif /* !WIN32 */ + return 0; +} + + +/** Returns the open file descriptor limit for this process. + * @return the number of max open descriptors or 0 in case of failure. */ +int get_max_open_descriptors() { + #ifndef WIN32 + struct rlimit r; + int flag=0; + + #if (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) + + #ifdef RLIMIT_NOFILE + flag=RLIMIT_NOFILE; /* Linux */ + #else + flag=RLIMIT_OFILE; /* BSD */ + #endif + + if (!getrlimit(flag, &r)) { + return (int)r.rlim_cur; + } + + #endif /* (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) */ + #endif /* !WIN32 */ + return 0; +} + + +/* Maximize the open file descriptor limit for this process go up to the + max allowed */ +int max_sd() { + return set_max_open_descriptors(-1); +} |