diff --git a/NmapOps.cc b/NmapOps.cc index 62464942a..23e62567f 100644 --- a/NmapOps.cc +++ b/NmapOps.cc @@ -131,6 +131,14 @@ int NmapOps::SourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len) { return 0; } +/* Returns a const pointer to the source address if set, or NULL if unset. */ +const struct sockaddr_storage *NmapOps::SourceSockAddr() const { + if (sourcesock.ss_family == AF_UNSPEC) + return NULL; + else + return &sourcesock; +} + /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted to sockaddr_storage */ void NmapOps::setSourceSockAddr(struct sockaddr_storage *ss, size_t ss_len) { @@ -139,22 +147,6 @@ void NmapOps::setSourceSockAddr(struct sockaddr_storage *ss, size_t ss_len) { sourcesocklen = ss_len; } -struct in_addr NmapOps::v4source() { - const struct in_addr *addy = v4sourceip(); - struct in_addr in; - if (addy) return *addy; - in.s_addr = 0; - return in; -} - -const struct in_addr *NmapOps::v4sourceip() { - struct sockaddr_in *sin = (struct sockaddr_in *) &sourcesock; - if (sin->sin_family == AF_INET) { - return &(sin->sin_addr); - } - return NULL; -} - // Number of seconds since getStartTime(). The current time is an // optional argument to avoid an extra gettimeofday() call. float NmapOps::TimeSinceStart(const struct timeval *now) { @@ -319,9 +311,9 @@ bool NmapOps::RawScan() { return true; if (pingtype & (PINGTYPE_ICMP_PING|PINGTYPE_ICMP_MASK|PINGTYPE_ICMP_TS|PINGTYPE_TCP_USE_ACK|PINGTYPE_UDP|PINGTYPE_SCTP_INIT)) return true; - /* A SYN scan will only generate raw packets if nmap is running as root and is - not issuing IPv6 packets. Otherwise, it becomes a connect scan. */ - if ((pingtype & PINGTYPE_TCP_USE_SYN) && (af() == AF_INET) && isr00t) + /* A SYN scan will only generate raw packets if nmap is running as root. + Otherwise, it becomes a connect scan. */ + if ((pingtype & PINGTYPE_TCP_USE_SYN) && isr00t) return true; return false; @@ -346,7 +338,7 @@ dialog where you can start NPF if you have administrator privileges."; /* Insure that at least one scantype is selected */ if (!noportscan && !(TCPScan() || UDPScan() || SCTPScan() || ipprotscan)) { - if (isr00t && af() == AF_INET) + if (isr00t) synscan++; else connectscan++; // if (verbose) error("No TCP, UDP, SCTP or ICMP scantype specified, assuming %s scan. Use -sn if you really don't want to portscan (and just want to see what hosts are up).", synscan? "SYN Stealth" : "vanilla tcp connect()"); @@ -369,8 +361,8 @@ dialog where you can start NPF if you have administrator privileges."; error("WARNING: -S will only affect the source address used in a connect() scan if you specify one of your own addresses. Use -sS or another raw scan if you want to completely spoof your source address, but then you need to know what you're doing to obtain meaningful results."); } - if ((pingtype & PINGTYPE_UDP) && (!isr00t || af() != AF_INET)) { - fatal("Sorry, UDP Ping (-PU) only works if you are root (because we need to read raw responses off the wire) and only for IPv4 (cause fyodor is too lazy right now to add IPv6 support and nobody has sent a patch)"); + if ((pingtype & PINGTYPE_UDP) && (!isr00t)) { + fatal("Sorry, UDP Ping (-PU) only works if you are root (because we need to read raw responses off the wire)"); } if ((pingtype & PINGTYPE_SCTP_INIT) && (!isr00t || af() != AF_INET)) { @@ -389,8 +381,8 @@ dialog where you can start NPF if you have administrator privileges."; fatal("-sL and -sn (skip port scan) are not valid with any other scan types"); } - if (af() == AF_INET6 && (pingtype & (PINGTYPE_ICMP_PING|PINGTYPE_ICMP_MASK|PINGTYPE_ICMP_TS))) { - fatal("ICMP Echo, Timestamp and Address Mask pings are only valid for IPv4."); + if (af() == AF_INET6 && (pingtype & (PINGTYPE_ICMP_MASK|PINGTYPE_ICMP_TS))) { + fatal("ICMP Timestamp and Address Mask pings are only valid for IPv4."); } if (sendpref == PACKET_SEND_NOPREF) { @@ -482,8 +474,8 @@ dialog where you can start NPF if you have administrator privileges."; fatal("--min-rate=%g must be less than or equal to --max-rate=%g", min_packet_send_rate, max_packet_send_rate); } - if (af() == AF_INET6 && (generate_random_ips|numdecoys|osscan|bouncescan|fragscan|ackscan|finscan|idlescan|ipprotscan|maimonscan|nullscan|synscan|udpscan|windowscan|xmasscan|sctpinitscan|sctpcookieechoscan)) { - fatal("Sorry -- IPv6 support is currently only available for connect() scan (-sT), ping scan (-sn), and list scan (-sL). OS detection, random targets and decoys are also not supported with IPv6. Further support is under consideration."); + if (af() == AF_INET6 && (generate_random_ips|numdecoys|osscan|bouncescan|fragscan|idlescan|ipprotscan)) { + fatal("Sorry -- IPv6 support is currently only available for TCP, UDP, and SCTP port scans and list scan (-sL). OS detection, random targets and decoys are also not supported with IPv6. Further support is under consideration."); } /* Prevent performance values from getting out of whack */ diff --git a/NmapOps.h b/NmapOps.h index ae00577cc..12e486ce6 100644 --- a/NmapOps.h +++ b/NmapOps.h @@ -107,6 +107,8 @@ class NmapOps { /* Returns 0 for success, nonzero if no source has been set or any other failure */ int SourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len); + /* Returns a const pointer to the source address if set, or NULL if unset. */ + const struct sockaddr_storage *SourceSockAddr() const; /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted to sockaddr_storage */ void setSourceSockAddr(struct sockaddr_storage *ss, size_t ss_len); @@ -116,8 +118,6 @@ class NmapOps { // Number of seconds since getStartTime(). The current time is an // optional argument to avoid an extra gettimeofday() call. float TimeSinceStart(const struct timeval *now=NULL); - struct in_addr v4source(); - const struct in_addr *v4sourceip(); diff --git a/Target.cc b/Target.cc index 31c368d87..14f7aa07c 100644 --- a/Target.cc +++ b/Target.cc @@ -195,6 +195,11 @@ void Target::GenerateIPString() { } } +/* Returns the address family of the destination address. */ +int Target::af() const { + return targetsock.ss_family; +} + /* Fills a sockaddr_storage with the AF_INET or AF_INET6 address information of the target. This is a preferred way to get the address since it is portable for IPv6 hosts. Returns 0 for @@ -211,6 +216,10 @@ int Target::TargetSockAddr(struct sockaddr_storage *ss, size_t *ss_len) const { return 0; } +const struct sockaddr_storage *Target::TargetSockAddr() const { + return &targetsock; +} + /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted to sockaddr_storage */ void Target::setTargetSockAddr(const struct sockaddr_storage *ss, size_t ss_len) { @@ -247,6 +256,14 @@ const struct in_addr *Target::v4hostip() const { return NULL; } +const struct in6_addr *Target::v6hostip() const { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &targetsock; + if (sin6->sin6_family == AF_INET6) { + return &(sin6->sin6_addr); + } + return NULL; +} + /* The source address used to reach the target */ int Target::SourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len) const { if (sourcesocklen <= 0) @@ -259,6 +276,10 @@ int Target::SourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len) const { return 0; } +const struct sockaddr_storage *Target::SourceSockAddr() const { + return &sourcesock; +} + /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted to sockaddr_storage */ void Target::setSourceSockAddr(const struct sockaddr_storage *ss, size_t ss_len) { diff --git a/Target.h b/Target.h index a854549b4..0bd75ceec 100644 --- a/Target.h +++ b/Target.h @@ -158,20 +158,25 @@ class Target { /* Recycles the object by freeing internal objects and reinitializing to default state */ void Recycle(); + /* Returns the address family of the destination address. */ + int af() const; /* Fills a sockaddr_storage with the AF_INET or AF_INET6 address information of the target. This is a preferred way to get the address since it is portable for IPv6 hosts. Returns 0 for success. ss_len must be provided. It is not examined, but is set to the size of the sockaddr copied in. */ int TargetSockAddr(struct sockaddr_storage *ss, size_t *ss_len) const; + const struct sockaddr_storage *TargetSockAddr() const; /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted to sockaddr_storage */ void setTargetSockAddr(const struct sockaddr_storage *ss, size_t ss_len); // Returns IPv4 target host address or {0} if unavailable. struct in_addr v4host() const; const struct in_addr *v4hostip() const; + const struct in6_addr *v6hostip() const; /* The source address used to reach the target */ int SourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len) const; + const struct sockaddr_storage *SourceSockAddr() const; /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted to sockaddr_storage */ void setSourceSockAddr(const struct sockaddr_storage *ss, size_t ss_len); diff --git a/acinclude.m4 b/acinclude.m4 index 8404c1c0e..3133a1219 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -82,3 +82,21 @@ int main() { [AC_MSG_RESULT(cross-compiling -- assuming yes); $3]) ]) ]) + +dnl Checks if IPPROTO_RAW induces IP_HDRINCL-like behavior in AF_INET6 sockets. +dnl Defines HAVE_IPV6_IPPROTO_RAW if so. So far I only know this happens on +dnl Linux. +AC_DEFUN([CHECK_IPV6_IPPROTO_RAW], +[ + AC_MSG_CHECKING(if AF_INET6 IPPROTO_RAW sockets include the packet header) + # This should be replaced with a better test, if possible. + case "$host" in + *-linux*) + AC_DEFINE(HAVE_IPV6_IPPROTO_RAW) + AC_MSG_RESULT(yes) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac +]) diff --git a/configure b/configure index e3f313bf1..878d9513c 100755 --- a/configure +++ b/configure @@ -5103,6 +5103,19 @@ fi done +for ac_header in linux/rtnetlink.h +do : + ac_fn_c_check_header_compile "$LINENO" "linux/rtnetlink.h" "ac_cv_header_linux_rtnetlink_h" "#include +" +if test "x$ac_cv_header_linux_rtnetlink_h" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LINUX_RTNETLINK_H 1 +_ACEOF + +fi + +done + for ac_header in sys/socket.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" @@ -5254,6 +5267,24 @@ if test "$ac_res" != no; then : fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if AF_INET6 IPPROTO_RAW sockets include the packet header" >&5 +$as_echo_n "checking if AF_INET6 IPPROTO_RAW sockets include the packet header... " >&6; } + # This should be replaced with a better test, if possible. + case "$host" in + *-linux*) + $as_echo "#define HAVE_IPV6_IPPROTO_RAW 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac + + NDIFFDIR=ndiff # Do they want Ndiff? diff --git a/configure.ac b/configure.ac index 564e666a6..b95a17cd7 100644 --- a/configure.ac +++ b/configure.ac @@ -173,6 +173,7 @@ AC_SUBST(LUA_CFLAGS) dnl Checks for header files. AC_CHECK_HEADERS(pwd.h termios.h sys/sockio.h) +AC_CHECK_HEADERS(linux/rtnetlink.h,,,[#include ]) dnl A special check required for on Darwin. See dnl http://www.gnu.org/software/autoconf/manual/html_node/Header-Portability.html. AC_CHECK_HEADERS([sys/socket.h]) @@ -195,6 +196,9 @@ dnl If any socket libraries needed AC_SEARCH_LIBS(setsockopt, socket) AC_SEARCH_LIBS(gethostbyname, nsl) +dnl Check IPv6 raw sending flavor. +CHECK_IPV6_IPPROTO_RAW + NDIFFDIR=ndiff # Do they want Ndiff? diff --git a/docs/nmap.usage.txt b/docs/nmap.usage.txt index 08927d14a..1dd646507 100644 --- a/docs/nmap.usage.txt +++ b/docs/nmap.usage.txt @@ -1,4 +1,4 @@ -Nmap 5.51SVN ( http://nmap.org ) +Nmap 5.52.IPv6.Beta1 ( http://nmap.org ) Usage: nmap [Scan Type(s)] [Options] {target specification} TARGET SPECIFICATION: Can pass hostnames, IP addresses, networks, etc. diff --git a/docs/refguide.xml b/docs/refguide.xml index 1b9d1ec52..53a692288 100644 --- a/docs/refguide.xml +++ b/docs/refguide.xml @@ -558,9 +558,7 @@ you would expect. underlying TCP stack must have received a SYN/ACK or RST and the host is marked available. If the connection attempt is left hanging until a timeout is reached, the host is - marked as down. This workaround is also used for IPv6 - connections, as raw IPv6 packet building support is not yet - available in Nmap.IPv6limitations of + marked as down. @@ -725,9 +723,6 @@ you would expect. packets.raw packets Using SCTP INIT Pings is currently not possible for unprivileged users.unprivileged userslimitations of - The same limitation applies to IPv6, which is currently not - supported for - SCTP INIT Ping.IPv6limitations of @@ -1125,7 +1120,7 @@ name, usually the first. The one exception to this is the deprecated FTP bounce scan (). By default, Nmap performs a SYN Scan, though it substitutes a connect scan if the user does not have proper privileges to send raw packets (requires root access on -Unix) or if IPv6 targets were specified. Of the scans listed in this +Unix). Of the scans listed in this section, unprivileged users can only execute connect and FTP bounce scans. @@ -1170,7 +1165,7 @@ error (type 3, code 1, 2, 3, 9, 10, or 13) is received. The port is also consid TCP connect scan is the default TCP scan type when SYN scan is not an option. This is the case when a user does not have raw packet -privileges or is scanning IPv6 networks. Instead of writing raw +privileges. Instead of writing raw packets as most other scan types do, Nmap asks the underlying operating system to establish a connection with the target machine and port by issuing the connect system call. This is @@ -3990,7 +3985,7 @@ hosts with at least one Since 2002, Nmap has offered IPv6 support for its most popular features. In particular, ping scanning (TCP-only), - connect scanning, version detection, and the Nmap Scripting + port scanning, version detection, and the Nmap Scripting Engine all support IPv6. The command syntax is the same as usual except that you also add the option. Of course, you must use IPv6 syntax if you specify diff --git a/idle_scan.cc b/idle_scan.cc index 687d3aca6..f01239426 100644 --- a/idle_scan.cc +++ b/idle_scan.cc @@ -375,7 +375,7 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, proxy->ethptr = &proxy->eth; } else { #ifdef WIN32 - win32_warn_raw_sockets(proxy->host.deviceName()); + win32_fatal_raw_sockets(proxy->host.deviceName()); #endif if ((proxy->rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 ) pfatal("socket troubles in %s", __func__); @@ -1042,9 +1042,9 @@ void idle_scan(Target *target, u16 *portarray, int numports, for(portidx = 0; portidx < numports; portidx++) { if (target->ports.portIsDefault(portarray[portidx], IPPROTO_TCP)) { target->ports.setPortState(portarray[portidx], IPPROTO_TCP, PORT_CLOSEDFILTERED); - target->ports.setStateReason(portarray[portidx], IPPROTO_TCP, ER_NOIPIDCHANGE, 0, 0); + target->ports.setStateReason(portarray[portidx], IPPROTO_TCP, ER_NOIPIDCHANGE, 0, NULL); } else - target->ports.setStateReason(portarray[portidx], IPPROTO_TCP, ER_IPIDCHANGE, 0, 0); + target->ports.setStateReason(portarray[portidx], IPPROTO_TCP, ER_IPIDCHANGE, 0, NULL); } target->stopTimeOutClock(NULL); diff --git a/libdnet-stripped/include/config.h.in b/libdnet-stripped/include/config.h.in index f79665183..304e4ad41 100644 --- a/libdnet-stripped/include/config.h.in +++ b/libdnet-stripped/include/config.h.in @@ -303,3 +303,15 @@ char *strsep(char **, const char *); #ifndef HAVE_SOCKLEN_T typedef int socklen_t; #endif + +/* Unix Network Programming, 3rd edition says that sockaddr structures in + rt_msghdr should be padded so their addresses start on a multiple of + sizeof(u_long). But on 64-bit Mac OS X 10.6 at least, this is false. Apple's + netstat code uses 4-byte padding, not 8-byte. This is relevant for IPv6 + addresses, for which sa_len == 28. + http://www.opensource.apple.com/source/network_cmds/network_cmds-329.2.2/netstat.tproj/route.c */ +#ifdef __APPLE__ +#define RT_MSGHDR_ALIGNMENT sizeof(uint32_t) +#else +#define RT_MSGHDR_ALIGNMENT sizeof(unsigned long) +#endif diff --git a/libdnet-stripped/include/dnet.h b/libdnet-stripped/include/dnet.h index 0af5763eb..ca006479e 100644 --- a/libdnet-stripped/include/dnet.h +++ b/libdnet-stripped/include/dnet.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/libdnet-stripped/include/dnet/icmpv6.h b/libdnet-stripped/include/dnet/icmpv6.h new file mode 100644 index 000000000..2394f184c --- /dev/null +++ b/libdnet-stripped/include/dnet/icmpv6.h @@ -0,0 +1,113 @@ +/* + * icmpv6.h + * + * ICMPv6. + * RFC 4443 + * + * $Id: $ + */ + +#ifndef DNET_ICMPV6_H +#define DNET_ICMPV6_H + +#define ICMPV6_HDR_LEN 4 /* base ICMPv6 header length */ + +#ifndef __GNUC__ +#ifndef __attribute__ +# define __attribute__(x) +#endif +# pragma pack(1) +#endif + +/* + * ICMPv6 header + */ +struct icmpv6_hdr { + uint8_t icmpv6_type; /* type of message, see below */ + uint8_t icmpv6_code; /* type sub code */ + uint16_t icmpv6_cksum; /* ones complement cksum of struct */ +}; + +/* + * Types (icmpv6_type) and codes (icmpv6_code) - + * http://www.iana.org/assignments/icmpv6-parameters + */ +#define ICMPV6_CODE_NONE 0 /* for types without codes */ +#define ICMPV6_UNREACH 1 /* dest unreachable, codes: */ +#define ICMPV6_UNREACH_NOROUTE 0 /* no route to dest */ +#define ICMPV6_UNREACH_PROHIB 1 /* admin prohibited */ +#define ICMPV6_UNREACH_SCOPE 2 /* beyond scope of source address */ +#define ICMPV6_UNREACH_ADDR 3 /* address unreach */ +#define ICMPV6_UNREACH_PORT 4 /* port unreach */ +#define ICMPV6_UNREACH_FILTER_PROHIB 5 /* src failed ingress/egress policy */ +#define ICMPV6_UNREACH_REJECT_ROUTE 6 /* reject route */ +#define ICMPV6_TIMEXCEED 3 /* time exceeded, code: */ +#define ICMPV6_TIMEXCEED_INTRANS 0 /* hop limit exceeded in transit */ +#define ICMPV6_TIMEXCEED_REASS 1 /* fragmetn reassembly time exceeded */ +#define ICMPV6_ECHO 128 /* echo request */ +#define ICMPV6_ECHOREPLY 129 /* echo reply */ +/* + * Neighbor discovery types (RFC 4861) + */ +#define ICMPV6_NEIGHBOR_SOLICITATION 135 +#define ICMPV6_NEIGHBOR_ADVERTISEMENT 136 + +#define ICMPV6_INFOTYPE(type) (((type) & 0x80) != 0) + +/* + * Echo message data + */ +struct icmpv6_msg_echo { + uint16_t icmpv6_id; + uint16_t icmpv6_seq; + uint8_t icmpv6_data __flexarr; /* optional data */ +}; + +/* Neighbor solicitation or advertisement (single hardcoded option). + RFC 4861, sections 4.3 and 4.4. */ +struct icmpv6_msg_nd { + uint32_t icmpv6_flags; + ip6_addr_t icmpv6_target; + uint8_t icmpv6_option_type; + uint8_t icmpv6_option_length; + eth_addr_t icmpv6_mac; +}; + +/* + * ICMPv6 message union + */ +union icmpv6_msg { + struct icmpv6_msg_echo echo; /* ICMPV6_ECHO{REPLY} */ + struct icmpv6_msg_nd nd; /* ICMPV6_NEIGHBOR_{SOLICITATION,ADVERTISEMENT} */ +}; + +#ifndef __GNUC__ +# pragma pack() +#endif + +#define icmpv6_pack_hdr(hdr, type, code) do { \ + struct icmpv6_hdr *icmpv6_pack_p = (struct icmpv6_hdr *)(hdr); \ + icmpv6_pack_p->icmpv6_type = type; icmpv6_pack_p->icmpv6_code = code; \ +} while (0) + +#define icmpv6_pack_hdr_echo(hdr, type, code, id, seq, data, len) do { \ + struct icmpv6_msg_echo *echo_pack_p = (struct icmpv6_msg_echo *)\ + ((uint8_t *)(hdr) + ICMPV6_HDR_LEN); \ + icmpv6_pack_hdr(hdr, type, code); \ + echo_pack_p->icmpv6_id = htons(id); \ + echo_pack_p->icmpv6_seq = htons(seq); \ + memmove(echo_pack_p->icmpv6_data, data, len); \ +} while (0) + +#define icmpv6_pack_hdr_ns(hdr, targetip, srcmac) do { \ + struct icmpv6_msg_nd *nd_pack_p = (struct icmpv6_msg_nd *) \ + ((uint8_t *)(hdr) + ICMPV6_HDR_LEN); \ + icmpv6_pack_hdr(hdr, ICMPV6_NEIGHBOR_SOLICITATION, 0); \ + nd_pack_p->icmpv6_flags = 0; \ + memmove(&nd_pack_p->icmpv6_target, &(targetip), IP6_ADDR_LEN); \ + nd_pack_p->icmpv6_option_type = 1; \ + nd_pack_p->icmpv6_option_length = 1; \ + memmove(&nd_pack_p->icmpv6_mac, &(srcmac), ETH_ADDR_LEN); \ +} while (0) + +#endif /* DNET_ICMPV6_H */ diff --git a/libdnet-stripped/include/dnet_winconfig.h b/libdnet-stripped/include/dnet_winconfig.h index 6abd94371..5d96e7aca 100644 --- a/libdnet-stripped/include/dnet_winconfig.h +++ b/libdnet-stripped/include/dnet_winconfig.h @@ -138,7 +138,7 @@ /* #undef HAVE_ROUTE_RT_MSGHDR */ /* Define if has sockaddr_in6 struct. */ -// #define HAVE_SOCKADDR_IN6 1 +#define HAVE_SOCKADDR_IN6 1 /* Define if sockaddr struct has sa_len. */ /* #undef HAVE_SOCKADDR_SA_LEN */ @@ -250,6 +250,7 @@ #ifdef HAVE_WINSOCK2_H # include +# include # include #endif diff --git a/libdnet-stripped/src/intf-win32.c b/libdnet-stripped/src/intf-win32.c index 85b63a3c2..2c5e2f497 100644 --- a/libdnet-stripped/src/intf-win32.c +++ b/libdnet-stripped/src/intf-win32.c @@ -26,7 +26,10 @@ #include struct ifcombo { - DWORD *idx; + struct { + DWORD ipv4; + DWORD ipv6; + } *idx; int cnt; int max; }; @@ -37,8 +40,7 @@ struct ifcombo { struct intf_handle { struct ifcombo ifcombo[MIB_IF_TYPE_MAX]; - MIB_IFTABLE *iftable; - MIB_IPADDRTABLE *iptable; + IP_ADAPTER_ADDRESSES *iftable; }; static char * @@ -46,19 +48,22 @@ _ifcombo_name(int type) { char *name = "eth"; /* XXX */ - if (type == MIB_IF_TYPE_TOKENRING) { + if (type == IF_TYPE_ISO88025_TOKENRING) { name = "tr"; - } else if (type == MIB_IF_TYPE_FDDI) { - name = "fddi"; - } else if (type == MIB_IF_TYPE_PPP) { + } else if (type == IF_TYPE_PPP) { name = "ppp"; - } else if (type == MIB_IF_TYPE_LOOPBACK) { + } else if (type == IF_TYPE_SOFTWARE_LOOPBACK) { name = "lo"; - } else if (type == MIB_IF_TYPE_SLIP) { - name = "sl"; - } else if (type == MIB_IF_TYPE_TUNNEL) { + } else if (type == IF_TYPE_TUNNEL) { name = "tun"; } + /* + IF_TYPE_OTHER + IF_TYPE_ETHERNET_CSMACD + IF_TYPE_ATM + IF_TYPE_IEEE80211 + IF_TYPE_IEEE1394 + */ return (name); } @@ -71,14 +76,10 @@ _ifcombo_type(const char *device) type = INTF_TYPE_ETH; } else if (strncmp(device, "tr", 2) == 0) { type = INTF_TYPE_TOKENRING; - } else if (strncmp(device, "fd", 2) == 0) { - type = INTF_TYPE_FDDI; } else if (strncmp(device, "ppp", 3) == 0) { type = INTF_TYPE_PPP; } else if (strncmp(device, "lo", 2) == 0) { type = INTF_TYPE_LOOPBACK; - } else if (strncmp(device, "sl", 2) == 0) { - type = INTF_TYPE_SLIP; } else if (strncmp(device, "tun", 3) == 0) { type = INTF_TYPE_TUN; } @@ -86,7 +87,7 @@ _ifcombo_type(const char *device) } static void -_ifcombo_add(struct ifcombo *ifc, DWORD idx) +_ifcombo_add(struct ifcombo *ifc, DWORD ipv4_idx, DWORD ipv6_idx) { if (ifc->cnt == ifc->max) { if (ifc->idx) { @@ -98,17 +99,18 @@ _ifcombo_add(struct ifcombo *ifc, DWORD idx) ifc->idx = malloc(sizeof(ifc->idx[0]) * ifc->max); } } - ifc->idx[ifc->cnt++] = idx; + ifc->idx[ifc->cnt].ipv4 = ipv4_idx; + ifc->idx[ifc->cnt].ipv6 = ipv6_idx; + ifc->cnt++; } -/* Map an MIB_IFROW.dwType interface type into an internal interface - type. The internal types are never exposed to users of this library; - they exist only for the sake of ordering interface types within an - intf_handle, which has an array of ifcombo structures ordered by - type. Entries in an intf_handle must not be stored or accessed by a - raw MIB_IFROW.dwType number because they will not be able to be found - by a device name such as "net0" if the device name does not map - exactly to the dwType. */ +/* Map an MIB interface type into an internal interface type. The + internal types are never exposed to users of this library; they exist + only for the sake of ordering interface types within an intf_handle, + which has an array of ifcombo structures ordered by type. Entries in + an intf_handle must not be stored or accessed by a raw MIB type + number because they will not be able to be found by a device name + such as "net0" if the device name does not map exactly to the type. */ static int _if_type_canonicalize(int type) { @@ -116,11 +118,13 @@ _if_type_canonicalize(int type) } static void -_ifrow_to_entry(intf_t *intf, MIB_IFROW *ifrow, struct intf_entry *entry) +_adapter_address_to_entry(intf_t *intf, IP_ADAPTER_ADDRESSES *a, + struct intf_entry *entry) { struct addr *ap, *lap; int i; int type; + IP_ADAPTER_UNICAST_ADDRESS *addr; /* The total length of the entry may be passed inside entry. Remember it and clear the entry. */ @@ -128,60 +132,69 @@ _ifrow_to_entry(intf_t *intf, MIB_IFROW *ifrow, struct intf_entry *entry) memset(entry, 0, sizeof(*entry)); entry->intf_len = intf_len; - type = _if_type_canonicalize(ifrow->dwType); + type = _if_type_canonicalize(a->IfType); for (i = 0; i < intf->ifcombo[type].cnt; i++) { - if (intf->ifcombo[type].idx[i] == ifrow->dwIndex) + if (intf->ifcombo[type].idx[i].ipv4 == a->IfIndex && + intf->ifcombo[type].idx[i].ipv6 == a->Ipv6IfIndex) { break; + } } /* XXX - type matches MIB-II ifType. */ snprintf(entry->intf_name, sizeof(entry->intf_name), "%s%lu", - _ifcombo_name(type), i); + _ifcombo_name(a->IfType), i); entry->intf_type = (uint16_t)type; /* Get interface flags. */ entry->intf_flags = 0; - if (ifrow->dwAdminStatus == MIB_IF_ADMIN_STATUS_UP && - (ifrow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL || - ifrow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED)) + if (a->OperStatus == IfOperStatusUp) entry->intf_flags |= INTF_FLAG_UP; - if (ifrow->dwType == MIB_IF_TYPE_LOOPBACK) + if (a->IfType == IF_TYPE_SOFTWARE_LOOPBACK) entry->intf_flags |= INTF_FLAG_LOOPBACK; else entry->intf_flags |= INTF_FLAG_MULTICAST; /* Get interface MTU. */ - entry->intf_mtu = ifrow->dwMtu; + entry->intf_mtu = a->Mtu; /* Get hardware address. */ - if (ifrow->dwPhysAddrLen == ETH_ADDR_LEN) { + if (a->PhysicalAddressLength == ETH_ADDR_LEN) { entry->intf_link_addr.addr_type = ADDR_TYPE_ETH; entry->intf_link_addr.addr_bits = ETH_ADDR_BITS; - memcpy(&entry->intf_link_addr.addr_eth, ifrow->bPhysAddr, + memcpy(&entry->intf_link_addr.addr_eth, a->PhysicalAddress, ETH_ADDR_LEN); } /* Get addresses. */ ap = entry->intf_alias_addrs; lap = ap + ((entry->intf_len - sizeof(*entry)) / sizeof(entry->intf_alias_addrs[0])); - for (i = 0; i < (int)intf->iptable->dwNumEntries; i++) { - if (intf->iptable->table[i].dwIndex == ifrow->dwIndex && - intf->iptable->table[i].dwAddr != 0) { - if (entry->intf_addr.addr_type == ADDR_TYPE_NONE) { - /* Set primary address if unset. */ - entry->intf_addr.addr_type = ADDR_TYPE_IP; - entry->intf_addr.addr_ip = - intf->iptable->table[i].dwAddr; - addr_mtob(&intf->iptable->table[i].dwMask, - IP_ADDR_LEN, &entry->intf_addr.addr_bits); - } else if (ap < lap) { - /* Set aliases. */ - ap->addr_type = ADDR_TYPE_IP; - ap->addr_ip = intf->iptable->table[i].dwAddr; - addr_mtob(&intf->iptable->table[i].dwMask, - IP_ADDR_LEN, &ap->addr_bits); - ap++, entry->intf_alias_num++; + for (addr = a->FirstUnicastAddress; addr != NULL; addr = addr->Next) { + IP_ADAPTER_PREFIX *prefix; + unsigned short bits; + + /* Find the netmask length. This is stored in a parallel list. + We just take the first one with a matching address family, + but that may not be right. Windows Vista and later has an + OnLinkPrefixLength member that is stored right with the + unicast address. */ + bits = 0; + for (prefix = a->FirstPrefix; prefix != NULL; prefix = prefix->Next) { + if (prefix->Address.lpSockaddr->sa_family == addr->Address.lpSockaddr->sa_family) { + bits = (unsigned short) prefix->PrefixLength; + break; } } + + if (entry->intf_addr.addr_type == ADDR_TYPE_NONE) { + /* Set primary address if unset. */ + addr_ston(addr->Address.lpSockaddr, &entry->intf_addr); + entry->intf_addr.addr_bits = bits; + } else if (ap < lap) { + /* Set aliases. */ + addr_ston(addr->Address.lpSockaddr, ap); + ap->addr_bits = bits; + ap++; + entry->intf_alias_num++; + } } entry->intf_len = (u_int) ((u_char *)ap - (u_char *)entry); } @@ -189,58 +202,59 @@ _ifrow_to_entry(intf_t *intf, MIB_IFROW *ifrow, struct intf_entry *entry) static int _refresh_tables(intf_t *intf) { - MIB_IFROW *ifrow; + IP_ADAPTER_ADDRESSES *p; + DWORD ret; ULONG len; - u_int i, ret; - /* Get interface table. */ - for (len = sizeof(intf->iftable[0]); ; ) { - if (intf->iftable) - free(intf->iftable); - intf->iftable = malloc(len); - ret = GetIfTable(intf->iftable, &len, FALSE); - if (ret == NO_ERROR) - break; - else if (ret != ERROR_INSUFFICIENT_BUFFER) - return (-1); - } - /* Get IP address table. */ - for (len = sizeof(intf->iptable[0]); ; ) { - if (intf->iptable) - free(intf->iptable); - intf->iptable = malloc(len); - ret = GetIpAddrTable(intf->iptable, &len, FALSE); - if (ret == NO_ERROR) - break; - else if (ret != ERROR_INSUFFICIENT_BUFFER) + p = NULL; + len = 2; + do { + free(p); + p = malloc(len); + if (p == NULL) return (-1); + ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST, NULL, p, &len); + } while (ret == ERROR_BUFFER_OVERFLOW); + + if (ret != NO_ERROR) { + free(p); + return (-1); } + intf->iftable = p; + /* * Map "unfriendly" win32 interface indices to ours. * XXX - like IP_ADAPTER_INFO ComboIndex */ - for (i = 0; i < intf->iftable->dwNumEntries; i++) { + for (p = intf->iftable; p != NULL; p = p->Next) { int type; - ifrow = &intf->iftable->table[i]; - type = _if_type_canonicalize(ifrow->dwType); - if (type < MIB_IF_TYPE_MAX) { - _ifcombo_add(&intf->ifcombo[type], ifrow->dwIndex); - } else + type = _if_type_canonicalize(p->IfType); + if (type < MIB_IF_TYPE_MAX) + _ifcombo_add(&intf->ifcombo[type], p->IfIndex, p->Ipv6IfIndex); + else return (-1); } return (0); } -static int -_find_ifindex(intf_t *intf, const char *device) +static IP_ADAPTER_ADDRESSES * +_find_adapter_address(intf_t *intf, const char *device) { + IP_ADAPTER_ADDRESSES *a; char *p = (char *)device; int n, type = _ifcombo_type(device); while (isalpha((int) (unsigned char) *p)) p++; n = atoi(p); - return (intf->ifcombo[type].idx[n]); + for (a = intf->iftable; a != NULL; a = a->Next) { + if (intf->ifcombo[type].idx[n].ipv4 == a->IfIndex && + intf->ifcombo[type].idx[n].ipv6 == a->Ipv6IfIndex) { + return a; + } + } + + return NULL; } intf_t * @@ -252,17 +266,16 @@ intf_open(void) int intf_get(intf_t *intf, struct intf_entry *entry) { - MIB_IFROW ifrow; + IP_ADAPTER_ADDRESSES *a; if (_refresh_tables(intf) < 0) return (-1); - ifrow.dwIndex = _find_ifindex(intf, entry->intf_name); - - if (GetIfEntry(&ifrow) != NO_ERROR) + a = _find_adapter_address(intf, entry->intf_name); + if (a == NULL) return (-1); - _ifrow_to_entry(intf, &ifrow, entry); + _adapter_address_to_entry(intf, a, entry); return (0); } @@ -270,9 +283,8 @@ intf_get(intf_t *intf, struct intf_entry *entry) int intf_get_src(intf_t *intf, struct intf_entry *entry, struct addr *src) { - MIB_IFROW ifrow; - MIB_IPADDRROW *iprow; - int i; + IP_ADAPTER_ADDRESSES *a; + IP_ADAPTER_UNICAST_ADDRESS *addr; if (src->addr_type != ADDR_TYPE_IP) { errno = EINVAL; @@ -281,14 +293,15 @@ intf_get_src(intf_t *intf, struct intf_entry *entry, struct addr *src) if (_refresh_tables(intf) < 0) return (-1); - for (i = 0; i < (int)intf->iptable->dwNumEntries; i++) { - iprow = &intf->iptable->table[i]; - if (iprow->dwAddr == src->addr_ip) { - ifrow.dwIndex = iprow->dwIndex; - if (GetIfEntry(&ifrow) != NO_ERROR) - return (-1); - _ifrow_to_entry(intf, &ifrow, entry); - return (0); + for (a = intf->iftable; a != NULL; a = a->Next) { + for (addr = a->FirstUnicastAddress; addr != NULL; addr = addr->Next) { + struct addr dnet_addr; + + addr_ston(addr->Address.lpSockaddr, &dnet_addr); + if (addr_cmp(&dnet_addr, src) == 0) { + _adapter_address_to_entry(intf, a, entry); + return (0); + } } } errno = ENXIO; @@ -298,24 +311,9 @@ intf_get_src(intf_t *intf, struct intf_entry *entry, struct addr *src) int intf_get_dst(intf_t *intf, struct intf_entry *entry, struct addr *dst) { - MIB_IFROW ifrow; - - if (dst->addr_type != ADDR_TYPE_IP) { - errno = EINVAL; - return (-1); - } - if (GetBestInterface(dst->addr_ip, &ifrow.dwIndex) != NO_ERROR) - return (-1); - - if (GetIfEntry(&ifrow) != NO_ERROR) - return (-1); - - if (_refresh_tables(intf) < 0) - return (-1); - - _ifrow_to_entry(intf, &ifrow, entry); - - return (0); + errno = ENOSYS; + SetLastError(ERROR_NOT_SUPPORTED); + return (-1); } int @@ -326,23 +324,6 @@ intf_set(intf_t *intf, const struct intf_entry *entry) * but what about the rest of the configuration? :-( * {Add,Delete}IPAddress for 2000/XP only */ -#if 0 - /* Set interface address. XXX - 2000/XP only? */ - if (entry->intf_addr.addr_type == ADDR_TYPE_IP) { - ULONG ctx = 0, inst = 0; - UINT ip, mask; - - memcpy(&ip, &entry->intf_addr.addr_ip, IP_ADDR_LEN); - addr_btom(entry->intf_addr.addr_bits, &mask, IP_ADDR_LEN); - - if (AddIPAddress(ip, mask, - _find_ifindex(intf, entry->intf_name), - &ctx, &inst) != NO_ERROR) { - return (-1); - } - return (0); - } -#endif errno = ENOSYS; SetLastError(ERROR_NOT_SUPPORTED); return (-1); @@ -351,18 +332,19 @@ intf_set(intf_t *intf, const struct intf_entry *entry) int intf_loop(intf_t *intf, intf_handler callback, void *arg) { + IP_ADAPTER_ADDRESSES *a; struct intf_entry *entry; u_char ebuf[1024]; - int i, ret = 0; + int ret; if (_refresh_tables(intf) < 0) return (-1); entry = (struct intf_entry *)ebuf; - for (i = 0; i < (int)intf->iftable->dwNumEntries; i++) { + for (a = intf->iftable; a != NULL; a = a->Next) { entry->intf_len = sizeof(ebuf); - _ifrow_to_entry(intf, &intf->iftable->table[i], entry); + _adapter_address_to_entry(intf, a, entry); if ((ret = (*callback)(entry, arg)) != 0) break; } @@ -381,8 +363,6 @@ intf_close(intf_t *intf) } if (intf->iftable) free(intf->iftable); - if (intf->iptable) - free(intf->iptable); free(intf); } return (NULL); @@ -394,11 +374,10 @@ intf_close(intf_t *intf) int intf_get_pcap_devname(const char *intf_name, char *pcapdev, int pcapdevlen) { - wchar_t descr_wc[512]; + IP_ADAPTER_ADDRESSES *a; pcap_if_t *pcapdevs; pcap_if_t *pdev, *selected; intf_t *intf; - MIB_IFROW ifrow; if ((intf = intf_open()) == NULL) return (-1); @@ -406,77 +385,36 @@ intf_get_pcap_devname(const char *intf_name, char *pcapdev, int pcapdevlen) intf_close(intf); return (-1); } - ifrow.dwIndex = _find_ifindex(intf, intf_name); - intf_close(intf); + a = _find_adapter_address(intf, intf_name); - if (GetIfEntry(&ifrow) != NO_ERROR) + if (a == NULL) { + intf_close(intf); return (-1); - - /* OID_GEN_FRIENDLY_NAME returns a wide-character string, so convert - the description to wide characters for string comparison. */ - mbstowcs(descr_wc, ifrow.bDescr, sizeof(descr_wc) / sizeof(descr_wc[0]) - 1); - descr_wc[sizeof(descr_wc) / sizeof(descr_wc[0]) - 1] = L'\0'; - - if (pcap_findalldevs(&pcapdevs, NULL) == -1) - return (-1); - - /* Loop through all the pcap devices until we find a match. pcap gets - its interface list from the registry; dnet gets it from GetIfList. - We must match them up using values common to both data sets. We do - it by comparing hardware addresses and interface descriptions. */ - selected = NULL; - for (pdev = pcapdevs; pdev != NULL; pdev = pdev->next) { - PACKET_OID_DATA *data; - u_char buf[512]; - LPADAPTER lpa; - - lpa = PacketOpenAdapter(pdev->name); - if (lpa == NULL) - continue; - if (lpa->hFile == INVALID_HANDLE_VALUE) - goto close_adapter; - - data = (PACKET_OID_DATA *) buf; - - /* Check the MAC address if available. */ - data->Oid = OID_802_3_CURRENT_ADDRESS; - data->Length = sizeof(buf) - sizeof(*data); - if (!PacketRequest(lpa, FALSE, data)) - goto close_adapter; - if (data->Length != ifrow.dwPhysAddrLen) - goto close_adapter; - if (memcmp(ifrow.bPhysAddr, data->Data, data->Length) != 0) - goto close_adapter; - - /* A hardware address match is good enough, but we will prefer - an additional match with the description if available. */ - if (selected == NULL) - selected = pdev; - - /* Distinct interfaces can have the same MAC address in the - case of "teamed" interfaces. Additionally check the - description string. */ - data->Oid = OID_GEN_FRIENDLY_NAME; - data->Length = sizeof(buf) - sizeof(*data); - if (PacketRequest(lpa, FALSE, data) != TRUE) - goto close_adapter; - if (wcscmp(descr_wc, (wchar_t *) data->Data) != 0) - goto close_adapter; - - /* This matches both the hardware address and description, so - it's the best candidate. */ - selected = pdev; - PacketCloseAdapter(lpa); - break; - -close_adapter: - PacketCloseAdapter(lpa); } - if (selected != NULL) - strlcpy(pcapdev, selected->name, pcapdevlen); + if (pcap_findalldevs(&pcapdevs, NULL) == -1) { + intf_close(intf); + return (-1); + } + + /* Loop through all the pcap devices until we find a match. */ + selected = NULL; + for (pdev = pcapdevs; pdev != NULL; pdev = pdev->next) { + char *name; + + if (pdev->name == NULL) + continue; + name = strchr(pdev->name, '{'); + if (name == NULL) + continue; + if (strcmp(name, a->AdapterName) == 0) + break; + } + if (pdev != NULL) + strlcpy(pcapdev, pdev->name, pcapdevlen); + intf_close(intf); pcap_freealldevs(pcapdevs); - if (selected == NULL) + if (pdev == NULL) return -1; else return 0; diff --git a/libdnet-stripped/src/route-bsd.c b/libdnet-stripped/src/route-bsd.c index 233cf4d70..c6532c941 100644 --- a/libdnet-stripped/src/route-bsd.c +++ b/libdnet-stripped/src/route-bsd.c @@ -44,7 +44,7 @@ #include "dnet.h" #define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + ((a) > 0 ? (1 + (((a) - 1) | (RT_MSGHDR_ALIGNMENT - 1))) : RT_MSGHDR_ALIGNMENT) #ifdef HAVE_SOCKADDR_SA_LEN #define NEXTSA(s) \ diff --git a/libdnet-stripped/src/route-linux.c b/libdnet-stripped/src/route-linux.c index b80f9a83b..5f150870a 100644 --- a/libdnet-stripped/src/route-linux.c +++ b/libdnet-stripped/src/route-linux.c @@ -228,9 +228,6 @@ route_loop(route_t *r, route_handler callback, void *arg) if (i < 10 || !(iflags & RTF_UP)) continue; - if (entry.route_gw.addr_ip == IP_ADDR_ANY) - continue; - entry.route_dst.addr_type = entry.route_gw.addr_type = ADDR_TYPE_IP; diff --git a/libdnet-stripped/src/route-win32.c b/libdnet-stripped/src/route-win32.c index e66076655..7b75d9154 100644 --- a/libdnet-stripped/src/route-win32.c +++ b/libdnet-stripped/src/route-win32.c @@ -21,14 +21,23 @@ #include "dnet.h" +typedef DWORD (WINAPI *GETIPFORWARDTABLE2)(ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2 *); + struct route_handle { + HINSTANCE iphlpapi; MIB_IPFORWARDTABLE *ipftable; + MIB_IPFORWARD_TABLE2 *ipftable2; }; route_t * route_open(void) { - return (calloc(1, sizeof(route_t))); + route_t *r; + + r = calloc(1, sizeof(route_t)); + r->iphlpapi = GetModuleHandle("iphlpapi.dll"); + + return r; } int @@ -113,13 +122,13 @@ route_get(route_t *route, struct route_entry *entry) return (0); } -int -route_loop(route_t *r, route_handler callback, void *arg) +static int +route_loop_getipforwardtable(route_t *r, route_handler callback, void *arg) { - struct route_entry entry; + struct route_entry entry; ULONG len; int i, ret; - + for (len = sizeof(r->ipftable[0]); ; ) { if (r->ipftable) free(r->ipftable); @@ -149,12 +158,59 @@ route_loop(route_t *r, route_handler callback, void *arg) return (0); } +static int +route_loop_getipforwardtable2(GETIPFORWARDTABLE2 GetIpForwardTable2, + route_t *r, route_handler callback, void *arg) +{ + struct route_entry entry; + ULONG i; + int ret; + + ret = GetIpForwardTable2(AF_UNSPEC, &r->ipftable2); + if (ret != NO_ERROR) + return (-1); + + for (i = 0; i < r->ipftable2->NumEntries; i++) { + MIB_IPFORWARD_ROW2 *row; + char buf[100]; + + row = &r->ipftable2->Table[i]; + addr_ston((struct sockaddr *) &row->DestinationPrefix.Prefix, &entry.route_dst); + entry.route_dst.addr_bits = row->DestinationPrefix.PrefixLength; + addr_ston((struct sockaddr *) &row->NextHop, &entry.route_gw); + + if ((ret = (*callback)(&entry, arg)) != 0) + return (ret); + } + return (0); +} + +int +route_loop(route_t *r, route_handler callback, void *arg) +{ + GETIPFORWARDTABLE2 GetIpForwardTable2; + + /* GetIpForwardTable2 is only available on Vista and later, dynamic load. */ + GetIpForwardTable2 = NULL; + if (r->iphlpapi != NULL) + GetIpForwardTable2 = (GETIPFORWARDTABLE2) GetProcAddress(r->iphlpapi, "GetIpForwardTable2"); + + if (GetIpForwardTable2 == NULL) + return route_loop_getipforwardtable(r, callback, arg); + else + return route_loop_getipforwardtable2(GetIpForwardTable2, r, callback, arg); +} + route_t * route_close(route_t *r) { if (r != NULL) { + if (r->iphlpapi != NULL) + FreeLibrary(r->iphlpapi); if (r->ipftable != NULL) free(r->ipftable); + if (r->ipftable2 != NULL) + FreeMibTable(r->ipftable2); free(r); } return (NULL); diff --git a/libnetutil/netutil.cc b/libnetutil/netutil.cc index 3ffc34906..c9b7c5b4f 100644 --- a/libnetutil/netutil.cc +++ b/libnetutil/netutil.cc @@ -114,6 +114,9 @@ #if HAVE_NETINET_IN_H #include #endif +#ifdef HAVE_LINUX_RTNETLINK_H +#include +#endif #ifndef NETINET_IN_SYSTM_H /* This guarding is needed for at least some versions of OpenBSD */ #include @@ -470,9 +473,8 @@ int ip_is_reserved(struct in_addr *ip) #define ARPCACHE_GET 1 #define ARPCACHE_SET 2 static int do_arp_cache(int command, struct sockaddr_storage *ss, u8 *mac) { - struct sockaddr_in *sin = (struct sockaddr_in *) ss; struct ArpCache { - u32 ip; /* Network byte order */ + struct sockaddr_storage ip; /* Network byte order */ u8 mac[6]; }; static struct ArpCache *Cache = NULL; @@ -480,12 +482,12 @@ static int do_arp_cache(int command, struct sockaddr_storage *ss, u8 *mac) { static int ArpCacheSz = 0; int i; - if (sin->sin_family != AF_INET) - return 0; + //if (sin->sin_family != AF_INET) + // return 0; if (command == ARPCACHE_GET) { for (i = 0; i < ArpCacheSz; i++) { - if (Cache[i].ip == sin->sin_addr.s_addr) { + if (sockaddr_storage_cmp(&Cache[i].ip, ss) == 0) { memcpy(mac, Cache[i].mac, 6); return 1; } @@ -503,14 +505,14 @@ static int do_arp_cache(int command, struct sockaddr_storage *ss, u8 *mac) { /* Ensure that it isn't already there ... */ for (i = 0; i < ArpCacheSz; i++) { - if (Cache[i].ip == sin->sin_addr.s_addr) { + 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 */ - Cache[i].ip = sin->sin_addr.s_addr; + memcpy(&Cache[i].ip, ss, sizeof(struct sockaddr_storage)); memcpy(Cache[i].mac, mac, 6); ArpCacheSz++; return 1; @@ -546,6 +548,173 @@ unsigned short in_cksum(u16 *ptr,int nbytes) { } +const void *ip_get_data(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr) { + 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(ip6, len, &hdr->proto); + } + + return NULL; +} + +/* 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; +} + +/* 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_TCP: + case IP_PROTO_UDP: + case IP_PROTO_ICMP: + case IP_PROTO_ICMPV6: + case IP_PROTO_SCTP: + return 1; + default: + return 0; + } +} + +/* 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) +{ + const unsigned char *p, *end; + unsigned int hdr_len; + + if (*len < sizeof(*ip6)) + return NULL; + + p = (unsigned char *) ip6; + end = p + *len; + + *nxt = ip6->ip6_nxt; + hdr_len = sizeof(*ip6); + p += sizeof(*ip6); + while (ipv6_is_extension_header(*nxt)) { + if (p + 2 > end) + return NULL; + *nxt = *p; + p += (*(p + 1) + 1) * 8; + if (p > end) + return NULL; + } + + *len = end - p; + if (!ipv6_is_upperlayer(*nxt)) + return NULL; + + return (char *) p; +} + +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. */ @@ -575,6 +744,38 @@ unsigned short ipv4_pseudoheader_cksum(const struct in_addr *src, return ip_cksum_carry(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); + /* 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 ip_cksum_carry(sum); +} + void sethdrinclude(int sd) { #ifdef IP_HDRINCL int one = 1; @@ -911,29 +1112,32 @@ void tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize) { static int nmaskcmp(const void *a, const void *b) { struct sys_route *r1 = (struct sys_route *) a; struct sys_route *r2 = (struct sys_route *) b; - if (r1->netmask == r2->netmask) { - /* 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; - } - if (ntohl(r1->netmask) > ntohl(r2->netmask)) + if (r1->dest.ss_family < r2->dest.ss_family) return -1; - else + 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; + + /* 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; } -#if WIN32 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; - int num_aliases_done; + unsigned int num_aliases_done; primary_done = false; num_aliases_done = 0; @@ -948,16 +1152,14 @@ static int collect_dnet_interfaces(const struct intf_entry *entry, void *arg) { /* 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 (entry->intf_addr.addr_type == ADDR_TYPE_IP) { - addr_ntos(&entry->intf_addr, (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr); - dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_addr.addr_bits; - } + if (addr_ntos(&entry->intf_addr, (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr) == -1) + dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_addr.addr_bits; primary_done = true; - } else if (num_aliases_done < (int) entry->intf_alias_num) { - if (entry->intf_alias_addrs[num_aliases_done].addr_type == ADDR_TYPE_IP) { - addr_ntos(&entry->intf_alias_addrs[num_aliases_done], (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr); - dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_alias_addrs[num_aliases_done].addr_bits; - } + } else if (num_aliases_done < entry->intf_alias_num) { + if (addr_ntos(&entry->intf_alias_addrs[num_aliases_done], (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr) == -1) + dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_alias_addrs[num_aliases_done].addr_bits; num_aliases_done++; } @@ -1024,202 +1226,10 @@ static struct interface_info *getinterfaces_dnet(int *howmany, char *errstr, siz return dcrn.ifaces; } -#else /* !WIN32 */ - -/* Get a list of interfaces using ioctl(SIOCGIFCONF). */ -static struct interface_info *getinterfaces_siocgifconf(int *howmany, char *errstr, size_t errstrlen) { - struct interface_info *devs; - int count = 0; - int capacity = 0; - struct ifconf ifc; - struct ifreq *ifr; - int sd; - int len; - assert(howmany); - capacity = 16; - devs = (struct interface_info *) safe_zalloc(sizeof(struct interface_info) * capacity); - - /* Dummy socket for ioctl */ - sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0){ - if(errstr) Snprintf(errstr, errstrlen, "socket in %s", __func__); - *howmany=-1; - return NULL; - } - - ifc.ifc_len = 20480; - ifc.ifc_buf = (char *) safe_zalloc(ifc.ifc_len); - /* Returns an array of struct ifreq in ifc.ifc_req, which is a union with - ifc.ifc_buf. */ - if (ioctl(sd, SIOCGIFCONF, &ifc) < 0){ - if(errstr) Snprintf(errstr, errstrlen, "Failed to determine your configured interfaces!\n"); - *howmany=-1; - return NULL; - } - if (ifc.ifc_len == 0){ - if(errstr) Snprintf(errstr, errstrlen, "%s: SIOCGIFCONF claims you have no network interfaces!\n", __func__); - *howmany=-1; - return NULL; - } - ifr = ifc.ifc_req; - - for (ifr = ifc.ifc_req; - ifr && ifr->ifr_name[0] && (void *) ifr < (void *)((char *) ifc.ifc_buf + ifc.ifc_len); - ifr = (struct ifreq *) ((char *) ifr + len)) { - struct sockaddr_in *sin; - struct ifreq tmpifr; - u16 ifflags; - int rc; - char *p; - - len = sizeof(struct ifreq); -#if HAVE_SOCKADDR_SA_LEN - /* Some platforms (such as FreeBSD) have an sa_len member that may make the - ifr longer than sizeof(struct ifreq). */ - if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru)) - len += ifr->ifr_addr.sa_len - sizeof(ifr->ifr_ifru); -#endif - - /* skip any device with no name */ - if (ifr->ifr_name[0] == '\0') - continue; - - /* We currently only handle IPv4 */ - sin = (struct sockaddr_in *) &ifr->ifr_addr; - if (sin->sin_family != AF_INET) - continue; - - /* Make room for this new interface if necessary. */ - if (count >= capacity) { - capacity <<= 2; - devs = (struct interface_info *) safe_realloc(devs, sizeof(struct interface_info) * capacity); - } - - /* We know the address, put it in the array. */ - memcpy(&(devs[count].addr), sin, MIN(sizeof(devs[count].addr), sizeof(*sin))); - Strncpy(devs[count].devname, ifr->ifr_name, sizeof(devs[count].devname)); - Strncpy(devs[count].devfullname, ifr->ifr_name, sizeof(devs[count].devfullname)); - /* devname isn't allowed to have alias qualification */ - p = strchr(devs[count].devname, ':'); - if (p != NULL) - *p = '\0'; - - /* Use tmpifr for further ioctl requests. We're going to make a bunch of - ioctl calls to learn about the interface and set fields in devs[count]. - - The Linux netdevice(7) man page says that you only have to set ifr_name - before making the ioctl, but perhaps other platforms need ifr_addr to be - set too. ifr_name will persist but ifr_addr is in a union with the ioctl - return value, so it has to be reset before every call. The general - pattern is memcpy, then ioctl. */ - Strncpy(tmpifr.ifr_name, ifr->ifr_name, sizeof(tmpifr.ifr_name)); - - /* Look up the netmask. Note setting of ifr_addr. */ - memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); - rc = ioctl(sd, SIOCGIFNETMASK, &tmpifr); - if (rc < 0 && errno != EADDRNOTAVAIL){ - if(errstr) Snprintf(errstr, errstrlen, "Failed to determine the netmask of %s!", tmpifr.ifr_name); - *howmany=-1; - return NULL; - } - else if (rc < 0) - devs[count].netmask_bits = 32; - else { - /* We would use ifr_netmask, but that's only on Linux, so use ifr_addr - which shares the same memory space in a union. */ - addr_stob(&(tmpifr.ifr_addr), &devs[count].netmask_bits); - } - - /* Now we need to determine the device type ... this technique is kinda iffy - ... may not be portable. */ - /* Get the flags. */ - memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); - rc = ioctl(sd, SIOCGIFFLAGS, &tmpifr); - if (rc < 0){ - if(errstr) Snprintf(errstr, errstrlen, "Failed to get IF Flags for device %s", ifr->ifr_name); - *howmany=-1; - return NULL; - } - ifflags = tmpifr.ifr_flags; - - if (ifflags & IFF_LOOPBACK) { - devs[count].device_type = devt_loopback; - } else if (ifflags & IFF_BROADCAST) { - devs[count].device_type = devt_ethernet; - - /* If the device type is ethernet, get the MAC address. */ -#ifdef SIOCGIFHWADDR - memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); - rc = ioctl(sd, SIOCGIFHWADDR, &tmpifr); - if (rc < 0 && errno != EADDRNOTAVAIL){ - if(errstr) Snprintf(errstr, errstrlen, "Failed to determine the MAC address of %s!", tmpifr.ifr_name); - *howmany=-1; - return NULL; - } - else if (rc >= 0) - memcpy(devs[count].mac, &tmpifr.ifr_addr.sa_data, 6); -#else - /* Let's just let libdnet handle it ... */ - eth_t *ethsd = eth_open_cached(devs[count].devname); - eth_addr_t ethaddr; - - if (!ethsd) { - netutil_error("Warning: Unable to open interface %s -- skipping it.", devs[count].devname); - continue; - } - if (eth_get(ethsd, ðaddr) != 0) { - netutil_error("Warning: Unable to get hardware address for interface %s -- skipping it.", devs[count].devname); - continue; - } - memcpy(devs[count].mac, ethaddr.data, 6); -#endif /*SIOCGIFHWADDR*/ - } else if (ifflags & IFF_POINTOPOINT) { - devs[count].device_type = devt_p2p; - } else { - devs[count].device_type = devt_other; - } - - if (ifflags & IFF_UP) - devs[count].device_up = 1; - else - devs[count].device_up = 0; - -#ifdef SIOCGIFMTU - memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); - rc = ioctl(sd, SIOCGIFMTU, &tmpifr); - if (rc < 0) { - if(errstr) Snprintf(errstr, errstrlen, "Failed to determine the mtu of %s!", tmpifr.ifr_name); - *howmany=-1; - return NULL; - } else { -#ifdef ifr_mtu - devs[count].mtu = tmpifr.ifr_mtu; -#else - /* Some systems lack ifr_mtu and a common solution (see pcap, dnet and - * others) is using ifr_metric instead - */ - devs[count].mtu = tmpifr.ifr_metric; -#endif - } -#else - devs[count].mtu = 0; -#endif - - /* All done with this interface. Increase the count. */ - count++; - } - free(ifc.ifc_buf); - close(sd); - - *howmany = count; - return devs; -} -#endif - /* 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() or getinterfaces_siocgifconf(). + 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) { @@ -1228,12 +1238,7 @@ struct interface_info *getinterfaces(int *howmany, char *errstr, size_t errstrle static int numifaces = 0; if (!initialized) { -#if WIN32 - /* On Win32 we just use Dnet to determine the interface list */ mydevs = getinterfaces_dnet(&numifaces, errstr, errstrlen); -#else - mydevs = getinterfaces_siocgifconf(&numifaces, errstr, errstrlen); -#endif initialized = 1; } @@ -1246,9 +1251,8 @@ struct interface_info *getinterfaces(int *howmany, char *errstr, size_t errstrle /* The 'dev' passed in must be at least 32 bytes long. Returns 0 on success. */ -int ipaddr2devname(char *dev, const struct in_addr *addr) { +int ipaddr2devname(char *dev, const struct sockaddr_storage *addr) { struct interface_info *ifaces; - struct sockaddr_in *sin; int numifaces; int i; @@ -1258,10 +1262,7 @@ int ipaddr2devname(char *dev, const struct in_addr *addr) { return -1; for (i = 0; i < numifaces; i++) { - sin = (struct sockaddr_in *) &ifaces[i].addr; - if (sin->sin_family != AF_INET) - continue; - if (addr->s_addr == sin->sin_addr.s_addr) { + if (sockaddr_storage_cmp(&ifaces[i].addr, addr) == 0) { Strncpy(dev, ifaces[i].devname, 32); return 0; } @@ -1270,9 +1271,8 @@ int ipaddr2devname(char *dev, const struct in_addr *addr) { return -1; } -int devname2ipaddr(char *dev, struct in_addr *addr) { +int devname2ipaddr(char *dev, struct sockaddr_storage *addr) { struct interface_info *mydevs; - struct sockaddr_in *s; int numdevs; int i; mydevs = getinterfaces(&numdevs, NULL, 0); @@ -1281,11 +1281,8 @@ int devname2ipaddr(char *dev, struct in_addr *addr) { return -1; for (i = 0; i < numdevs; i++) { - s = (struct sockaddr_in *) &mydevs[i].addr; - if (s->sin_family != AF_INET) /* Currently we only support IPv4 */ - continue; if (!strcmp(dev, mydevs[i].devfullname)) { - memcpy(addr, (char *) &s->sin_addr, sizeof(struct in_addr)); + *addr = mydevs[i].addr; return 0; } } @@ -1312,148 +1309,77 @@ struct interface_info *getInterfaceByName(const char *iname) { } +int sockaddr_equal(const struct sockaddr_storage *a, + const struct sockaddr_storage *b) { -/* Read system routes from a handle to a /proc/net/route file. */ -static struct sys_route *getsysroutes_proc(FILE *routefp, int *howmany, char *errstr, size_t errstrlen) { - struct sys_route *routes = NULL; - int route_capacity = 128; - struct interface_info *ifaces; - char buf[1024]; - char iface[16]; - char *p, *endptr; - struct interface_info *ii; - u32 routeaddr, mask; - struct sockaddr_in *sin; - int numifaces = 0, numroutes = 0; - int i; - assert(howmany); - - /* Obtain list of system network interfaces */ - if( (ifaces=getinterfaces(&numifaces, errstr, errstrlen)) == NULL ){ - *howmany=-1; - return NULL; - } - routes = (struct sys_route *) safe_zalloc(route_capacity * sizeof(struct sys_route)); + if (a->ss_family == AF_INET && b->ss_family == AF_INET) { + struct sockaddr_in *sa, *sb; - /* Kill the first line (column headers) */ - errno = 0; - if (fgets(buf, sizeof(buf), routefp) == NULL) { - if (errno){ - if(errstr) Snprintf(errstr, errstrlen, "Read error in /proc/net/route"); - }else{ - if(errstr) Snprintf(errstr, errstrlen, "Premature EOF in /proc/net/route"); - } - *howmany=-1; - return NULL; - } - - while (fgets(buf, sizeof(buf), routefp)) { - p = strtok(buf, " \t\n"); - if (!p) { - netutil_error("Could not find interface in /proc/net/route line"); - continue; - } - if (*p == '*') - continue; /* Deleted route -- any other valid reason for a route to start with an asterict? */ - Strncpy(iface, p, sizeof(iface)); - p = strtok(NULL, " \t\n"); - if (!p) { - netutil_error("Could not find destination in /proc/net/route line"); - continue; - } - endptr = NULL; - routes[numroutes].dest = strtoul(p, &endptr, 16); - if (!endptr || *endptr) { - netutil_error("Failed to determine Destination from /proc/net/route"); - continue; - } + sa = (struct sockaddr_in *) a; + sb = (struct sockaddr_in *) b; - /* Now for the gateway */ - p = strtok(NULL, " \t\n"); - if (!p) { - netutil_error("Could not find gateway in /proc/net/route line"); - continue; - } - endptr = NULL; - routes[numroutes].gw.s_addr = strtoul(p, &endptr, 16); - if (!endptr || *endptr) { - netutil_error("Failed to determine gw for %s from /proc/net/route", iface); - } - for (i = 0; i < 5; i++) { - p = strtok(NULL, " \t\n"); - if (!p) - break; - } - if (!p) { - netutil_error("Failed to find field %d in /proc/net/route", i + 2); - continue; - } - endptr = NULL; - routes[numroutes].netmask = strtoul(p, &endptr, 16); - if (!endptr || *endptr) { - netutil_error("Failed to determine mask from /proc/net/route"); - continue; - } - for (i = 0; i < numifaces; i++) { - if (!strcmp(iface, ifaces[i].devfullname)) { - routes[numroutes].device = &ifaces[i]; - break; - } - } - /* If device name in the route file does not match the full name (including - alias extension) of any interface, then try to find at least an alias of - the proper interface. */ - if (i == numifaces) { - for (i = 0; i < numifaces; i++) { - if (!strcmp(iface, ifaces[i].devname)) { - routes[numroutes].device = &ifaces[i]; - break; - } - } - } - if (i == numifaces) { - netutil_error("Failed to find device %s which was referenced in /proc/net/route", iface); - continue; - } + 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; - /* Now to deal with some alias nonsense ... at least on Linux - this file will just list the short name, even though IP - information (such as source address) from an alias must be - used. So if the purported device can't reach the gateway - (or destination address for directly connected routes), - try to find a device that starts with the same short - devname, but can (e.g. eth0 -> eth0:3) */ - if (routes[numroutes].gw.s_addr != 0) - routeaddr = routes[numroutes].gw.s_addr; - else - routeaddr = routes[numroutes].dest; - ii = &ifaces[i]; - mask = htonl((unsigned long) (0 - 1) << (32 - ii->netmask_bits)); - sin = (struct sockaddr_in *) &ii->addr; - if ((sin->sin_addr.s_addr & mask) != (routeaddr & mask)) { - for (i = 0; i < numifaces; i++) { - if (ii == &ifaces[i]) - continue; - if (strcmp(ii->devname, ifaces[i].devname) == 0) { - sin = (struct sockaddr_in *) &ifaces[i].addr; - if ((sin->sin_addr.s_addr & mask) == (routeaddr & mask)) { - routes[numroutes].device = &ifaces[i]; - } - } - } - } + sa = (struct sockaddr_in6 *) a; + sb = (struct sockaddr_in6 *) b; - numroutes++; - if (numroutes >= route_capacity) { - route_capacity <<= 2; - routes = (struct sys_route *) safe_realloc(routes, route_capacity * sizeof(struct sys_route)); - } + return memcmp(sa->sin6_addr.s6_addr, sb->sin6_addr.s6_addr, sizeof(sa->sin6_addr.s6_addr)) == 0; } - *howmany = numroutes; - return routes; + 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 @@ -1461,8 +1387,6 @@ static struct sys_route *getsysroutes_proc(FILE *routefp, int *howmany, char *er static struct dnet_collector_route_nfo *sysroutes_dnet_find_interfaces(struct dnet_collector_route_nfo *dcrn) { struct interface_info *ifaces; - u32 mask; - struct sockaddr_in *sin; int numifaces = 0; int i, j; int changed=0; @@ -1473,11 +1397,7 @@ static struct dnet_collector_route_nfo *sysroutes_dnet_find_interfaces(struct dn /* First we match up routes whose gateway address directly matches the address of an interface. */ for (j = 0; j < numifaces; j++) { - sin = (struct sockaddr_in *) &ifaces[j].addr; - mask = - htonl((unsigned long) (0 - 1) << (32 - ifaces[j].netmask_bits)); - if ((sin->sin_addr.s_addr & mask) == - (dcrn->routes[i].gw.s_addr & mask)) { + if (sockaddr_equal_netmask(&ifaces[j].addr, &dcrn->routes[i].gw, ifaces[j].netmask_bits)) { dcrn->routes[i].device = &ifaces[j]; break; } @@ -1498,7 +1418,7 @@ static struct dnet_collector_route_nfo *sysroutes_dnet_find_interfaces(struct dn /* Does this route's gateway go through another route with an assigned interface? */ for (j = 0; j < dcrn->numroutes; j++) { - if (dcrn->routes[i].gw.s_addr == dcrn->routes[j].dest + 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; @@ -1513,13 +1433,11 @@ static struct dnet_collector_route_nfo *sysroutes_dnet_find_interfaces(struct dn if (dcrn->routes[i].device == NULL) { char destbuf[INET6_ADDRSTRLEN]; char gwbuf[INET6_ADDRSTRLEN]; - struct in_addr ia; - ia.s_addr = dcrn->routes[i].dest; - strncpy(destbuf, inet_ntoa(ia), sizeof(destbuf)); - strncpy(gwbuf, inet_ntoa(dcrn->routes[i].gw), sizeof(gwbuf)); + 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, gwbuf); + 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--; @@ -1536,9 +1454,6 @@ static struct dnet_collector_route_nfo *sysroutes_dnet_find_interfaces(struct dn 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 that it is the proper type of route ... */ - if (entry->route_dst.addr_type != ADDR_TYPE_IP || entry->route_gw.addr_type != ADDR_TYPE_IP) - return 0; /* Not interested in IPv6 routes at the moment ... */ /* Make sure we have room for the new route */ if (dcrn->numroutes >= dcrn->capacity) { @@ -1547,11 +1462,10 @@ static int collect_dnet_routes(const struct route_entry *entry, void *arg) { } /* Now for the important business */ - dcrn->routes[dcrn->numroutes].dest = entry->route_dst.addr_ip; - addr_btom(entry->route_dst.addr_bits, - &dcrn->routes[dcrn->numroutes].netmask, - sizeof(dcrn->routes[dcrn->numroutes].netmask)); - dcrn->routes[dcrn->numroutes].gw.s_addr = entry->route_gw.addr_ip; + dcrn->routes[dcrn->numroutes].device = NULL; + 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->numroutes++; return 0; @@ -1604,7 +1518,6 @@ static struct sys_route *getsysroutes_dnet(int *howmany, char *errstr, size_t er struct sys_route *getsysroutes(int *howmany, char *errstr, size_t errstrlen) { static struct sys_route *routes = NULL; static int numroutes = 0; - FILE *routefp; assert(howmany); if (routes != NULL) { @@ -1613,14 +1526,7 @@ struct sys_route *getsysroutes(int *howmany, char *errstr, size_t errstrlen) { return routes; } - /* First let us try Linux-style /proc/net/route */ - routefp = fopen("/proc/net/route", "r"); - if (routefp) { - routes = getsysroutes_proc(routefp, howmany, errstr, errstrlen); - fclose(routefp); - } else { - routes = getsysroutes_dnet(howmany, errstr, errstrlen); - } + routes = getsysroutes_dnet(howmany, errstr, errstrlen); /* Check if we managed to get the routes and sort them if we did */ if(routes==NULL){ @@ -1641,6 +1547,9 @@ struct sys_route *getsysroutes(int *howmany, char *errstr, size_t errstrlen) { * Returns 1 if the address is thought to be localhost and 0 otherwise */ int islocalhost(const struct in_addr *const addr) { char dev[128]; + struct sockaddr_storage ss; + struct sockaddr_in *sin; + /* If it is 0.0.0.0 or starts with 127 then it is probably localhost */ if ((addr->s_addr & htonl(0xFF000000)) == htonl(0x7F000000)) @@ -1651,7 +1560,10 @@ int islocalhost(const struct in_addr *const addr) { /* If it is the same addy as a local interface, then it is probably localhost */ - if (ipaddr2devname(dev, addr) != -1) + sin = (struct sockaddr_in *) &ss; + sin->sin_family = AF_INET; + sin->sin_addr = *addr; + if (ipaddr2devname(dev, &ss) != -1) return 1; /* OK, so to a first approximation, this addy is probably not @@ -2121,7 +2033,10 @@ char *format_ip_options(u8* ipopt, int ipoptlen) { * protocol headers . */ const char *ippackethdrinfo(const u8 *packet, u32 len, int detail) { - struct ip *ip = (struct ip *) packet; /* IPv4 header structure. */ + 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. */ @@ -2133,18 +2048,16 @@ const char *ippackethdrinfo(const u8 *packet, u32 len, int detail) { 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. */ - struct in_addr saddr, daddr; /* Src and Dst IPs in binary. */ 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. */ - size_t iphdrlen=0; /* Length of the IP (4 or 6) header */ - u8 nextproto=0; /* Protocol after IP (4 or 6) header */ - - /* Ensure IP version makes sense */ - if (ip->ip_v != 4 && ip->ip_v != 6 ) - return "BOGUS! IP Version in packet is not 4"; + + datalen = len; + data = (u8 *) ip_get_data(packet, &datalen, &hdr); + if (data == NULL) + return "BOGUS! Can't parse supposed IP packet"; /* Ensure we end up with a valid detail number */ @@ -2152,20 +2065,17 @@ const char *ippackethdrinfo(const u8 *packet, u32 len, int detail) { detail=LOW_DETAIL; /* IP INFORMATION ************************************************************/ -if( ip->ip_v == 4 ){ /* IPv4 */ +if (hdr.version == 4) { /* IPv4 */ + const struct ip *ip; + const struct sockaddr_in *sin; - if (len < 20) - return "BOGUS! Packet too short."; - else{ - iphdrlen=ip->ip_hl * 4; - nextproto=ip->ip_p; - } + ip = (struct ip *) packet; /* Obtain IP source and destination info */ - saddr.s_addr = ip->ip_src.s_addr; - daddr.s_addr = ip->ip_dst.s_addr; - inet_ntop(AF_INET, &saddr, srchost, sizeof(srchost)); - inet_ntop(AF_INET, &daddr, dsthost, sizeof(dsthost)); + sin = (struct sockaddr_in *) &hdr.src; + inet_ntop(AF_INET, &sin->sin_addr.s_addr, srchost, sizeof(srchost)); + sin = (struct sockaddr_in *) &hdr.dst; + inet_ntop(AF_INET, &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 */; @@ -2211,55 +2121,35 @@ if( ip->ip_v == 4 ){ /* IPv4 */ } }else{ /* IPv6 */ + const struct ip6_hdr *ip6; + const struct sockaddr_in6 *sin6; - /* I'd rather use a regular u8 pointer to access the IPv6 header because - * it's surprinsingly easy to fuck the whole thing up using structures due - * to the weird IPv6 field alignment and the f*** compiler padding structs - * when it shouldn't. */ - u8 *ipv6pnt = (u8 *)packet; - - if (len < 40) - return "BOGUS! IPv6 Packet too short."; - else{ - iphdrlen=40; - nextproto=ipv6pnt[6]; - } + ip6 = (struct ip6_hdr *) packet; /* Obtain IP source and destination info */ - struct in6_addr ip6_src; - struct in6_addr ip6_dst; - memcpy(ip6_src.s6_addr, &ipv6pnt[8], 16); - memcpy(ip6_dst.s6_addr, &ipv6pnt[24], 16); - inet_ntop(AF_INET6, &ip6_src, srchost, sizeof(srchost)); - inet_ntop(AF_INET6, &ip6_dst, dsthost, sizeof(dsthost)); - - /* Obtain payload length, next protocol and hop limit */ - u16 *ipv6_pl = (u16 *)(&ipv6pnt[4]); - u8 *ipv6_nh = (u8 *)(& ipv6pnt[6]); - u8 *ipv6_hl = (u8 *)(& ipv6pnt[7]); - + sin6 = (struct sockaddr_in6 *) &hdr.src; + inet_ntop(AF_INET6, sin6->sin6_addr.s6_addr, srchost, sizeof(srchost)); + sin6 = (struct sockaddr_in6 *) &hdr.dst; + inet_ntop(AF_INET6, sin6->sin6_addr.s6_addr, dsthost, sizeof(dsthost)); /* Obtain flow label and traffic class */ - u32 *word = (u32 *)(&ipv6pnt[0]); - u32 flow= ntohl( *word ); - + 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=%d", - (*ipv6_hl), ip6_fl, ntohs(*ipv6_pl) + ip6->ip6_hlim, ip6_fl, ntohs(ip6->ip6_plen) ); }else if( detail == MEDIUM_DETAIL ){ Snprintf(ipinfo, sizeof(ipinfo), "hopl=%d tclass=%d flow=%x payloadlen=%d", - (*ipv6_hl), ip6_tc, ip6_fl, ntohs(*ipv6_pl) + ip6->ip6_hlim, ip6_tc, ip6_fl, ntohs(ip6->ip6_plen) ); }else if( detail==HIGH_DETAIL ){ Snprintf(ipinfo, sizeof(ipinfo), "ver=6, tclass=%x flow=%x payloadlen=%d nh=%s hopl=%d ", - ip6_tc, ip6_fl, ntohs(*ipv6_pl), - nexthdrtoa(*ipv6_nh, 1), *ipv6_hl + ip6_tc, ip6_fl, ntohs(ip6->ip6_plen), + nexthdrtoa(ip6->ip6_nxt, 1), ip6->ip6_hlim ); } @@ -2267,12 +2157,12 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* TCP INFORMATION ***********************************************************/ - if (nextproto == IPPROTO_TCP) { + if (hdr.proto == IPPROTO_TCP) { char tflags[10]; char tcpinfo[64] = ""; char buf[32]; char tcpoptinfo[256] = ""; - tcp = (struct tcp_hdr *) (packet + iphdrlen); + tcp = (struct tcp_hdr *) data; /* Let's parse the TCP header. The following code is very ugly because we @@ -2301,7 +2191,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ * 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) || (len < (u32) iphdrlen + 8) ){ + if (frag_off > 8 || datalen < 8) { Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? ?? %s (incomplete)", srchost, dsthost, ipinfo); } @@ -2309,7 +2199,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* 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 == 8) && (len >= (u32) iphdrlen + 8)) { + else if (frag_off == 8 && datalen >= 8) { tcp = (struct tcp_hdr *)((u8 *) tcp - frag_off); // ugly? @@ -2331,7 +2221,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* TCP Options */ if((u32) tcp->th_off * 4 > sizeof(struct tcp_hdr)) { - if(len < (u32) iphdrlen + (u32) tcp->th_off * 4 - frag_off) { + if (datalen < (u32) tcp->th_off * 4 - frag_off) { Snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); } else { tcppacketoptinfo((u8*) tcp + sizeof(struct tcp_hdr), @@ -2350,7 +2240,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ ntohl(tcp->th_ack), ntohs(tcp->th_win), tcpoptinfo, ipinfo); }else if( detail==HIGH_DETAIL ){ - if( len >= (u32) iphdrlen + 12 ){ /* We have at least bytes 8-20 */ + 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=%d%s%s] IP [%s]", srchost, dsthost, tflags, (unsigned long) ntohl(tcp->th_seq), @@ -2371,23 +2261,23 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* 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 ((len > (u32)iphdrlen) && (len < (u32) iphdrlen + 20)) { + else if (datalen > 0 && datalen < 20) { /* We only have the first 32 bits: source and dst port */ - if( (len >= (u32)iphdrlen + 4) && (len < (u32)iphdrlen + 8) ){ + if (datalen >= 4 && datalen < 8) { Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%d > %s:%d ?? (incomplete) %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), ipinfo); } /* We only have the first 64 bits: ports and seq number */ - if( (len >= (u32)iphdrlen + 8) && (len < (u32)iphdrlen + 12) ) { + if (datalen >= 8 && datalen < 12) { Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%d > %s:%d ?? seq=%lu (incomplete) %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), ipinfo); } /* We only have the first 96 bits: ports, seq and ack number */ - if( (len >= (u32)iphdrlen + 12) && (len < (u32)iphdrlen + 16) ) { + if (datalen >= 12 && datalen < 16) { if(detail == LOW_DETAIL){ /* We don't print ACK in low detail */ Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%d > %s:%d seq=%lu (incomplete), %s", srchost, ntohs(tcp->th_sport), dsthost, @@ -2401,7 +2291,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ } /* We are missing the last 32 bits (checksum and urgent pointer) */ - if( (len >= (u32)iphdrlen + 16) && (len < (u32)iphdrlen + 20) ) { + if (datalen >= 16 && datalen < 20) { p = tflags; /* These are basically in tcpdump order */ if (tcp->th_flags & TH_SYN) *p++ = 'S'; @@ -2446,7 +2336,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* CASE 4: where we (finally!) have a full 20 byte TCP header so we can * safely print all fields */ - else if (len >= (u32) iphdrlen + 20){ + else if (datalen >= 20) { /* TCP Flags */ p = tflags; @@ -2468,7 +2358,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* TCP Options */ if((u32) tcp->th_off * 4 > sizeof(struct tcp_hdr)) { - if(len < (u32) iphdrlen + (u32) tcp->th_off * 4) { + if (datalen < (unsigned int) tcp->th_off * 4) { Snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); } else { @@ -2512,9 +2402,9 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* UDP INFORMATION ***********************************************************/ - } else if (nextproto == IPPROTO_UDP && frag_off) { + } else if (hdr.proto == IPPROTO_UDP && frag_off) { Snprintf(protoinfo, sizeof(protoinfo), "UDP %s:?? > %s:?? fragment %s (incomplete)", srchost, dsthost, ipinfo); - } else if (nextproto == IPPROTO_UDP) { + } else if (hdr.proto == IPPROTO_UDP) { udp = (struct udp_hdr *) (packet + sizeof(struct ip)); /* TODO: See if we can segfault if we receive a fragmented packet whose IP packet does not say a thing about fragmentation */ @@ -2536,9 +2426,9 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* SCTP INFORMATION **********************************************************/ - } else if (nextproto == IPPROTO_SCTP && frag_off) { + } else if (hdr.proto == IPPROTO_SCTP && frag_off) { Snprintf(protoinfo, sizeof(protoinfo), "SCTP %s:?? > %s:?? fragment %s (incomplete)", srchost, dsthost, ipinfo); - } else if (nextproto == IPPROTO_SCTP) { + } else if (hdr.proto == IPPROTO_SCTP) { sctp = (struct sctp_hdr *) (packet + sizeof(struct ip)); if( detail == LOW_DETAIL ){ @@ -2558,9 +2448,9 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* ICMP INFORMATION **********************************************************/ - } else if (nextproto == IPPROTO_ICMP && frag_off) { + } else if (hdr.proto == IPPROTO_ICMP && frag_off) { Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s fragment %s (incomplete)", srchost, dsthost, ipinfo); - } else if (nextproto == IPPROTO_ICMP) { + } 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 */ @@ -2620,15 +2510,15 @@ if( ip->ip_v == 4 ){ /* IPv4 */ u32 mask; } *icmpmask=NULL; - /* Compute the length of the IP datagram + ICMP minimum length */ - unsigned pktlen = (iphdrlen) + 8; + /* Compute the ICMP minimum length. */ + unsigned pktlen = 8; /* We need the ICMP packet to be at least 8 bytes long */ - if (pktlen > len) + if (pktlen > datalen) goto icmpbad; - ping = (struct ppkt *) ((iphdrlen) + (char *) ip); - icmppkt=(struct icmp_packet *) ((iphdrlen) + (char *) ip); + ping = (struct ppkt *) data; + icmppkt = (struct icmp_packet *) data; switch(icmppkt->type) { @@ -2641,12 +2531,12 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* Destination Unreachable *************/ case 3: /* Point to the start of the original datagram */ - ip2 = (struct ip *) ((char *) ip + (iphdrlen) + 8); + 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 > len){ - if(len==(u32)(iphdrlen) + 8 ) + if (pktlen > datalen) { + if(datalen == 8) Snprintf(icmptype, sizeof icmptype, "Destination unreachable%s", (detail!=LOW_DETAIL)? " (original datagram missing)" : "" ); else @@ -2667,7 +2557,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ /* We have the original datagram + the first 8 bytes of the * transport layer header */ - if ( (pktlen+8) < len) { + 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)); @@ -2691,7 +2581,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ break; case 3: - if ( (pktlen+8) < len){ + if (pktlen + 8 < datalen) { if (ip2->ip_p == IPPROTO_UDP && udp) Snprintf(icmptype, sizeof icmptype, "Port %u unreachable", ntohs(udp->uh_dport)); else if (ip2->ip_p == IPPROTO_TCP && tcp) @@ -2886,7 +2776,7 @@ if( ip->ip_v == 4 ){ /* IPv4 */ } /* End of ICMP Type switch */ - if (pktlen > len) { + if (pktlen > datalen) { icmpbad: if (ping) { /* We still have this information */ @@ -2913,17 +2803,21 @@ if( ip->ip_v == 4 ){ /* IPv4 */ } /* UNKNOWN PROTOCOL **********************************************************/ - } else if( nextproto == IPPROTO_ICMPV6){ - Snprintf(protoinfo, sizeof(protoinfo), "ICMPv6 (%d) %s > %s: %s", - ip->ip_p, srchost, dsthost, ipinfo); + } else if( hdr.proto == IPPROTO_ICMPV6){ + 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{ - if( nexthdrtoa(nextproto, 1) == NULL ){ + if( nexthdrtoa(hdr.proto, 1) == NULL ){ Snprintf(protoinfo, sizeof(protoinfo), "Unknown protocol (%d) %s > %s: %s", - ip->ip_p, srchost, dsthost, ipinfo); + hdr.proto, srchost, dsthost, ipinfo); }else{ Snprintf(protoinfo, sizeof(protoinfo), "%s (%d) %s > %s: %s", - nexthdrtoa(nextproto, 1), ip->ip_p, srchost, dsthost, ipinfo); + nexthdrtoa(hdr.proto, 1), hdr.proto, srchost, dsthost, ipinfo); } } @@ -2931,19 +2825,176 @@ if( ip->ip_v == 4 ){ /* IPv4 */ } -static int match_netmask(u32 addr1, u32 addr2, u32 mask) { - return (addr1 & mask) == (addr2 & mask); +#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; } -static int match_netmask_bits(u32 addr1, u32 addr2, int bits) { - return match_netmask(addr1, addr2, htonl((unsigned long) (0 - 1) << (32 - bits))); +/* Get an interface name from an index number. */ +static int intf_name(int index, char *name, size_t len) { + struct ifreq ifr; + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return -1; + + ifr.ifr_ifindex = index; + if (ioctl(s, SIOCGIFNAME, &ifr) < 0) + goto bail; + strncpy(name, ifr.ifr_name, len); + if (name[len - 1] != '\0') + goto bail; + + return 0; + +bail: + close(s); + return -1; } -static int match_netmask_bits(const struct sockaddr_in *addr1, - const struct sockaddr_in *addr2, int bits) { - return match_netmask_bits(addr1->sin_addr.s_addr, addr2->sin_addr.s_addr, bits); +/* 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; + unsigned char buf[512]; + void *addr; + size_t addrlen; + int fd, rc, len; + + if (dst->ss_family == AF_INET) { + addr = &((struct sockaddr_in *) dst)->sin_addr.s_addr; + addrlen = IP_ADDR_LEN; + } else if (dst->ss_family == AF_INET6) { + addr = ((struct sockaddr_in6 *) dst)->sin6_addr.s6_addr; + addrlen = IP6_ADDR_LEN; + } else { + netutil_fatal("%s: unknown address family %d", __func__, dst->ss_family); + } + + 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)); + + memset(buf, 0, sizeof(buf)); + + nlmsg = (struct nlmsghdr *) buf; + + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(*rtmsg) + RTA_LENGTH(addrlen)); + 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; + rtmsg->rtm_dst_len = addrlen * 8; + + rtattr = RTM_RTA(rtmsg); + rtattr->rta_type = RTA_DST; + rtattr->rta_len = RTA_LENGTH(addrlen); + memcpy(RTA_DATA(rtattr), addr, addrlen); + + 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)); + + /* 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; + + struct interface_info *ii; + ii = NULL; + if (device != NULL && device[0] != '\0') { + ii = getInterfaceByName(device); + if (ii == NULL) + netutil_fatal("Could not find interface %s which was specified by -e", device); + } + + for ( ; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) { + if (rtattr->rta_type == RTA_GATEWAY) { + rnfo->direct_connect = 0; + rc = set_sockaddr(&rnfo->nexthop, rtmsg->rtm_family, RTA_DATA(rtattr)); + assert(rc != -1); + } else if (rtattr->rta_type == RTA_OIF && ii == NULL) { + char namebuf[32]; + int intf_index; + + intf_index = *(int *) RTA_DATA(rtattr); + rc = intf_name(intf_index, namebuf, sizeof(namebuf)); + assert(rc != -1); + ii = getInterfaceByName(namebuf); + 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, rtmsg->rtm_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; @@ -2956,34 +3007,20 @@ static struct interface_info *find_loopback_iface(struct interface_info *ifaces, return NULL; } -/* Takes an IPv4 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 * const dst, struct route_nfo *rnfo, - char *device, struct sockaddr_storage *spoofss) { +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; - struct sockaddr_in *ifsin, *dstsin; char errstr[256]; errstr[0]='\0'; if (!dst) netutil_fatal("%s passed a NULL dst address", __func__); - dstsin = (struct sockaddr_in *) dst; - - if (dstsin->sin_family != AF_INET) - netutil_fatal("Sorry -- %s currently only supports IPv4", __func__); if(spoofss!=NULL){ /* Throughout the rest of this function we only change rnfo->srcaddr if the source isnt spoofed */ @@ -3010,8 +3047,7 @@ int route_dst(const struct sockaddr_storage * const dst, struct route_nfo *rnfo, for (i = 0; i < numifaces; i++) { struct interface_info *loopback; - ifsin = (struct sockaddr_in *) &ifaces[i].addr; - if (dstsin->sin_addr.s_addr != ifsin->sin_addr.s_addr) + if (!sockaddr_equal(dst, &ifaces[i].addr)) continue; if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0) continue; @@ -3036,7 +3072,7 @@ int route_dst(const struct sockaddr_storage * const dst, struct route_nfo *rnfo, /* 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 (!match_netmask(dstsin->sin_addr.s_addr, routes[i].dest, routes[i].netmask)) + 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) @@ -3045,26 +3081,21 @@ int route_dst(const struct sockaddr_storage * const dst, struct route_nfo *rnfo, 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, when it exactly matches the interface address, or when it + 0.0.0.0 or ::, when it exactly matches the interface address, or when it exactly matches the destination address. */ - rnfo->direct_connect = (routes[i].gw.s_addr == 0) || - (routes[i].gw.s_addr == ((struct sockaddr_in *) &routes[i].device->addr)->sin_addr.s_addr) || - (routes[i].gw.s_addr == dstsin->sin_addr.s_addr); + 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) rnfo->srcaddr = routes[i].device->addr; - ifsin = (struct sockaddr_in *) &rnfo->nexthop; - ifsin->sin_family = AF_INET; - ifsin->sin_addr = routes[i].gw; + rnfo->nexthop = routes[i].gw; return 1; } /* No match on routes. Try interfaces directly. */ for (i = 0; i < numifaces; i++) { - if (ifaces[i].addr.ss_family != AF_INET) - continue; - ifsin = (struct sockaddr_in *) &ifaces[i].addr; - if (!match_netmask_bits(dstsin, ifsin, ifaces[i].netmask_bits)) + if (!sockaddr_equal_netmask(dst, &ifaces[i].addr, ifaces[i].netmask_bits)) continue; if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0) continue; @@ -3079,6 +3110,26 @@ int route_dst(const struct sockaddr_storage * const dst, struct route_nfo *rnfo, 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 @@ -3088,7 +3139,6 @@ int Sendto(const char *functionname, int sd, const unsigned char *packet, int len, unsigned int flags, struct sockaddr *to, int tolen) { - struct sockaddr_in *sin = (struct sockaddr_in *) to; int res; int retries = 0; int sleeptime = 0; @@ -3101,7 +3151,7 @@ int Sendto(const char *functionname, int sd, numerrors++; if(numerrors <= 10) { netutil_error("sendto in %s: sendto(%d, packet, %d, 0, %s, %d) => %s", - functionname, sd, len, inet_ntoa(sin->sin_addr), tolen, + 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) { @@ -3266,6 +3316,212 @@ int send_frag_ip_packet(int sd, struct eth_nfo *eth, u8 *packet, 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(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 unsigned char *packet, unsigned int packetlen) { + struct ip6_hdr *hdr; + struct sockaddr_in6 dest = { 0 }; + int sd, n; + + sd = -1; + n = -1; + + if (packetlen < sizeof(*hdr)) + return -1; + + hdr = (struct ip6_hdr *) packet; + dest.sin6_family = AF_INET6; + memcpy(&dest.sin6_addr.s6_addr, &hdr->ip6_dst, sizeof(dest.sin6_addr.s6_addr)); + dest.sin6_port = 0; + + sd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (sd == -1) { + perror("socket"); + goto bail; + } + + n = Sendto(__func__, sd, packet, packetlen, 0, (struct sockaddr *) &dest, sizeof(dest)); + +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 must be allocated to + hold at least maxlen bytes. 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, size_t maxlen, + int level, int type, const void *data, size_t len) +{ + struct cmsghdr *cm; + + if (maxlen < msg->msg_controllen + CMSG_SPACE(len)) + return -1; + + 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; +} + +/* 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). While there are standard + functions to add extension headers, we don't use them, instead opening the + socket with a protocol the same as the Next Header field in the packet. Then + we paste in the packet contents (including extension headers) verbatim. This + allows controlling the order of headers, making broken headers, and sending + headers not understood by the underlying OS. */ +static int send_ipv6_ip(const unsigned char *packet, size_t packetlen) { + struct msghdr msg; + struct sockaddr_in6 dest = { 0 }; + struct iovec iov; + + struct ip6_hdr *hdr; + int tclass, hoplimit; + + char *control; + size_t controllen; + int sd; + int n; + + /* Allocate a control buffer big enough to hold the IPV6_TCLASS and + IPV6_HOPLIMIT options. This is a byte buffer but must be aligned for + a struct cmsghdr. See section 15.7 (p. 425) of Unix Network + Programming, third edition. */ + controllen = CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(int)); + control = (char *) safe_zalloc(controllen); + sd = -1; + n = -1; + + /* Set up sendmsg data structure. dest and iov are filled in below. */ + msg.msg_name = &dest; + msg.msg_namelen = sizeof(dest); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (packetlen < sizeof(*hdr)) + return -1; + hdr = (struct ip6_hdr *) packet; + + dest.sin6_family = AF_INET6; + memcpy(&dest.sin6_addr.s6_addr, &hdr->ip6_dst, sizeof(dest.sin6_addr.s6_addr)); + dest.sin6_port = 0; + + iov.iov_base = (unsigned char *) packet + sizeof(*hdr); + iov.iov_len = packetlen - sizeof(*hdr); + + /* 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, controllen, 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, controllen, 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. */ + + sd = socket(AF_INET6, SOCK_RAW, hdr->ip6_nxt); + if (sd == -1) { + perror("socket"); + goto bail; + } + + n = sendmsg(sd, &msg, 0); + if (n == -1) + perror("sendmsg"); + +bail: + free(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, struct eth_nfo *eth, 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(packet, packetlen); +#elif !WIN32 + return send_ipv6_ip(packet, packetlen); +#endif + } + + return -1; +} + #ifdef WIN32 @@ -3383,29 +3639,13 @@ void set_pcap_filter(const char *device, pcap_t *pd, const char *bpf, ...) { va_list ap; char buf[3072]; struct bpf_program fcode; -#ifndef __amigaos__ - unsigned int localnet, netmask; -#else - bpf_u_int32 localnet, netmask; -#endif - char err0r[256]; - - // Cast below is becaue OpenBSD apparently has a version that takes a - // non-const device (hopefully they don't actually write to it). - if (pcap_lookupnet((char *) device, &localnet, &netmask, err0r) < 0) - netutil_fatal("Failed to lookup subnet/netmask for device (%s): %s", device, err0r); va_start(ap, bpf); if (Vsnprintf(buf, sizeof(buf), bpf, ap) >= (int) sizeof(buf)) netutil_fatal("%s called with too-large filter arg\n", __func__); va_end(ap); - /* Due to apparent bug in libpcap */ - /* Maybe this bug no longer exists ... I'll comment out for now - * if (islocalhost(target->v4hostip())) - * buf[0] = '\0'; */ - - if (pcap_compile(pd, &fcode, buf, 0, netmask) < 0) + if (pcap_compile(pd, &fcode, buf, 0, 0) < 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)); @@ -3554,6 +3794,256 @@ int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, return 1; } +/* 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, + void (*traceND_callback)(int, const u8 *, u32 , struct timeval *)) { + static int warning = 0; + int datalink; + struct pcap_pkthdr head; + u8 *p; + int timedout = 0; + int badcounter = 0; + unsigned int offset=0; + struct timeval tv_start, tv_end; + struct icmpv6_hdr *icmp6_header = NULL; + struct icmpv6_msg_nd *na = NULL; + + 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)); + + if (datalink == DLT_EN10MB) { + offset = ETH_HDR_LEN; + } else if (datalink == DLT_LINUX_SLL) { + /* The datalink type is Linux "cooked" sockets. See pcap-linktype(7). */ + offset = 16; + } else { + netutil_fatal("%s called on interface that is datatype %d rather than DLT_EN10MB (%d) or DLT_LINUX_SLL (%d)", __func__, datalink, DLT_EN10MB, DLT_LINUX_SLL); + } + + if (to_usec > 0) { + gettimeofday(&tv_start, NULL); + } + + do { +#ifdef WIN32 + if (to_usec == 0) { + PacketSetReadTimeout(pd->adapter, 1); + } else { + gettimeofday(&tv_end, NULL); + long to_left = + MAX(1, (to_usec - TIMEVAL_SUBTRACT(tv_end, tv_start)) / 1000); + // Set the timeout (BUGBUG: this is cheating) + PacketSetReadTimeout(pd->adapter, to_left); + } +#endif + + p = NULL; + + if (pcap_select(pd, to_usec) == 0) + timedout = 1; + else + p = (u8 *) pcap_next(pd, &head); + + if (p && head.caplen >= offset + IP6_HDR_LEN + 32) { + /* hw type eth (0x0001), prot ip (0x0800), + hw size (0x06), prot size (0x04) */ + icmp6_header = (struct icmpv6_hdr *)(p + offset + IP6_HDR_LEN); + na = (struct icmpv6_msg_nd *)(p + offset + IP6_HDR_LEN + ICMPV6_HDR_LEN); + if (icmp6_header->icmpv6_type == ICMPV6_NEIGHBOR_ADVERTISEMENT && + icmp6_header->icmpv6_code == 0 && + na->icmpv6_option_type == 2 && na->icmpv6_option_length == 1){ + memcpy(sendermac, &na->icmpv6_mac, 6); + memcpy(&senderIP->sin6_addr.s6_addr, &na->icmpv6_target, 16); + break; + } + } + + if (!p) { + /* 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) { + // FIXME: I eventually need to figure out why Windows head.ts time is sometimes BEFORE the time I + // sent the packet (which is according to gettimeofday() in nbase). For now, I will sadly have to + // use gettimeofday() for Windows in this case + // Actually I now allow .05 discrepancy. So maybe this isn't needed. I'll comment out for now. + // Nope: it is still needed at least for Windows. Sometimes the time from he 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 + } + if(traceND_callback!=NULL){ + /* TODO: First parameter "2" is a hardcoded value for Nmap's PacketTrace::RECV*/ + traceND_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; + 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(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, 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 diff --git a/libnetutil/netutil.h b/libnetutil/netutil.h index 08f1e76b4..69c22760c 100644 --- a/libnetutil/netutil.h +++ b/libnetutil/netutil.h @@ -114,6 +114,17 @@ enum { OP_FAILURE = -1, OP_SUCCESS = 0 }; #define IPPROTO_SCTP 132 #endif +/* Container used for information common to IPv4 and IPv6 headers, used by + ip_get_data. */ +struct abstract_ip_hdr { + u8 version; /* 4 or 6. */ + struct sockaddr_storage src; + struct sockaddr_storage dst; + u8 proto; /* IPv4 proto or IPv6 next header. */ + u8 ttl; /* IPv4 TTL or IPv6 hop limit. */ + u8 ipid; /* IPv4 IP ID or IPv6 flow label. */ +}; + void netutil_fatal(const char *str, ...) __attribute__ ((noreturn)) __attribute__ ((format (printf, 1, 2))); @@ -181,6 +192,17 @@ int ip_is_reserved(struct in_addr *ip); int arp_cache_get(struct sockaddr_storage *ss, u8 *mac); int arp_cache_set(struct sockaddr_storage *ss, u8 *mac); +const void *ip_get_data(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr); +/* Get the upper-layer protocol from an IPv4 packet. */ +const void *ipv4_get_data(const struct ip *ip, unsigned int *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); +const void *icmp_get_data(const struct icmp_hdr *icmp, unsigned int *len); +const void *icmpv6_get_data(const struct icmpv6_hdr *icmpv6, unsigned int *len); + /* Standard BSD internet checksum routine. */ unsigned short in_cksum(u16 *ptr, int nbytes); @@ -190,6 +212,11 @@ unsigned short in_cksum(u16 *ptr, int nbytes); unsigned short ipv4_pseudoheader_cksum(const struct in_addr *src, const struct in_addr *dst, u8 proto, u16 len, const void *hstart); +/* 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); + void sethdrinclude(int sd); void set_ipoptions(int sd, void *opts, size_t optslen); void set_ttl(int sd, int ttl); @@ -253,9 +280,9 @@ struct route_nfo { struct sys_route { struct interface_info *device; - u32 dest; - u32 netmask; - struct in_addr gw; /* gateway - 0 if none */ + struct sockaddr_storage dest; + u16 netmask_bits; + struct sockaddr_storage gw; /* gateway - 0 if none */ }; struct eth_nfo { @@ -295,11 +322,19 @@ void tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize); /* Convert an IP address to the device (IE ppp0 eth0) using that * address. Supplied "dev" must be able to hold at least 32 bytes. * Returns 0 on success or -1 in case of error. */ -int ipaddr2devname( char *dev, const struct in_addr *addr ); +int ipaddr2devname( char *dev, const struct sockaddr_storage *addr ); -/* Convert a network interface name (IE ppp0 eth0) to an IPv4 address. +/* Convert a network interface name (IE ppp0 eth0) to an IP address. * Returns 0 on success or -1 in case of error. */ -int devname2ipaddr(char *dev, struct in_addr *addr); +int devname2ipaddr(char *dev, struct sockaddr_storage *addr); + +int sockaddr_equal(const struct sockaddr_storage *a, + const struct sockaddr_storage *b); + +int sockaddr_equal_netmask(const struct sockaddr_storage *a, + const struct sockaddr_storage *b, u16 nbits); + +int sockaddr_equal_zero(const struct sockaddr_storage *s); /* Returns an allocated array of struct interface_info representing the available interfaces. The number of interfaces is returned in *howmany. This @@ -392,7 +427,7 @@ const char *ippackethdrinfo(const u8 *packet, u32 len, int detail); * 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 * const dst, struct route_nfo *rnfo, - char *device, struct sockaddr_storage *spoofss); + const char *device, const struct sockaddr_storage *spoofss); /* Send an IP packet over a raw socket. */ int send_ip_packet_sd(int sd, u8 *packet, unsigned int packetlen); @@ -405,6 +440,9 @@ int send_ip_packet_eth(struct eth_nfo *eth, u8 *packet, unsigned int packetlen); * ethernet level. */ int send_ip_packet_eth_or_sd(int sd, struct eth_nfo *eth, u8 *packet, unsigned int packetlen); +/* Sends an IPv4 packet. */ +int send_ipv6_packet_eth_or_sd(int sd, struct eth_nfo *eth, const u8 *packet, unsigned int len); + /* 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 */ @@ -443,6 +481,23 @@ bool doArp(const char *dev, const u8 *srcmac, u8 *targetmac, void (*traceArp_callback)(int, const u8 *, u32 , struct timeval *)); + +/* 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 (*traceArp_callback)(int, const u8 *, u32 , struct timeval *) + ) ; + /* 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) diff --git a/nmap.cc b/nmap.cc index f10cd2e29..2d449c8de 100644 --- a/nmap.cc +++ b/nmap.cc @@ -382,20 +382,24 @@ static unsigned short *merge_port_lists(unsigned short *port_list1, int count1, void validate_scan_lists(scan_lists &ports, NmapOps &o){ if (o.pingtype == PINGTYPE_UNKNOWN) { - if (o.isr00t && o.pf() == PF_INET) { - o.pingtype = DEFAULT_PING_TYPES; + if (o.isr00t) { + if (o.pf() == PF_INET){ + o.pingtype = DEFAULT_IPV4_PING_TYPES; + } else { + o.pingtype = DEFAULT_IPV6_PING_TYPES; + } getpts_simple(DEFAULT_PING_ACK_PORT_SPEC, SCAN_TCP_PORT, &ports.ack_ping_ports, &ports.ack_ping_count); getpts_simple(DEFAULT_PING_SYN_PORT_SPEC, SCAN_TCP_PORT, &ports.syn_ping_ports, &ports.syn_ping_count); } else { - o.pingtype = PINGTYPE_TCP; // if nonr00t or IPv6 + o.pingtype = PINGTYPE_TCP; // if nonr00t getpts_simple(DEFAULT_PING_CONNECT_PORT_SPEC, SCAN_TCP_PORT, &ports.syn_ping_ports, &ports.syn_ping_count); } } - if ((o.pingtype & PINGTYPE_TCP) && (!o.isr00t || o.pf() != PF_INET)) { + if ((o.pingtype & PINGTYPE_TCP) && (!o.isr00t)) { // We will have to do a connect() style ping // Pretend we wanted SYN probes all along. if (ports.ack_ping_count > 0) { @@ -629,6 +633,8 @@ int nmap_main(int argc, char *argv[]) { {"adler32", no_argument, 0, 0}, {"stats_every", required_argument, 0, 0}, {"stats-every", required_argument, 0, 0}, + {"route_dst", required_argument, 0, 0}, + {"route-dst", required_argument, 0, 0}, {0, 0, 0, 0} }; @@ -912,6 +918,27 @@ int nmap_main(int argc, char *argv[]) { if (d < 0) fatal("Argument to --stats-every cannot be negative."); o.stats_interval = d; + } else if(optcmp(long_options[option_index].name, "route-dst") == 0) { + struct sockaddr_storage ss; + struct route_nfo rnfo; + size_t sslen; + + if (!resolve(optarg, 0, 0, &ss, &sslen, 0)) + fatal("Can't resolve %s.", optarg); + + printf("%s\n", inet_ntop_ez(&ss, sslen)); + + if (!route_dst(&ss, &rnfo, o.device, o.SourceSockAddr())) { + printf("Can't route %s (%s).", optarg, inet_ntop_ez(&ss, sslen)); + } else { + printf("%s %s", rnfo.ii.devname, rnfo.ii.devfullname); + printf(" srcaddr %s", inet_ntop_ez(&rnfo.srcaddr, sizeof(rnfo.srcaddr))); + if (rnfo.direct_connect) + printf(" direct"); + else + printf(" nexthop %s", inet_ntop_ez(&rnfo.nexthop, sizeof(rnfo.nexthop))); + } + printf("\n"); } else { fatal("Unknown long option (%s) given@#!$#$", long_options[option_index].name); } @@ -929,7 +956,8 @@ int nmap_main(int argc, char *argv[]) { o.script = 1; #endif if (o.isr00t) { - o.osscan++; + if (o.af() == AF_INET) + o.osscan++; o.traceroute = true; } break; @@ -1121,7 +1149,7 @@ int nmap_main(int argc, char *argv[]) { else if (*optarg == 'B') { if (ports.ack_ping_count > 0) fatal("Only one -PB, -PA, or -PT option is allowed. Combine port ranges with commas."); - o.pingtype = DEFAULT_PING_TYPES; + o.pingtype = DEFAULT_IPV4_PING_TYPES; if (*(optarg + 1) != '\0') { getpts_simple(optarg + 1, SCAN_TCP_PORT, &ports.ack_ping_ports, &ports.ack_ping_count); if (ports.ack_ping_count <= 0) @@ -1349,14 +1377,10 @@ int nmap_main(int argc, char *argv[]) { } #endif -#if HAVE_IPV6 - if(o.af() == AF_INET6 && o.traceroute) - fatal("Traceroute does not support IPv6"); -#endif if (o.traceroute && !o.isr00t) fatal("Traceroute has to be run as root"); - if (o.traceroute && (o.idlescan || o.connectscan)) - fatal("Traceroute does not support idle or connect scan"); + if (o.traceroute && o.idlescan) + fatal("Traceroute does not support idle scan"); if ((o.noportscan) && (portlist || o.fastscan)) fatal("You cannot use -F (fast scan) or -p (explicit port selection) when not doing a port scan"); @@ -1459,23 +1483,19 @@ int nmap_main(int argc, char *argv[]) { * --None have been specified AND * --We are root and doing tcp ping OR * --We are doing a raw sock scan and NOT pinging anyone */ - if (o.af() == AF_INET && o.v4sourceip() && !*o.device) { - if (ipaddr2devname(o.device, o.v4sourceip()) != 0) { + if (o.SourceSockAddr() && !*o.device) { + if (ipaddr2devname(o.device, o.SourceSockAddr()) != 0) { fatal("Could not figure out what device to send the packet out on with the source address you gave me! If you are trying to sp00f your scan, this is normal, just give the -e eth0 or -e ppp0 or whatever. Otherwise you can still use -e, but I find it kindof fishy."); } } - if (o.af() == AF_INET && *o.device && !o.v4sourceip()) { - struct sockaddr_in tmpsock; + if (*o.device && !o.SourceSockAddr()) { + struct sockaddr_storage tmpsock; memset(&tmpsock, 0, sizeof(tmpsock)); - if (devname2ipaddr(o.device, &(tmpsock.sin_addr)) == -1) { + if (devname2ipaddr(o.device, &tmpsock) == -1) { fatal("I cannot figure out what source address to use for device %s, does it even exist?", o.device); } - tmpsock.sin_family = AF_INET; -#if HAVE_SOCKADDR_SA_LEN - tmpsock.sin_len = sizeof(tmpsock); -#endif - o.setSourceSockAddr((struct sockaddr_storage *) &tmpsock, sizeof(tmpsock)); + o.setSourceSockAddr(&tmpsock, sizeof(tmpsock)); } @@ -2001,11 +2021,9 @@ static bool target_needs_new_hostgroup(std::vector &targets, cope with that, because it uses IP addresses to look up targets from replies. What happens is one target gets the replies for all probes referring to the same IP address. */ - if (o.af() == AF_INET) { - for (it = targets.begin(); it != targets.end(); it++) { - if ((*it)->v4host().s_addr == target->v4host().s_addr) - return true; - } + for (it = targets.begin(); it != targets.end(); it++) { + if (sockaddr_storage_cmp((*it)->TargetSockAddr(), target->TargetSockAddr()) == 0) + return true; } return false; diff --git a/nmap.h b/nmap.h index b834dad41..d1a3e9bc5 100644 --- a/nmap.h +++ b/nmap.h @@ -361,7 +361,8 @@ void *realloc(); -PE -PA80 -PS443 -PP -PE -PA80 -PS443 -PP -PU40125 We use the four-probe combination. */ -#define DEFAULT_PING_TYPES (PINGTYPE_ICMP_PING|PINGTYPE_TCP|PINGTYPE_TCP_USE_ACK|PINGTYPE_TCP_USE_SYN|PINGTYPE_ICMP_TS) +#define DEFAULT_IPV4_PING_TYPES (PINGTYPE_ICMP_PING|PINGTYPE_TCP|PINGTYPE_TCP_USE_ACK|PINGTYPE_TCP_USE_SYN|PINGTYPE_ICMP_TS) +#define DEFAULT_IPV6_PING_TYPES (PINGTYPE_ICMP_PING|PINGTYPE_TCP|PINGTYPE_TCP_USE_ACK|PINGTYPE_TCP_USE_SYN) #define DEFAULT_PING_ACK_PORT_SPEC "80" #define DEFAULT_PING_SYN_PORT_SPEC "443" /* For nonroot. */ diff --git a/nmap_config.h.in b/nmap_config.h.in index b9f59305d..55e9165c2 100644 --- a/nmap_config.h.in +++ b/nmap_config.h.in @@ -130,6 +130,8 @@ #undef HAVE_SYS_SOCKIO_H +#undef HAVE_LINUX_RTNETLINK_H + #undef HAVE_SYS_STAT_H #undef HAVE_NET_IF_H @@ -196,4 +198,6 @@ extern "C" int gethostname (char *, unsigned int); #undef NETBSD #undef MACOSX +#undef HAVE_IPV6_IPPROTO_RAW + #endif /* CONFIG_H */ diff --git a/nse_dnet.cc b/nse_dnet.cc index 1c1cbbaa3..fdc19fd19 100644 --- a/nse_dnet.cc +++ b/nse_dnet.cc @@ -287,7 +287,7 @@ static int ip_send (lua_State *L) usesock: #ifdef WIN32 if (strlen(dev) > 0) - win32_warn_raw_sockets(dev); + win32_fatal_raw_sockets(dev); #endif ret = send_ip_packet(udata->sock, NULL, (u8 *) packet, lua_objlen(L, 2)); } diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc index 31333845b..0796137cc 100644 --- a/nse_nmaplib.cc +++ b/nse_nmaplib.cc @@ -471,7 +471,7 @@ static int l_set_port_state (lua_State *L) target->ports.setPortState(p->portno, p->proto, PORT_CLOSED); break; } - target->ports.setStateReason(p->portno, p->proto, ER_SCRIPT, 0, 0); + target->ports.setStateReason(p->portno, p->proto, ER_SCRIPT, 0, NULL); } return 0; } diff --git a/osscan2.cc b/osscan2.cc index 61d954253..669466421 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -934,7 +934,7 @@ HostOsScan::HostOsScan(Target *t) { } else { /* Init our raw socket */ #ifdef WIN32 - win32_warn_raw_sockets(t->deviceName()); + win32_fatal_raw_sockets(t->deviceName()); #endif if ((rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 ) pfatal("socket troubles in %s", __func__); @@ -3253,7 +3253,7 @@ static void doSeqTests(OsScanInfo *OSI, HostOsScan *HOS) { struct ip *ip = NULL; struct link_header linkhdr; - struct sockaddr_in sin; + struct sockaddr_storage ss; unsigned int bytes; struct timeval rcvdtime; @@ -3367,12 +3367,12 @@ static void doSeqTests(OsScanInfo *OSI, HostOsScan *HOS) { if(bytes < (4 * ip->ip_hl) + 4U) continue; - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hsi = OSI->findIncompleteHost((struct sockaddr_storage *) &sin); + memset(&ss, 0, sizeof(ss)); + ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; + ss.ss_family = AF_INET; + hsi = OSI->findIncompleteHost(&ss); if (!hsi) continue; /* Not from one of our targets. */ - setTargetMACIfAvailable(hsi->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); @@ -3417,7 +3417,7 @@ static void doTUITests(OsScanInfo *OSI, HostOsScan *HOS) { struct ip *ip = NULL; struct link_header linkhdr; - struct sockaddr_in sin; + struct sockaddr_storage ss; unsigned int bytes; struct timeval rcvdtime; @@ -3536,12 +3536,12 @@ static void doTUITests(OsScanInfo *OSI, HostOsScan *HOS) { if(bytes < (4 * ip->ip_hl) + 4U) continue; - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hsi = OSI->findIncompleteHost((struct sockaddr_storage *) &sin); + memset(&ss, 0, sizeof(ss)); + ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; + ss.ss_family = AF_INET; + hsi = OSI->findIncompleteHost(&ss); if (!hsi) continue; /* Not from one of our targets. */ - setTargetMACIfAvailable(hsi->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); diff --git a/output.cc b/output.cc index 45fa23aeb..1adbc8e8c 100644 --- a/output.cc +++ b/output.cc @@ -241,15 +241,13 @@ static void print_xml_service(const struct serviceDeductions *sd) { } #ifdef WIN32 -/* Display a warning that a device is not Ethernet and so raw sockets - will be used. The warning is shown only once per unique device name. */ -void win32_warn_raw_sockets(const char *devname) { - static set shown_names; - - if (devname != NULL && shown_names.find(devname) == shown_names.end()) { - error("WARNING: Using raw sockets because %s is not an ethernet device." - " This probably won't work on Windows.\n", devname); - shown_names.insert(devname); +/* Show a fatal error explaining that an interface is not Ethernet and won't + work on Windows. Do nothing if --send-ip (PACKET_SEND_IP_STRONG) was used. */ +void win32_fatal_raw_sockets(const char *devname) { + if ((o.sendpref & PACKET_SEND_IP_STRONG) == 0) { + fatal("Only ethernet devices can be used for raw scans on Windows, and\n" + "\"%s\" is not an ethernet device. Use the --unprivileged option\n" + "for this scan.", devname); } } @@ -383,9 +381,7 @@ int print_iflist(void) { /* OK -- time to handle routes */ errstr[0]='\0'; routes = getsysroutes(&numroutes, errstr, sizeof(errstr)); - u32 mask_nbo; u16 nbits; - struct in_addr ia; if (routes==NULL || numroutes<= 0) { log_write(LOG_PLAIN, "ROUTES: NONE FOUND(!)\n"); if (o.debugging) @@ -397,15 +393,12 @@ int print_iflist(void) { Tbl->addItem(0, devcol, false, "DEV", 3); Tbl->addItem(0, gwcol, false, "GATEWAY", 7); for (i = 0; i < numroutes; i++) { - mask_nbo = routes[i].netmask; - addr_mtob(&mask_nbo, sizeof(mask_nbo), &nbits); - assert(nbits <= 32); - ia.s_addr = routes[i].dest; - Tbl->addItemFormatted(i + 1, dstcol, false, "%s/%d", inet_ntoa(ia), - nbits); + nbits = routes[i].netmask_bits; + Tbl->addItemFormatted(i + 1, dstcol, false, "%s/%d", + inet_ntop_ez(&routes[i].dest, sizeof(routes[i].dest)), nbits); Tbl->addItem(i + 1, devcol, false, routes[i].device->devfullname); - if (routes[i].gw.s_addr != 0) - Tbl->addItem(i + 1, gwcol, true, inet_ntoa(routes[i].gw)); + if (!sockaddr_equal_zero(&routes[i].gw)) + Tbl->addItem(i + 1, gwcol, true, inet_ntop_ez(&routes[i].gw, sizeof(routes[i].gw))); } log_write(LOG_PLAIN, "**************************ROUTES**************************\n"); log_write(LOG_PLAIN, "%s\n", Tbl->printableTable(NULL)); @@ -653,8 +646,8 @@ void printportoutput(Target *currenths, PortList *plist) { xml_attribute("state", "%s", state); xml_attribute("reason", "%s", reason_str(current->reason.reason_id, SINGULAR)); xml_attribute("reason_ttl", "%d", current->reason.ttl); - if (current->reason.ip_addr.s_addr) - xml_attribute("reason_ip", "%s", inet_ntoa(current->reason.ip_addr)); + if (current->reason.ip_addr.ss_family != AF_UNSPEC) + xml_attribute("reason_ip", "%s", inet_ntop_ez(¤t->reason.ip_addr, sizeof(current->reason.ip_addr))); xml_close_empty_tag(); if (proto && proto->p_name && *proto->p_name) { @@ -765,8 +758,8 @@ void printportoutput(Target *currenths, PortList *plist) { xml_attribute("state", "%s", state); xml_attribute("reason", "%s", reason_str(current->reason.reason_id, SINGULAR)); xml_attribute("reason_ttl", "%d", current->reason.ttl); - if (current->reason.ip_addr.s_addr) - xml_attribute("reason_ip", "%s", inet_ntoa(current->reason.ip_addr)); + if (current->reason.ip_addr.ss_family != AF_UNSPEC) + xml_attribute("reason_ip", "%s", inet_ntop_ez(¤t->reason.ip_addr, sizeof(current->reason.ip_addr))); xml_close_empty_tag(); if (sd.name || sd.service_fp) @@ -2029,13 +2022,15 @@ static void printtraceroute_normal(Target *currenths) { } else if (probe.type == PS_SCTP) { log_write(LOG_PLAIN, "TRACEROUTE (using port %d/%s)\n", probe.pd.sctp.dport, proto2ascii_lowercase(probe.proto)); - } else if (probe.type == PS_ICMP || probe.type == PS_PROTO) { + } else if (probe.type == PS_ICMP || probe.type == PS_ICMPV6 || probe.type == PS_PROTO) { struct protoent *proto = nmap_getprotbynum(htons(probe.proto)); log_write(LOG_PLAIN, "TRACEROUTE (using proto %d/%s)\n", probe.proto, proto ? proto->p_name : "unknown"); - } else { + } else if (probe.type == PS_NONE) { /* "Traces" of directly connected targets don't send any packets. */ log_write(LOG_PLAIN, "TRACEROUTE\n"); + } else { + fatal("Unknown probe type %d.", probe.type); } row = 0; diff --git a/output.h b/output.h index 3df1a0afe..a7d39854a 100644 --- a/output.h +++ b/output.h @@ -136,9 +136,9 @@ #include #ifdef WIN32 -/* Display a warning that a device is not Ethernet and so raw sockets - will be used. The warning is shown only once per unique device name. */ -void win32_warn_raw_sockets(const char *devname); +/* Show a fatal error explaining that an interface is not Ethernet and won't + work on Windows. Do nothing if --send-ip (PACKET_SEND_IP_STRONG) was used. */ +void win32_fatal_raw_sockets(const char *devname); #endif /* Prints the familiar Nmap tabular output showing the "interesting" diff --git a/portlist.cc b/portlist.cc index 2d5d3cab5..e1eca48a1 100644 --- a/portlist.cc +++ b/portlist.cc @@ -545,7 +545,7 @@ void PortList::setPortState(u16 portno, u8 protocol, int state) { state_counts_proto[proto][state]++; if(state == PORT_FILTERED || state == PORT_OPENFILTERED) - setStateReason(portno, protocol, ER_NORESPONSE, 0, 0); + setStateReason(portno, protocol, ER_NORESPONSE, 0, NULL); return; } @@ -894,14 +894,18 @@ bool PortList::hasOpenPorts() const { getStateCounts(PORT_UNFILTERED) != 0; } -int PortList::setStateReason(u16 portno, u8 proto, reason_t reason, u8 ttl, u32 ip_addr) { +int PortList::setStateReason(u16 portno, u8 proto, reason_t reason, u8 ttl, + const struct sockaddr_storage *ip_addr) { Port *answer = NULL; answer = createPort(portno, proto); /* set new reason and increment its count */ answer->reason.reason_id = reason; - answer->reason.ip_addr.s_addr = ip_addr; + if (ip_addr == NULL) + answer->reason.ip_addr.ss_family = AF_UNSPEC; + else + answer->reason.ip_addr = *ip_addr; answer->reason.ttl = ttl; return 0; } diff --git a/portlist.h b/portlist.h index 1769acc25..fccee8878 100644 --- a/portlist.h +++ b/portlist.h @@ -240,7 +240,7 @@ class PortList { Port *nextPort(const Port *cur, Port *next, int allowed_protocol, int allowed_state); - int setStateReason(u16 portno, u8 proto, reason_t reason, u8 ttl, u32 ip_addr); + int setStateReason(u16 portno, u8 proto, reason_t reason, u8 ttl, const struct sockaddr_storage *ip_addr); int numscriptresults; /* Total number of scripts which produced output */ diff --git a/portreasons.cc b/portreasons.cc index b63f805ff..c237d92f5 100644 --- a/portreasons.cc +++ b/portreasons.cc @@ -161,6 +161,9 @@ reason_map_type::reason_map_type(){ reason_map[ER_UNKNOWN] = reason_string("unknown-response","unknown-responses"); reason_map[ER_USER] = reason_string("user-set","user-sets"); + reason_map[ER_NOROUTE] = reason_string("no-route", "no-routes"); + reason_map[ER_BEYONDSCOPE] = reason_string("beyond-scope", "beyond-scopes"); + reason_map[ER_REJECTROUTE] = reason_string("reject-route", "reject-routes"); } /* Map holding plural and singular versions of error codes */ @@ -169,7 +172,7 @@ reason_map_type reason_map; /* Function to Translate ICMP codes and types to * * Reason Codes */ -reason_codes icmp_to_reason(int icmp_type, int icmp_code){ +static reason_codes icmpv4_to_reason(int icmp_type, int icmp_code) { switch(icmp_type){ @@ -212,6 +215,47 @@ reason_codes icmp_to_reason(int icmp_type, int icmp_code){ return ER_UNKNOWN; }; +static reason_codes icmpv6_to_reason(int icmp_type, int icmp_code) { + + switch(icmp_type){ + + case ICMPV6_ECHOREPLY: + return ER_ECHOREPLY; + + case ICMPV6_UNREACH: + switch(icmp_code) { + case ICMPV6_UNREACH_NOROUTE: + return ER_NOROUTE; + case ICMPV6_UNREACH_PROHIB: + return ER_ADMINPROHIBITED; + case ICMPV6_UNREACH_SCOPE: + return ER_BEYONDSCOPE; + case ICMPV6_UNREACH_ADDR: + return ER_HOSTUNREACH; + case ICMPV6_UNREACH_PORT: + return ER_PORTUNREACH; + case ICMPV6_UNREACH_FILTER_PROHIB: + return ER_ADMINPROHIBITED; + case ICMPV6_UNREACH_REJECT_ROUTE: + return ER_REJECTROUTE; + } + return ER_UNKNOWN; + + case ICMPV6_TIMEXCEED: + return ER_TIMEEXCEEDED; + } + return ER_UNKNOWN; +}; + +reason_codes icmp_to_reason(u8 proto, int icmp_type, int icmp_code) { + if (proto == IPPROTO_ICMP) + return icmpv4_to_reason(icmp_type, icmp_code); + else if (proto == IPPROTO_ICMPV6) + return icmpv6_to_reason(icmp_type, icmp_code); + else + return ER_UNKNOWN; +} + static void state_reason_summary_init(state_reason_summary_t *r) { r->reason_id = ER_UNKNOWN; r->count = 0; @@ -378,7 +422,7 @@ const char *reason_str(reason_t reason_code, unsigned int number) { void state_reason_init(state_reason_t *reason) { reason->reason_id = ER_UNKNOWN; - reason->ip_addr.s_addr = 0; + reason->ip_addr.ss_family = AF_UNSPEC; reason->ttl = 0; } @@ -455,7 +499,7 @@ char *port_reason_str(state_reason_t r) { static char reason[128]; memset(reason,'\0', 128); Snprintf(reason, 128, "%s%s%s", reason_str(r.reason_id, SINGULAR), - (r.ip_addr.s_addr==0)?"":" from ", - (r.ip_addr.s_addr==0)?"":inet_ntoa(r.ip_addr)); + (r.ip_addr.ss_family==AF_UNSPEC)?"":" from ", + (r.ip_addr.ss_family==AF_UNSPEC)?"":inet_ntop_ez(&r.ip_addr, sizeof(r.ip_addr))); return reason; } diff --git a/portreasons.h b/portreasons.h index b9a2f763a..d65cf1bd1 100644 --- a/portreasons.h +++ b/portreasons.h @@ -125,7 +125,7 @@ public: * why a port is in a specific state */ typedef struct port_reason { reason_t reason_id; - struct in_addr ip_addr; + struct sockaddr_storage ip_addr; unsigned short ttl; } state_reason_t; @@ -152,7 +152,8 @@ enum reason_codes { ER_ADDRESSMASKREPLY, ER_NOIPIDCHANGE, ER_IPIDCHANGE, ER_ARPRESPONSE, ER_TCPRESPONSE, ER_NORESPONSE, ER_INITACK, ER_ABORT, - ER_LOCALHOST, ER_SCRIPT, ER_UNKNOWN, ER_USER + ER_LOCALHOST, ER_SCRIPT, ER_UNKNOWN, ER_USER, + ER_NOROUTE, ER_BEYONDSCOPE, ER_REJECTROUTE, }; /* A map of reason_codes to plural and singular * @@ -171,7 +172,7 @@ public: }; /* Function to translate ICMP code and typ to reason code */ -reason_codes icmp_to_reason(int icmp_type, int icmp_code); +reason_codes icmp_to_reason(u8 proto, int icmp_type, int icmp_code); /* passed to the print_state_summary. * STATE_REASON_EMPTY will append to the current line, prefixed with " because of" diff --git a/scan_engine.cc b/scan_engine.cc index f02844eb3..45641e7e4 100644 --- a/scan_engine.cc +++ b/scan_engine.cc @@ -806,31 +806,44 @@ void UltraProbe::setARP(u8 *arppkt, u32 arplen) { internal IPProbe. The relevent probespec is necessary for setIP because pspec.type is ambiguous with just the ippacket (e.g. a tcp packet could be PS_PROTO or PS_TCP). */ -void UltraProbe::setIP(u8 *ippacket, u32 iplen, const probespec *pspec) { - struct ip *ipv4 = (struct ip *) ippacket; +void UltraProbe::setIP(u8 *ippacket, u32 len, const probespec *pspec) { + struct ip *ip = (struct ip *) ippacket; struct tcp_hdr *tcp = NULL; struct udp_hdr *udp = NULL; struct sctp_hdr *sctp = NULL; + const void *data; + u8 hdr; type = UP_IP; - if (ipv4->ip_v != 4) - fatal("Bogus packet passed to %s -- only IPv4 packets allowed", - __func__); - assert(iplen >= 20); - assert(iplen == (u32) ntohs(ipv4->ip_len)); - probes.IP.ipid = ntohs(ipv4->ip_id); - if (ipv4->ip_p == IPPROTO_TCP) { - assert (iplen >= (unsigned) ipv4->ip_hl * 4 + 20); - tcp = (struct tcp_hdr *) ((u8 *) ipv4 + ipv4->ip_hl * 4); + if (ip->ip_v == 4) { + data = ipv4_get_data(ip, &len); + assert(data != NULL); + assert(len + ip->ip_hl * 4 == (u32) ntohs(ip->ip_len)); + probes.IP.ipid = ntohs(ip->ip_id); + hdr = ip->ip_p; + } else if (ip->ip_v == 6) { + const struct ip6_hdr *ip6 = (struct ip6_hdr *) ippacket; + data = ipv6_get_data(ip6, &len, &hdr); + assert(data != NULL); + assert(len == (u32) ntohs(ip6->ip6_plen)); + probes.IP.ipid = ntohs(ip6->ip6_flow & IP6_FLOWLABEL_MASK) & 0xFFFF; + hdr = ip6->ip6_nxt; + } else { + fatal("Bogus packet passed to %s -- only IP packets allowed", __func__); + } + + if (hdr == IPPROTO_TCP) { + assert(len >= 20); + tcp = (struct tcp_hdr *) data; probes.IP.pd.tcp.sport = ntohs(tcp->th_sport); probes.IP.pd.tcp.seq = ntohl(tcp->th_seq); - } else if (ipv4->ip_p == IPPROTO_UDP) { - assert(iplen >= (unsigned) ipv4->ip_hl * 4 + 8); - udp = (struct udp_hdr *) ((u8 *) ipv4 + ipv4->ip_hl * 4); + } else if (hdr == IPPROTO_UDP) { + assert(len >= 8); + udp = (struct udp_hdr *) data; probes.IP.pd.udp.sport = ntohs(udp->uh_sport); - } else if (ipv4->ip_p == IPPROTO_SCTP) { - assert(iplen >= (unsigned) ipv4->ip_hl * 4 + 12); - sctp = (struct sctp_hdr *) ((u8 *) ipv4 + ipv4->ip_hl * 4); + } else if (hdr == IPPROTO_SCTP) { + assert(len >= 12); + sctp = (struct sctp_hdr *) data; probes.IP.pd.sctp.sport = ntohs(sctp->sh_sport); probes.IP.pd.sctp.vtag = ntohl(sctp->sh_vtag); } @@ -1562,7 +1575,7 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp if (o.pingtype & PINGTYPE_SCTP_INIT) ptech.rawsctpscan = 1; if (o.pingtype & PINGTYPE_TCP) { - if (o.isr00t && o.af() == AF_INET) + if (o.isr00t) ptech.rawtcpscan = 1; else ptech.connecttcpscan = 1; @@ -1621,7 +1634,7 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp } else { /* Initialize a raw socket */ #ifdef WIN32 - win32_warn_raw_sockets(Targets[0]->deviceName()); + win32_fatal_raw_sockets(Targets[0]->deviceName()); #endif if ((rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 ) pfatal("socket troubles in %s", __func__); @@ -1753,20 +1766,22 @@ bool UltraScanInfo::sendOK(struct timeval *when) { Returns NULL if none are found. */ HostScanStats *UltraScanInfo::findHost(struct sockaddr_storage *ss) { list::iterator hss; - struct sockaddr_in *sin = (struct sockaddr_in *) ss; - - if (sin->sin_family != AF_INET) - fatal("%s passed a non IPv4 address", __func__); + struct sockaddr_storage target_addr; + size_t target_addr_len; for(hss = incompleteHosts.begin(); hss != incompleteHosts.end(); hss++) { - if ((*hss)->target->v4hostip()->s_addr == sin->sin_addr.s_addr) { + target_addr_len = sizeof(target_addr); + (*hss)->target->TargetSockAddr(&target_addr, &target_addr_len); + if (sockaddr_storage_cmp(&target_addr, ss) == 0) { if (o.debugging > 2) log_write(LOG_STDOUT, "Found %s in incomplete hosts list.\n", (*hss)->target->targetipstr()); return *hss; } } for(hss = completedHosts.begin(); hss != completedHosts.end(); hss++) { - if ((*hss)->target->v4hostip()->s_addr == sin->sin_addr.s_addr) { + target_addr_len = sizeof(target_addr); + (*hss)->target->TargetSockAddr(&target_addr, &target_addr_len); + if (sockaddr_storage_cmp(&target_addr, ss) == 0) { if (o.debugging > 2) log_write(LOG_STDOUT, "Found %s in completed hosts list.\n", (*hss)->target->targetipstr()); return *hss; @@ -2013,6 +2028,16 @@ static int get_next_target_probe(UltraScanInfo *USI, HostScanStats *hss, -PA is slightly better than -PS when combined with -PE, but give -PS an edge because it is less likely to be dropped by firewalls. */ if (USI->ptech.rawicmpscan) { + if (hss->target->af() == AF_INET6){ + pspec->type = PS_ICMPV6; + pspec->proto = IPPROTO_ICMPV6; + if ((o.pingtype & PINGTYPE_ICMP_PING) && !hss->sent_icmp_ping) { + hss->sent_icmp_ping = true; + pspec->pd.icmp.type = ICMPV6_ECHO; + pspec->pd.icmp.code = 0; + return 0; + } + } pspec->type = PS_ICMP; pspec->proto = IPPROTO_ICMP; if ((o.pingtype & PINGTYPE_ICMP_PING) && !hss->sent_icmp_ping) { @@ -2421,24 +2446,24 @@ static bool sport_decode(const UltraScanInfo *USI, u16 magic_portno, u16 portno, } static bool tcp_probe_match(const UltraScanInfo *USI, const UltraProbe *probe, - const HostScanStats *hss, const struct ip *ip) { - const struct tcp_hdr *tcp; + const HostScanStats *hss, const struct tcp_hdr *tcp, + const struct sockaddr_storage *src, const struct sockaddr_storage *dst, + u32 ipid) { const struct probespec_tcpdata *probedata; + struct sockaddr_storage srcaddr; + size_t srcaddr_len; unsigned int tryno, pingseq; bool goodseq; - if (ip->ip_p != IPPROTO_TCP) - return false; - - tcp = (struct tcp_hdr *) ((u8 *) ip + ip->ip_hl * 4); - - if (o.af() != AF_INET || probe->protocol() != IPPROTO_TCP) + if (probe->protocol() != IPPROTO_TCP) return false; + srcaddr_len = sizeof(srcaddr); + hss->target->SourceSockAddr(&srcaddr, &srcaddr_len); /* Ensure the connection info matches. */ if (probe->dport() != ntohs(tcp->th_sport) || probe->sport() != ntohs(tcp->th_dport) - || hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr) + || sockaddr_storage_cmp(&srcaddr, dst) != 0) return false; tryno = 0; @@ -2470,7 +2495,7 @@ static bool tcp_probe_match(const UltraScanInfo *USI, const UltraProbe *probe, used in those cases where it happens. Then I should make this just a debugging > X statement. */ if (o.debugging) - log_write(LOG_PLAIN, "Bad Sequence number from host %s.\n", inet_ntoa(ip->ip_src)); + log_write(LOG_PLAIN, "Bad Sequence number from host %s.\n", inet_ntop_ez(src, sizeof(*src))); /* I'll just assume it is a response to this (most recent) probe. */ tryno = probe->tryno; pingseq = probe->pingseq; @@ -2503,8 +2528,8 @@ static bool tcp_probe_match(const UltraScanInfo *USI, const UltraProbe *probe, scan localhost with src port = dst port and see our outgoing packet and think it is a response. */ if (probe->dport() == probe->sport() - && ip->ip_src.s_addr == ip->ip_dst.s_addr - && probe->ipid() == ntohs(ip->ip_id)) + && sockaddr_storage_cmp(src, dst) == 0 + && probe->ipid() == ipid) return false; return true; @@ -3196,19 +3221,40 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, tcpopslen = 4; } - for(decoy = 0; decoy < o.numdecoys; decoy++) { - packet = build_tcp_raw(&o.decoys[decoy], hss->target->v4hostip(), - o.ttl, ipid, IP_TOS_DEFAULT, false, - o.ipoptions, o.ipoptionslen, - sport, pspec->pd.tcp.dport, - seq, ack, 0, pspec->pd.tcp.flags, 0, 0, - tcpops, tcpopslen, - o.extra_payload, o.extra_payload_length, - &packetlen); - if (decoy == o.decoyturn) { - probe->setIP(packet, packetlen, pspec); - probe->sent = USI->now; + if (hss->target->af() == AF_INET) { + for(decoy = 0; decoy < o.numdecoys; decoy++) { + packet = build_tcp_raw(&o.decoys[decoy], hss->target->v4hostip(), + o.ttl, ipid, IP_TOS_DEFAULT, false, + o.ipoptions, o.ipoptionslen, + sport, pspec->pd.tcp.dport, + seq, ack, 0, pspec->pd.tcp.flags, 0, 0, + tcpops, tcpopslen, + o.extra_payload, o.extra_payload_length, + &packetlen); + if (decoy == o.decoyturn) { + probe->setIP(packet, packetlen, pspec); + probe->sent = USI->now; + } + hss->probeSent(packetlen); + send_ip_packet(USI->rawsd, ethptr, packet, packetlen); + free(packet); } + } else if (hss->target->af() == AF_INET6) { + struct sockaddr_storage source; + struct sockaddr_in6 *sin6; + size_t source_len; + + source_len = sizeof(source); + hss->target->SourceSockAddr(&source, &source_len); + sin6 = (struct sockaddr_in6 *) &source; + packet = build_tcp_raw_ipv6(&sin6->sin6_addr, hss->target->v6hostip(), + 0, 0, o.ttl, sport, pspec->pd.tcp.dport, + seq, ack, 0, pspec->pd.tcp.flags, 0, 0, + tcpops, tcpopslen, + o.extra_payload, o.extra_payload_length, + &packetlen); + probe->setIP(packet, packetlen, pspec); + probe->sent = USI->now; hss->probeSent(packetlen); send_ip_packet(USI->rawsd, ethptr, packet, packetlen); free(packet); @@ -3219,17 +3265,36 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, payload = get_udp_payload(pspec->pd.udp.dport, &payload_length); - for(decoy = 0; decoy < o.numdecoys; decoy++) { - packet = build_udp_raw(&o.decoys[decoy], hss->target->v4hostip(), - o.ttl, ipid, IP_TOS_DEFAULT, false, - o.ipoptions, o.ipoptionslen, - sport, pspec->pd.udp.dport, - (char *) payload, payload_length, - &packetlen); - if (decoy == o.decoyturn) { - probe->setIP(packet, packetlen, pspec); - probe->sent = USI->now; + if (hss->target->af() == AF_INET) { + for(decoy = 0; decoy < o.numdecoys; decoy++) { + packet = build_udp_raw(&o.decoys[decoy], hss->target->v4hostip(), + o.ttl, ipid, IP_TOS_DEFAULT, false, + o.ipoptions, o.ipoptionslen, + sport, pspec->pd.udp.dport, + (char *) payload, payload_length, + &packetlen); + if (decoy == o.decoyturn) { + probe->setIP(packet, packetlen, pspec); + probe->sent = USI->now; + } + hss->probeSent(packetlen); + send_ip_packet(USI->rawsd, ethptr, packet, packetlen); + free(packet); } + } else if (hss->target->af() == AF_INET6) { + struct sockaddr_storage source; + struct sockaddr_in6 *sin6; + size_t source_len; + + source_len = sizeof(source); + hss->target->SourceSockAddr(&source, &source_len); + sin6 = (struct sockaddr_in6 *) &source; + packet = build_udp_raw_ipv6(&sin6->sin6_addr, hss->target->v6hostip(), + 0, 0, o.ttl, sport, pspec->pd.tcp.dport, + (char *) payload, payload_length, + &packetlen); + probe->setIP(packet, packetlen, pspec); + probe->sent = USI->now; hss->probeSent(packetlen); send_ip_packet(USI->rawsd, ethptr, packet, packetlen); free(packet); @@ -3256,18 +3321,38 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, default: assert(0); } - for(decoy = 0; decoy < o.numdecoys; decoy++) { - packet = build_sctp_raw(&o.decoys[decoy], hss->target->v4hostip(), - o.ttl, ipid, IP_TOS_DEFAULT, false, - o.ipoptions, o.ipoptionslen, - sport, pspec->pd.sctp.dport, - vtag, chunk, chunklen, - o.extra_payload, o.extra_payload_length, - &packetlen); - if (decoy == o.decoyturn) { - probe->setIP(packet, packetlen, pspec); - probe->sent = USI->now; + if (hss->target->af() == AF_INET) { + for(decoy = 0; decoy < o.numdecoys; decoy++) { + packet = build_sctp_raw(&o.decoys[decoy], hss->target->v4hostip(), + o.ttl, ipid, IP_TOS_DEFAULT, false, + o.ipoptions, o.ipoptionslen, + sport, pspec->pd.sctp.dport, + vtag, chunk, chunklen, + o.extra_payload, o.extra_payload_length, + &packetlen); + if (decoy == o.decoyturn) { + probe->setIP(packet, packetlen, pspec); + probe->sent = USI->now; + } + hss->probeSent(packetlen); + send_ip_packet(USI->rawsd, ethptr, packet, packetlen); + free(packet); } + } else if (hss->target->af() == AF_INET6) { + struct sockaddr_storage source; + struct sockaddr_in6 *sin6; + size_t source_len; + + source_len = sizeof(source); + hss->target->SourceSockAddr(&source, &source_len); + sin6 = (struct sockaddr_in6 *) &source; + packet = build_sctp_raw_ipv6(&sin6->sin6_addr, hss->target->v6hostip(), + 0, 0, o.ttl, sport, pspec->pd.sctp.dport, + vtag, chunk, chunklen, + o.extra_payload, o.extra_payload_length, + &packetlen); + probe->setIP(packet, packetlen, pspec); + probe->sent = USI->now; hss->probeSent(packetlen); send_ip_packet(USI->rawsd, ethptr, packet, packetlen); free(packet); @@ -3363,7 +3448,26 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, send_ip_packet(USI->rawsd, ethptr, packet, packetlen); free(packet); } - } else assert(0); /* TODO: Maybe RPC scan and the like */ + } else if (pspec->type == PS_ICMPV6) { + struct sockaddr_storage source; + struct sockaddr_in6 *sin6; + size_t source_len; + + source_len = sizeof(source); + hss->target->SourceSockAddr(&source, &source_len); + sin6 = (struct sockaddr_in6 *) &source; + packet = build_icmpv6_raw(&sin6->sin6_addr, hss->target->v6hostip(), + 0, 0, o.ttl, 0, icmp_ident, pspec->pd.icmpv6.type, + pspec->pd.icmpv6.code, o.extra_payload, + o.extra_payload_length, + &packetlen); + probe->setIP(packet, packetlen, pspec); + probe->sent = USI->now; + hss->probeSent(packetlen); + send_ip_packet(USI->rawsd, ethptr, packet, packetlen); + free(packet); + }else assert(0); /* TODO: Maybe RPC scan and the like */ + /* Now that the probe has been sent, add it to the Queue for this host */ hss->probes_outstanding.push_back(probe); USI->gstats->num_probes_active++; @@ -3388,7 +3492,7 @@ static void sendNextScanProbe(UltraScanInfo *USI, HostScanStats *hss) { sendConnectScanProbe(USI, hss, pspec.pd.tcp.dport, 0, 0); else if (pspec.type == PS_TCP || pspec.type == PS_UDP || pspec.type == PS_SCTP || pspec.type == PS_PROTO - || pspec.type == PS_ICMP) + || pspec.type == PS_ICMP || pspec.type == PS_ICMPV6) sendIPScanProbe(USI, hss, &pspec, 0, 0); else assert(0); @@ -3553,7 +3657,7 @@ static void retransmitProbe(UltraScanInfo *USI, HostScanStats *hss, } else if (probe->protocol() == IPPROTO_SCTP) { newProbe = sendIPScanProbe(USI, hss, probe->pspec(), probe->tryno + 1, 0); - } else if (probe->protocol() == IPPROTO_ICMP) { + } else if (probe->protocol() == IPPROTO_ICMP || probe->protocol() == IPPROTO_ICMPV6) { newProbe = sendIPScanProbe(USI, hss, probe->pspec(), probe->tryno + 1, 0); } else { @@ -3563,7 +3667,10 @@ static void retransmitProbe(UltraScanInfo *USI, HostScanStats *hss, newProbe = sendConnectScanProbe(USI, hss, probe->pspec()->pd.tcp.dport, probe->tryno + 1, 0); } else if (probe->type == UltraProbe::UP_ARP) { newProbe = sendArpScanProbe(USI, hss, probe->tryno + 1, 0); - } else assert(0); /* TODO: Support any other probe types */ + } else { + /* TODO: Support any other probe types */ + fatal("%s: unsupported probe type %d", __func__, probe->type); + } if (newProbe) newProbe->prevSent = probe->sent; probe->retransmitted = true; @@ -3915,7 +4022,7 @@ static bool do_one_select_round(UltraScanInfo *USI, struct timeval *stime) { u16 dport = probe->dport(); ultrascan_port_probe_update(USI, host, probeI, newportstate, &USI->now, adjust_timing); - host->target->ports.setStateReason(dport, protocol, current_reason, 0, 0); + host->target->ports.setStateReason(dport, protocol, current_reason, 0, NULL); } } } @@ -3995,7 +4102,6 @@ static bool get_arp_result(UltraScanInfo *USI, struct timeval *stime) { - /* Tries to get one *good* (finishes a probe) pcap response by the (absolute) time given in stime. Even if stime is now, try an ultra-quick pcap read just in case. Returns true if a "good" result @@ -4005,37 +4111,40 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { bool timedout = false; bool adjust_timing = true; struct timeval rcvdtime; - struct ip *ip = NULL; struct ip *ip_icmp = NULL; - struct icmp *icmp = NULL; struct link_header linkhdr; unsigned int bytes; long to_usec; HostScanStats *hss = NULL; - struct sockaddr_in sin; list::iterator probeI; UltraProbe *probe = NULL; int newstate = PORT_UNKNOWN; unsigned int probenum; unsigned int listsz; - unsigned int requiredbytes; /* Static so that we can detect an ICMP response now, then add it later when the icmp probe is made */ static bool protoscanicmphack = false; - static struct sockaddr_in protoscanicmphackaddy; + static struct sockaddr_storage protoscanicmphackaddy; reason_t current_reason = ER_NORESPONSE; - u32 reason_sip = 0; + struct sockaddr_storage reason_sip = { AF_UNSPEC }; + + const void *data = NULL; + unsigned int datalen; + struct abstract_ip_hdr hdr; + gettimeofday(&USI->now, NULL); do { + struct ip *ip_tmp; + to_usec = TIMEVAL_SUBTRACT(*stime, USI->now); if (to_usec < 2000) to_usec = 2000; - ip = (struct ip *) readip_pcap(USI->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); + ip_tmp = (struct ip *) readip46_pcap(USI->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); gettimeofday(&USI->now, NULL); - if (!ip && TIMEVAL_SUBTRACT(*stime, USI->now) < 0) { + if (!ip_tmp && TIMEVAL_SUBTRACT(*stime, USI->now) < 0) { timedout = true; break; - } else if (!ip) + } else if (!ip_tmp) continue; if (TIMEVAL_SUBTRACT(USI->now, *stime) > 200000) { @@ -4044,23 +4153,22 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { timedout = true; } - /* OK, we got a packet. Most packet validity tests are taken care - * of in readip_pcap, so this is simple - */ - if (bytes < 28) + struct sockaddr_storage target_src, target_dst; + size_t ss_len; + + datalen = bytes; + data = ip_get_data(ip_tmp, &bytes, &hdr); + if (data == NULL) continue; if (USI->prot_scan) { - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&hdr.src); if (hss) { - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); - if (ip->ip_p == IPPROTO_ICMP) { + setTargetMACIfAvailable(hss->target, &linkhdr, &hdr.src, 0); + if (hdr.proto == IPPROTO_ICMP) { protoscanicmphack = true; - protoscanicmphackaddy = sin; - ip_icmp = ip; + protoscanicmphackaddy = hdr.src; + ip_icmp = ip_tmp; } else { probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); @@ -4069,10 +4177,10 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { probeI--; probe = *probeI; - if (probe->protocol() == ip->ip_p) { + if (probe->protocol() == hdr.proto) { /* if this is our probe we sent to localhost, then it doesn't count! */ - if (ip->ip_src.s_addr == ip->ip_dst.s_addr && - probe->ipid() == ntohs(ip->ip_id)) + if (sockaddr_storage_cmp(&hdr.src, &hdr.dst) == 0 && + probe->ipid() == hdr.ipid) continue; /* We got a packet from the dst host in the protocol we looked for, and @@ -4086,15 +4194,12 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { } } - if (ip->ip_p == IPPROTO_TCP && !USI->prot_scan) { - struct tcp_hdr *tcp = (struct tcp_hdr *) ((u8 *) ip + ip->ip_hl * 4); + if (hdr.proto == IPPROTO_TCP && !USI->prot_scan) { + struct tcp_hdr *tcp = (struct tcp_hdr *) data; /* Now ensure this host is even in the incomplete list */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&hdr.src); if (!hss) continue; // Not from a host that interests us - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hss->target, &linkhdr, &hdr.src, 0); probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); @@ -4105,7 +4210,7 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { probeI--; probe = *probeI; - if (!tcp_probe_match(USI, probe, hss, ip)) + if (!tcp_probe_match(USI, probe, hss, tcp, &hdr.src, &hdr.dst, hdr.ipid)) continue; if (!probe->isPing()) { @@ -4134,41 +4239,41 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { goodone = true; } - } else if (ip->ip_p == IPPROTO_SCTP && !USI->prot_scan) { - struct sctp_hdr *sctp = (struct sctp_hdr *) ((u8 *) ip + ip->ip_hl * 4); + } else if (hdr.proto == IPPROTO_SCTP && !USI->prot_scan) { + struct sctp_hdr *sctp = (struct sctp_hdr *) data; struct dnet_sctp_chunkhdr *chunk = (struct dnet_sctp_chunkhdr *) ((u8 *) sctp + 12); + /* Now ensure this host is even in the incomplete list */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&hdr.src); if (!hss) continue; // Not from a host that interests us - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hss->target, &linkhdr, &hdr.src, 0); probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); goodone = false; - + + ss_len = sizeof(target_src); + hss->target->SourceSockAddr(&target_src, &ss_len); + /* Find the probe that provoked this response. */ for (probenum = 0; probenum < listsz && !goodone; probenum++) { probeI--; probe = *probeI; - if (o.af() != AF_INET || probe->protocol() != IPPROTO_SCTP) + if (probe->protocol() != IPPROTO_SCTP) continue; - /* Ensure the connection info matches. */ if (probe->dport() != ntohs(sctp->sh_sport) || probe->sport() != ntohs(sctp->sh_dport) - || hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr) + || sockaddr_storage_cmp(&target_src, &hdr.dst) != 0) continue; /* Sometimes we get false results when scanning localhost with -p- because we scan localhost with src port = dst port and see our outgoing packet and think it is a response. */ if (probe->dport() == probe->sport() && - ip->ip_src.s_addr == ip->ip_dst.s_addr && - probe->ipid() == ntohs(ip->ip_id)) + sockaddr_storage_cmp(&hdr.src, &hdr.dst) == 0 && + probe->ipid() == hdr.ipid) continue; /* We saw the packet we ourselves sent */ if (!probe->isPing()) { @@ -4201,73 +4306,79 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { goodone = true; } - } else if (ip->ip_p == IPPROTO_ICMP) { - icmp = (struct icmp *) ((char *)ip + 4 * ip->ip_hl); + } else if (hdr.proto == IPPROTO_ICMP) { + const void *encaps_data; + unsigned int encaps_len; + struct abstract_ip_hdr encaps_hdr; + struct icmp *icmp = NULL; + icmp = (struct icmp *) data; + + if (datalen < 8) + continue; if (icmp->icmp_type != 3) continue; - struct ip *ip2 = (struct ip *) (((char *) ip) + 4 * ip->ip_hl + 8); - requiredbytes = /* IPlen*/ 4 * ip->ip_hl + - /* ICMPLen */ 8 + - /* IP2 Len */ 4 * ip2->ip_hl; - if (USI->tcp_scan || USI->udp_scan || USI->sctp_scan) { + encaps_len = datalen - 8; + encaps_data = ip_get_data((char *) data + 8, &encaps_len, &encaps_hdr); + if (encaps_data == NULL || /* UDP hdr, or TCP hdr up to seq #, or SCTP hdr up to vtag */ - requiredbytes += 8; - } - /* prot scan has no headers coming back, so we don't reserve the - 8 xtra bytes */ - if (bytes < requiredbytes) { + ((USI->tcp_scan || USI->udp_scan || USI->sctp_scan) && encaps_len < 8) + /* prot scan has no headers coming back, so we don't reserve the + 8 xtra bytes */ + ) { if (o.debugging) - error("Received short ICMP packet (%d bytes)", bytes); + error("Received short ICMP packet (%u bytes)", datalen); continue; } - + /* Make sure the protocol is right */ - if (USI->tcp_scan && ip2->ip_p != IPPROTO_TCP) + if (USI->tcp_scan && encaps_hdr.proto != IPPROTO_TCP) continue; - if (USI->udp_scan && ip2->ip_p != IPPROTO_UDP) + if (USI->udp_scan && encaps_hdr.proto != IPPROTO_UDP) continue; - if (USI->sctp_scan && ip2->ip_p != IPPROTO_SCTP) + if (USI->sctp_scan && encaps_hdr.proto != IPPROTO_SCTP) continue; /* ensure this packet relates to a packet to the host we are scanning ... */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip2->ip_dst.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&encaps_hdr.dst); if (!hss) continue; // Not from a host that interests us probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); + + ss_len = sizeof(target_src); + hss->target->SourceSockAddr(&target_src, &ss_len); + ss_len = sizeof(target_dst); + hss->target->TargetSockAddr(&target_dst, &ss_len); + goodone = false; /* Find the matching probe */ for(probenum = 0; probenum < listsz && !goodone; probenum++) { probeI--; probe = *probeI; - assert(o.af() == AF_INET); - if (probe->protocol() != ip2->ip_p || - hss->target->v4sourceip()->s_addr != ip2->ip_src.s_addr || - hss->target->v4hostip()->s_addr != ip2->ip_dst.s_addr) + if (probe->protocol() != encaps_hdr.proto || + sockaddr_storage_cmp(&target_src, &encaps_hdr.src) != 0 || + sockaddr_storage_cmp(&target_dst, &encaps_hdr.dst) != 0) continue; - if (ip2->ip_p == IPPROTO_TCP && !USI->prot_scan) { - struct tcp_hdr *tcp = (struct tcp_hdr *) ((u8 *) ip2 + ip2->ip_hl * 4); + if (encaps_hdr.proto == IPPROTO_TCP && !USI->prot_scan) { + struct tcp_hdr *tcp = (struct tcp_hdr *) encaps_data; if (ntohs(tcp->th_sport) != probe->sport() || ntohs(tcp->th_dport) != probe->dport() || ntohl(tcp->th_seq) != probe->tcpseq()) continue; - } else if (ip2->ip_p == IPPROTO_SCTP && !USI->prot_scan) { - struct sctp_hdr *sctp = (struct sctp_hdr *) ((u8 *) ip2 + ip2->ip_hl * 4); + } else if (encaps_hdr.proto == IPPROTO_SCTP && !USI->prot_scan) { + struct sctp_hdr *sctp = (struct sctp_hdr *) encaps_data; if (ntohs(sctp->sh_sport) != probe->sport() || ntohs(sctp->sh_dport) != probe->dport() || ntohl(sctp->sh_vtag) != probe->sctpvtag()) continue; - } else if (ip2->ip_p == IPPROTO_UDP && !USI->prot_scan) { + } else if (encaps_hdr.proto == IPPROTO_UDP && !USI->prot_scan) { /* TODO: IPID verification */ - struct udp_hdr *udp = (struct udp_hdr *) ((u8 *) ip2 + ip2->ip_hl * 4); + struct udp_hdr *udp = (struct udp_hdr *) encaps_data; if (ntohs(udp->uh_sport) != probe->sport() || ntohs(udp->uh_dport) != probe->dport()) continue; @@ -4285,17 +4396,17 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { break; case 2: /* protocol unreachable */ if (USI->scantype == IPPROT_SCAN && - hss->target->v4hostip()->s_addr == ip->ip_src.s_addr) { + sockaddr_storage_cmp(&target_dst, &hdr.src) == 0) { newstate = PORT_CLOSED; } else newstate = PORT_FILTERED; break; case 3: /* Port unreach */ if (USI->scantype == UDP_SCAN && - hss->target->v4hostip()->s_addr == ip->ip_src.s_addr) + sockaddr_storage_cmp(&target_dst, &hdr.src) == 0) newstate = PORT_CLOSED; else if (USI->scantype == IPPROT_SCAN && - hss->target->v4hostip()->s_addr == ip->ip_src.s_addr) + sockaddr_storage_cmp(&target_dst, &hdr.src) == 0) newstate = PORT_OPEN; else newstate = PORT_FILTERED; @@ -4309,25 +4420,149 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { default: error("Unexpected ICMP type/code 3/%d unreachable packet:\n", icmp->icmp_code); - nmap_hexdump((unsigned char *)icmp, ntohs(ip->ip_len) - - sizeof(struct ip)); + nmap_hexdump((unsigned char *)icmp, datalen); break; } - current_reason = icmp_to_reason(icmp->icmp_type, icmp->icmp_code); + current_reason = icmp_to_reason(hdr.proto, icmp->icmp_type, icmp->icmp_code); if (newstate == PORT_UNKNOWN) break; goodone = true; } } - } else if (ip->ip_p == IPPROTO_UDP && !USI->prot_scan) { - struct udp_hdr *udp = (struct udp_hdr *) ((u8 *) ip + ip->ip_hl * 4); - /* Search for this host on the incomplete list */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + } else if (hdr.proto == IPPROTO_ICMPV6) { + const void *encaps_data; + unsigned int encaps_len; + struct abstract_ip_hdr encaps_hdr; + const struct icmpv6_hdr *icmpv6; + + icmpv6 = (struct icmpv6_hdr *) data; + + if (datalen < 8) + continue; + if (icmpv6->icmpv6_type != ICMPV6_UNREACH) + continue; + + encaps_len = datalen - 8; + encaps_data = ip_get_data((char *) data + 8, &encaps_len, &encaps_hdr); + if (encaps_data == NULL || + /* UDP hdr, or TCP hdr up to seq #, or SCTP hdr up to vtag */ + ((USI->tcp_scan || USI->udp_scan || USI->sctp_scan) && encaps_len < 8) + /* prot scan has no headers coming back, so we don't reserve the + 8 xtra bytes */ + ) { + if (o.debugging) + error("Received short ICMPv6 packet (%u bytes)", datalen); + continue; + } + + /* Make sure the protocol is right */ + if (USI->tcp_scan && encaps_hdr.proto != IPPROTO_TCP) + continue; + + if (USI->udp_scan && encaps_hdr.proto != IPPROTO_UDP) + continue; + + if (USI->sctp_scan && encaps_hdr.proto != IPPROTO_SCTP) + continue; + + /* ensure this packet relates to a packet to the host + we are scanning ... */ + hss = USI->findHost(&encaps_hdr.dst); if (!hss) continue; // Not from a host that interests us probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); + + ss_len = sizeof(target_src); + hss->target->SourceSockAddr(&target_src, &ss_len); + ss_len = sizeof(target_dst); + hss->target->TargetSockAddr(&target_dst, &ss_len); + + goodone = false; + /* Find the matching probe */ + for(probenum = 0; probenum < listsz && !goodone; probenum++) { + probeI--; + probe = *probeI; + if (probe->protocol() != encaps_hdr.proto || + sockaddr_storage_cmp(&target_src, &encaps_hdr.src) != 0 || + sockaddr_storage_cmp(&target_dst, &encaps_hdr.dst) != 0) + continue; + + if (encaps_hdr.proto == IPPROTO_TCP && !USI->prot_scan) { + struct tcp_hdr *tcp = (struct tcp_hdr *) encaps_data; + if (ntohs(tcp->th_sport) != probe->sport() || + ntohs(tcp->th_dport) != probe->dport() || + ntohl(tcp->th_seq) != probe->tcpseq()) + continue; + } else if (encaps_hdr.proto == IPPROTO_SCTP && !USI->prot_scan) { + struct sctp_hdr *sctp = (struct sctp_hdr *) encaps_data; + if (ntohs(sctp->sh_sport) != probe->sport() || + ntohs(sctp->sh_dport) != probe->dport() || + ntohl(sctp->sh_vtag) != probe->sctpvtag()) + continue; + } else if (encaps_hdr.proto == IPPROTO_UDP && !USI->prot_scan) { + /* TODO: IPID verification */ + struct udp_hdr *udp = (struct udp_hdr *) encaps_data; + if (ntohs(udp->uh_sport) != probe->sport() || + ntohs(udp->uh_dport) != probe->dport()) + continue; + } else if (!USI->prot_scan) { + assert(0); + } + + if (icmpv6->icmpv6_type == ICMPV6_UNREACH) { + switch(icmpv6->icmpv6_code) { + case ICMPV6_UNREACH_NOROUTE: + current_reason = ER_NOROUTE; + newstate = PORT_FILTERED; + break; + case ICMPV6_UNREACH_PROHIB: + current_reason = ER_ADMINPROHIBITED; + newstate = PORT_FILTERED; + break; + case ICMPV6_UNREACH_SCOPE: + current_reason = ER_BEYONDSCOPE; + newstate = PORT_FILTERED; + break; + case ICMPV6_UNREACH_ADDR: + current_reason = ER_HOSTUNREACH; + newstate = PORT_FILTERED; + break; + case ICMPV6_UNREACH_FILTER_PROHIB: + current_reason = ER_ADMINPROHIBITED; + newstate = PORT_FILTERED; + break; + case ICMPV6_UNREACH_REJECT_ROUTE: + current_reason = ER_REJECTROUTE; + newstate = PORT_FILTERED; + break; + case ICMPV6_UNREACH_PORT: + current_reason = ER_PORTUNREACH; + if (USI->scantype == UDP_SCAN && + sockaddr_storage_cmp(&target_dst, &hdr.src) == 0) + newstate = PORT_CLOSED; + else + newstate = PORT_FILTERED; + break; + default: + error("Unexpected ICMPv6 type/code %d/%d unreachable packet:\n", + icmpv6->icmpv6_type, icmpv6->icmpv6_code); + nmap_hexdump((unsigned char *)icmpv6, datalen); + break; + } + if (newstate == PORT_UNKNOWN) break; + goodone = true; + } + } + } else if (hdr.proto == IPPROTO_UDP && !USI->prot_scan) { + struct udp_hdr *udp = (struct udp_hdr *) data; + + /* Search for this host on the incomplete list */ + hss = USI->findHost(&hdr.src); + if (!hss) continue; // Not from a host that interests us + probeI = hss->probes_outstanding.end(); + listsz = hss->num_probes_outstanding(); + ss_len = sizeof(target_src); + hss->target->SourceSockAddr(&target_src, &ss_len); + goodone = false; for(probenum = 0; probenum < listsz && !goodone; probenum++) { @@ -4335,21 +4570,21 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { probe = *probeI; newstate = PORT_UNKNOWN; - if (o.af() != AF_INET || probe->protocol() != IPPROTO_UDP) + if (probe->protocol() != IPPROTO_UDP) continue; /* Ensure the connection info matches. */ if (probe->dport() != ntohs(udp->uh_sport) || probe->sport() != ntohs(udp->uh_dport) || - hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr) + sockaddr_storage_cmp(&target_src, &hdr.dst) != 0) continue; /* Sometimes we get false results when scanning localhost with -p- because we scan localhost with src port = dst port and see our outgoing packet and think it is a response. */ if (probe->dport() == probe->sport() && - ip->ip_src.s_addr == ip->ip_dst.s_addr && - probe->ipid() == ntohs(ip->ip_id)) + sockaddr_storage_cmp(&hdr.src, &hdr.dst) == 0 && + probe->ipid() == hdr.ipid) continue; /* We saw the packet we ourselves sent */ newstate = PORT_OPEN; @@ -4360,7 +4595,16 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { } while (!goodone && !timedout); if (goodone) { - reason_sip = (ip->ip_src.s_addr == hss->target->v4hostip()->s_addr) ? 0 : ip->ip_src.s_addr; + struct sockaddr_storage target_dst; + size_t ss_len; + + ss_len = sizeof(target_dst); + hss->target->TargetSockAddr(&target_dst, &ss_len); + + if (sockaddr_storage_cmp(&hdr.src, &target_dst) == 0) + reason_sip.ss_family = AF_UNSPEC; + else + reason_sip = hdr.src; if (probe->isPing()) ultrascan_ping_update(USI, hss, probeI, &rcvdtime, adjust_timing); else { @@ -4372,10 +4616,10 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { ultrascan_port_probe_update(USI, hss, probeI, newstate, &rcvdtime, adjust_timing); if(USI->prot_scan) hss->target->ports.setStateReason(protocol, IPPROTO_IP, - current_reason, ip->ip_ttl, reason_sip); + current_reason, hdr.ttl, &reason_sip); else hss->target->ports.setStateReason(dport, protocol, - current_reason, ip->ip_ttl, reason_sip); + current_reason, hdr.ttl, &reason_sip); } } @@ -4398,15 +4642,18 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { if (probe->isPing()) ultrascan_ping_update(USI, hss, probeI, &rcvdtime, adjust_timing); else { + struct icmp *icmp = (struct icmp *) data; ultrascan_port_probe_update(USI, hss, probeI, PORT_OPEN, &rcvdtime, adjust_timing); - icmp = (struct icmp *) ((char *)ip_icmp + 4 * ip_icmp->ip_hl); - reason_sip = (ip_icmp->ip_src.s_addr == protoscanicmphackaddy.sin_addr.s_addr) ? 0 : ip_icmp->ip_src.s_addr; + if (sockaddr_storage_cmp(&hdr.src, &protoscanicmphackaddy) == 0) + reason_sip.ss_family = AF_UNSPEC; + else + reason_sip = hdr.src; if(!icmp->icmp_code && !icmp->icmp_type) - hss->target->ports.setStateReason(IPPROTO_ICMP, IPPROTO_IP, ER_ECHOREPLY, - ip_icmp->ip_ttl, reason_sip); + hss->target->ports.setStateReason(IPPROTO_ICMP, IPPROTO_IP, ER_ECHOREPLY, + hdr.ttl, &reason_sip); else - hss->target->ports.setStateReason(IPPROTO_ICMP, IPPROTO_IP, icmp_to_reason(icmp->icmp_type, icmp->icmp_code), - ip_icmp->ip_ttl, reason_sip); + hss->target->ports.setStateReason(IPPROTO_ICMP, IPPROTO_IP, icmp_to_reason(hdr.proto, icmp->icmp_type, icmp->icmp_code), + hdr.ttl, &reason_sip); } if (!goodone) goodone = true; break; @@ -4429,7 +4676,7 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { bool adjust_timing = true; struct timeval rcvdtime; struct link_header linkhdr; - struct ip *ip; + struct ip *ip_tmp; unsigned int bytes; struct ppkt { unsigned char type; @@ -4440,22 +4687,28 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { } *ping; long to_usec; HostScanStats *hss = NULL; - struct sockaddr_in sin; list::iterator probeI; UltraProbe *probe = NULL; unsigned int trynum = 0; - unsigned int requiredbytes; int newstate = HOST_UNKNOWN; unsigned int probenum; unsigned int listsz; reason_t current_reason = ER_NORESPONSE; + struct sockaddr_storage target_src, target_dst; + size_t ss_len; + + const void *data = NULL; + unsigned int datalen; + struct abstract_ip_hdr hdr; + do { to_usec = TIMEVAL_SUBTRACT(*stime, USI->now); if (to_usec < 2000) to_usec = 2000; - ip = (struct ip *) readip_pcap(USI->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); + ip_tmp = (struct ip *) readip46_pcap(USI->pd, &bytes, to_usec, &rcvdtime, + &linkhdr, true); gettimeofday(&USI->now, NULL); - if (!ip) { + if (!ip_tmp) { if (TIMEVAL_SUBTRACT(*stime, USI->now) < 0) { timedout = true; break; @@ -4473,32 +4726,38 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { /* OK, we got a packet. Most packet validity tests are taken care * of in readip_pcap, so this is simple */ - if (bytes == 0) + + datalen = bytes; + data = ip_get_data(ip_tmp, &bytes, &hdr); + if (data == NULL) continue; /* First check if it is ICMP, TCP, or UDP */ - if (ip->ip_p == IPPROTO_ICMP) { + if (hdr.proto == IPPROTO_ICMP || hdr.proto == IPPROTO_ICMPV6) { /* if it is our response */ - ping = (struct ppkt *) ((ip->ip_hl * 4) + (char *) ip); - if (bytes < ip->ip_hl * 4 + 8U) { - if (!ip->ip_off) - error("Supposed ping packet is only %d bytes long!", bytes); + ping = (struct ppkt *) data; + if (bytes < 8U) { + if (!ip_tmp->ip_off) + error("Supposed ping packet is only %d bytes long!", bytes); continue; } - current_reason = icmp_to_reason(ping->type, ping->code); + current_reason = icmp_to_reason(hdr.proto, ping->type, ping->code); /* Echo reply, Timestamp reply, or Address Mask Reply. RFCs 792 and 950. */ - if (USI->ptech.rawicmpscan && (ping->type == 0 || ping->type == 14 || ping->type == 18)) { - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + /* ICMPv6 Echo reply */ + if (USI->ptech.rawicmpscan && ((hdr.proto == IPPROTO_ICMP && (ping->type + == 0 || ping->type == 14 || ping->type == 18)) || (hdr.proto + == IPPROTO_ICMPV6 && ping->type == 129))) { + hss = USI->findHost(&hdr.src); if (!hss) continue; // Not from a host that interests us - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hss->target, &linkhdr, &hdr.src, 0); probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); + ss_len = sizeof(target_src); + hss->target->SourceSockAddr(&target_src, &ss_len); + /* A check for weird_responses is needed here. This is not currently possible because we don't have a good way to look up the original target of an ICMP probe based on the response. (massping encoded an @@ -4518,94 +4777,117 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { probeI--; probe = *probeI; - if (o.af() != AF_INET || probe->protocol() != IPPROTO_ICMP) + /* Check if it is ICMP or ICMPV6. */ + if (probe->protocol() != IPPROTO_ICMPV6 && probe->protocol() != IPPROTO_ICMP) continue; /* Ensure the connection info matches. */ - if (hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr) + if (sockaddr_storage_cmp(&target_src, &hdr.dst) != 0) continue; /* Don't match a timestamp request with an echo reply, for example. */ - if ((ping->type == 0 && probe->pspec()->pd.icmp.type != 8) || + if (hdr.proto == IPPROTO_ICMP && + ((ping->type == 0 && probe->pspec()->pd.icmp.type != 8) || (ping->type == 14 && probe->pspec()->pd.icmp.type != 13) || - (ping->type == 18 && probe->pspec()->pd.icmp.type != 17)) + (ping->type == 18 && probe->pspec()->pd.icmp.type != 17))) + continue; + if (hdr.proto == IPPROTO_ICMPV6 && + (ping->type == 129 && probe->pspec()->pd.icmpv6.type != 128)) continue; /* Sometimes we get false results when scanning localhost with -p- because we scan localhost with src port = dst port and see our outgoing packet and think it is a response. */ if (probe->dport() == probe->sport() && - ip->ip_src.s_addr == ip->ip_dst.s_addr && - probe->ipid() == ntohs(ip->ip_id)) + sockaddr_storage_cmp(&hdr.src, &hdr.dst) == 0 && + probe->ipid() == hdr.ipid) continue; /* We saw the packet we ourselves sent */ goodone = true; newstate = HOST_UP; if (o.debugging) - log_write(LOG_STDOUT, "We got a ping packet back from %s: id = %d seq = %d checksum = %d\n", inet_ntoa(ip->ip_src), ping->id, ping->seq, ping->checksum); + log_write(LOG_STDOUT, "We got a ping packet back from %s: id = %d seq = %d checksum = %d\n", inet_ntop_ez(&hdr.src, sizeof(hdr.src)), ping->id, ping->seq, ping->checksum); } } - // Destination unreachable, source quench, or time exceeded - else if (ping->type == 3 || ping->type == 4 || ping->type == 11) { - struct ip *ip2 = (struct ip *) ((char *) ip + ip->ip_hl * 4 + 8); + // For ICMP, the reply of TCP/UDP/ICMP packets can be Destination unreachable, source quench, or time exceeded + /* For ICMPv6, the reply of TCP/UDP/ICMPV6 packets can be Destination Unreachable, + * Packet Too Big, Time Exceeded and Parameter Problem.*/ + else if ((hdr.proto == IPPROTO_ICMP && (ping->type == 3 || ping->type + == 4 || ping->type == 11)) + || (hdr.proto == IPPROTO_ICMPV6 && (ping->type == 1 || ping->type == 2 + || ping->type == 3 || ping->type == 4))) { + const void *encaps_data; + unsigned int encaps_len; + struct abstract_ip_hdr encaps_hdr; - requiredbytes = ip->ip_hl * 4 + 8U + ip2->ip_hl * 4 + 8U; + if (datalen < 8) + continue; - /* IPProto Scan (generally) sends bare IP headers, so no extra payload */ - if (bytes < requiredbytes && !USI->ptech.rawprotoscan) { + encaps_len = datalen - 8; + encaps_data = ip_get_data((char *) data + 8, &encaps_len, &encaps_hdr); + if (encaps_data == NULL || + /* UDP hdr, or TCP hdr up to seq #, or SCTP hdr up to vtag */ + ((USI->tcp_scan || USI->udp_scan || USI->sctp_scan) && encaps_len < 8) + /* prot scan has no headers coming back, so we don't reserve the + 8 xtra bytes */ + ) { if (o.debugging) - error("ICMP (embedded) type %d code %d packet is only %d bytes", ping->type, ping->code, bytes); + error("Received short ICMP or ICMPv6 packet (%u bytes)", datalen); continue; } /* Bail out early if possible. */ if (!USI->ptech.rawprotoscan) { - if (ip2->ip_p == IPPROTO_ICMP && !USI->ptech.rawicmpscan) + if (encaps_hdr.proto == IPPROTO_ICMP && !USI->ptech.rawicmpscan) continue; - if (ip2->ip_p == IPPROTO_TCP && !USI->ptech.rawtcpscan) + if (encaps_hdr.proto == IPPROTO_ICMPV6 && !USI->ptech.rawicmpscan) continue; - if (ip2->ip_p == IPPROTO_UDP && !USI->ptech.rawudpscan) + if (encaps_hdr.proto == IPPROTO_TCP && !USI->ptech.rawtcpscan) continue; - if (ip2->ip_p == IPPROTO_SCTP && !USI->ptech.rawsctpscan) + if (encaps_hdr.proto == IPPROTO_UDP && !USI->ptech.rawudpscan) + continue; + if (encaps_hdr.proto == IPPROTO_SCTP && !USI->ptech.rawsctpscan) continue; } - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip2->ip_dst.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&encaps_hdr.dst); if (!hss) continue; // Not referring to a host that interests us - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hss->target, &linkhdr, &encaps_hdr.dst, 0); probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); + ss_len = sizeof(target_src); + hss->target->SourceSockAddr(&target_src, &ss_len); + ss_len = sizeof(target_dst); + hss->target->TargetSockAddr(&target_dst, &ss_len); + /* Find the probe that provoked this response. */ for (probenum = 0; probenum < listsz; probenum++) { probeI--; probe = *probeI; - assert(o.af() == AF_INET); - if (probe->protocol() != ip2->ip_p || - hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr || - hss->target->v4sourceip()->s_addr != ip2->ip_src.s_addr || - hss->target->v4hostip()->s_addr != ip2->ip_dst.s_addr) + if (probe->protocol() != encaps_hdr.proto || + sockaddr_storage_cmp(&target_src, &hdr.dst) != 0 || + sockaddr_storage_cmp(&target_src, &encaps_hdr.src) != 0 || + sockaddr_storage_cmp(&target_dst, &encaps_hdr.dst) != 0) continue; - if (ip2->ip_p == IPPROTO_ICMP && USI->ptech.rawicmpscan) { + if ((encaps_hdr.proto == IPPROTO_ICMP || encaps_hdr.proto == IPPROTO_ICMPV6) + && USI->ptech.rawicmpscan) { /* The response was based on a ping packet we sent */ - } else if (ip2->ip_p == IPPROTO_TCP && USI->ptech.rawtcpscan) { - struct tcp_hdr *tcp = (struct tcp_hdr *) (((char *) ip2) + 4 * ip2->ip_hl); + } else if (encaps_hdr.proto == IPPROTO_TCP && USI->ptech.rawtcpscan) { + struct tcp_hdr *tcp = (struct tcp_hdr *) encaps_data; if (probe->dport() != ntohs(tcp->th_dport) || probe->sport() != ntohs(tcp->th_sport) || probe->tcpseq() != ntohl(tcp->th_seq)) continue; - } else if (ip2->ip_p == IPPROTO_UDP && USI->ptech.rawudpscan) { - struct udp_hdr *udp = (struct udp_hdr *) ((u8 *) ip2 + ip2->ip_hl * 4); + } else if (encaps_hdr.proto == IPPROTO_UDP && USI->ptech.rawudpscan) { + struct udp_hdr *udp = (struct udp_hdr *) encaps_data; if (probe->dport() != ntohs(udp->uh_dport) || probe->sport() != ntohs(udp->uh_sport)) continue; - } else if (ip2->ip_p == IPPROTO_SCTP && USI->ptech.rawsctpscan) { - struct sctp_hdr *sctp = (struct sctp_hdr *) ((u8 *) ip2 + ip2->ip_hl * 4); + } else if (encaps_hdr.proto == IPPROTO_SCTP && USI->ptech.rawsctpscan) { + struct sctp_hdr *sctp = (struct sctp_hdr *) encaps_data; if (probe->dport() != ntohs(sctp->sh_dport) || probe->sport() != ntohs(sctp->sh_sport) || probe->sctpvtag() != ntohl(sctp->sh_vtag)) @@ -4624,10 +4906,11 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { if (probenum >= listsz) continue; - if (ping->type == 3) { + if ((hdr.proto == IPPROTO_ICMP && ping->type == 3) + || (hdr.proto == IPPROTO_ICMPV6 && ping->type == 1)) { /* Destination unreachable. */ - if (hss->target->v4hostip()->s_addr == ip->ip_src.s_addr) { - /* The ICMP error came directly from the target, so it's up. */ + if (sockaddr_storage_cmp(&target_dst, &hdr.src) == 0) { + /* The ICMP or ICMPv6 error came directly from the target, so it's up. */ goodone = true; newstate = HOST_UP; } else { @@ -4635,38 +4918,47 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { newstate = HOST_DOWN; } if (o.debugging) { - if (ping->code == 3) + if ((hdr.proto == IPPROTO_ICMP && ping->code == 3) + || (hdr.proto == IPPROTO_ICMPV6 && ping->code == 4)) log_write(LOG_STDOUT, "Got port unreachable for %s\n", hss->target->targetipstr()); else log_write(LOG_STDOUT, "Got destination unreachable for %s\n", hss->target->targetipstr()); } - } else if (ping->type == 11) { + } else if ((hdr.proto == IPPROTO_ICMP && ping->type == 11) + || (hdr.proto == IPPROTO_ICMPV6 && ping->type == 3)) { if (o.debugging) log_write(LOG_STDOUT, "Got Time Exceeded for %s\n", hss->target->targetipstr()); goodone = 1; newstate = HOST_DOWN; /* I don't want anything to do with timing this. */ adjust_timing = false; - } else if (ping->type == 4) { + } else if (hdr.proto == IPPROTO_ICMP && ping->type == 4) { if (o.debugging) log_write(LOG_STDOUT, "Got ICMP source quench\n"); usleep(50000); - } else if (o.debugging) { - log_write(LOG_STDOUT, "Got ICMP message type %d code %d\n", ping->type, ping->code); + } else if (hdr.proto == IPPROTO_ICMPV6 && ping->type == 4){ + if (o.debugging) + log_write(LOG_STDOUT, "Got ICMPv6 Parameter Problem\n"); + } else if (hdr.proto == IPPROTO_ICMP){ + if (o.debugging) { + log_write(LOG_STDOUT, "Got ICMP message type %d code %d\n", + ping->type, ping->code); + } + } else if(hdr.proto == IPPROTO_ICMPV6){ + if (o.debugging) + log_write(LOG_STDOUT, "Got ICMPv6 message type %d code %d\n", + ping->type, ping->code); } } - } else if (ip->ip_p == IPPROTO_TCP && USI->ptech.rawtcpscan) { - struct tcp_hdr *tcp = (struct tcp_hdr *) (((u8 *) ip) + 4 * ip->ip_hl); + } else if (hdr.proto == IPPROTO_TCP && USI->ptech.rawtcpscan) { + struct tcp_hdr *tcp = (struct tcp_hdr *) data; /* Check that the packet has useful flags. */ if (!(tcp->th_flags & TH_RST) && ((tcp->th_flags & (TH_SYN|TH_ACK)) != (TH_SYN|TH_ACK))) continue; /* Now ensure this host is even in the incomplete list */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&hdr.src); if (!hss) continue; // Not from a host that interests us - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hss->target, &linkhdr, &hdr.src, 0); probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); @@ -4677,7 +4969,7 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { probeI--; probe = *probeI; - if (!tcp_probe_match(USI, probe, hss, ip)) + if (!tcp_probe_match(USI, probe, hss, tcp, &hdr.src, &hdr.dst, hdr.ipid)) continue; goodone = true; @@ -4696,20 +4988,20 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { } if (o.debugging) - log_write(LOG_STDOUT, "We got a TCP ping packet back from %s port %hu (trynum = %d)\n", inet_ntoa(ip->ip_src), ntohs(tcp->th_sport), trynum); + log_write(LOG_STDOUT, "We got a TCP ping packet back from %s port %hu (trynum = %d)\n", inet_ntop_ez(&hdr.src, sizeof(hdr.src)), ntohs(tcp->th_sport), trynum); } - } else if (ip->ip_p == IPPROTO_UDP && USI->ptech.rawudpscan) { - struct udp_hdr *udp = (struct udp_hdr *) (((char *) ip) + 4 * ip->ip_hl); + } else if (hdr.proto == IPPROTO_UDP && USI->ptech.rawudpscan) { + struct udp_hdr *udp = (struct udp_hdr *) data; /* Search for this host on the incomplete list */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&hdr.src); if (!hss) continue; // Not from a host that interests us probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); goodone = false; + ss_len = sizeof(target_src); + hss->target->SourceSockAddr(&target_src, &ss_len); + for(probenum = 0; probenum < listsz && !goodone; probenum++) { probeI--; probe = *probeI; @@ -4720,7 +5012,7 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { /* Ensure the connection info matches. */ if (probe->dport() != ntohs(udp->uh_sport) || probe->sport() != ntohs(udp->uh_dport) || - hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr) + sockaddr_storage_cmp(&target_src, &hdr.dst) != 0) continue; /* Replace this with a call to probe_check_trynum_pingseq or similar. */ @@ -4734,8 +5026,8 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { -p- because we scan localhost with src port = dst port and see our outgoing packet and think it is a response. */ if (probe->dport() == probe->sport() && - ip->ip_src.s_addr == ip->ip_dst.s_addr && - probe->ipid() == ntohs(ip->ip_id)) + sockaddr_storage_cmp(&hdr.src, &hdr.dst) == 0 && + probe->ipid() == hdr.ipid) continue; /* We saw the packet we ourselves sent */ goodone = true; @@ -4743,21 +5035,22 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { current_reason = ER_UDPRESPONSE; if (o.debugging) - log_write(LOG_STDOUT, "In response to UDP-ping, we got UDP packet back from %s port %hu (trynum = %d)\n", inet_ntoa(ip->ip_src), htons(udp->uh_sport), trynum); + log_write(LOG_STDOUT, "In response to UDP-ping, we got UDP packet back from %s port %hu (trynum = %d)\n", inet_ntop_ez(&hdr.src, sizeof(hdr.src)), htons(udp->uh_sport), trynum); } - } else if (ip->ip_p == IPPROTO_SCTP && USI->ptech.rawsctpscan) { - struct sctp_hdr *sctp = (struct sctp_hdr *) (((char *) ip) + 4 * ip->ip_hl); - struct dnet_sctp_chunkhdr *chunk = (struct dnet_sctp_chunkhdr *) ((u8 *) sctp + 12); + } else if (hdr.proto == IPPROTO_SCTP && USI->ptech.rawsctpscan) { + struct sctp_hdr *sctp = (struct sctp_hdr *) data; + struct dnet_sctp_chunkhdr *chunk = + (struct dnet_sctp_chunkhdr *) ((u8 *) sctp + 12); /* Search for this host on the incomplete list */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&hdr.src); if (!hss) continue; // Not from a host that interests us probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); goodone = false; + ss_len = sizeof(target_dst); + hss->target->SourceSockAddr(&target_src, &ss_len); + for(probenum = 0; probenum < listsz && !goodone; probenum++) { probeI--; probe = *probeI; @@ -4768,15 +5061,15 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { /* Ensure the connection info matches. */ if (probe->dport() != ntohs(sctp->sh_sport) || probe->sport() != ntohs(sctp->sh_dport) || - hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr) + sockaddr_storage_cmp(&target_src, &hdr.dst) == 0) continue; /* Sometimes we get false results when scanning localhost with -p- because we scan localhost with src port = dst port and see our outgoing packet and think it is a response. */ if (probe->dport() == probe->sport() && - ip->ip_src.s_addr == ip->ip_dst.s_addr && - probe->ipid() == ntohs(ip->ip_id)) + sockaddr_storage_cmp(&hdr.src, &hdr.dst) == 0 && + probe->ipid() == hdr.ipid) continue; /* We saw the packet we ourselves sent */ goodone = true; @@ -4793,28 +5086,25 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { } } else if (!USI->ptech.rawprotoscan) { if (o.debugging > 2) - error("Received packet with protocol %d; ignoring.", ip->ip_p); + error("Received packet with protocol %d; ignoring.", hdr.proto); } /* Check for a protocol reply */ if (!goodone && USI->ptech.rawprotoscan) { - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = ip->ip_src.s_addr; - sin.sin_family = AF_INET; - hss = USI->findHost((struct sockaddr_storage *) &sin); + hss = USI->findHost(&hdr.src); if (!hss) continue; - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); + setTargetMACIfAvailable(hss->target, &linkhdr, &hdr.src, 0); probeI = hss->probes_outstanding.end(); listsz = hss->num_probes_outstanding(); goodone = false; for(probenum = 0; probenum < listsz && !goodone; probenum++) { probeI--; probe = *probeI; - - if (probe->protocol() == ip->ip_p) { + + if (probe->protocol() == hdr.proto) { /* if this is our probe we sent to localhost, then it doesn't count! */ - if (ip->ip_src.s_addr == ip->ip_dst.s_addr && - probe->ipid() == ntohs(ip->ip_id)) + if (sockaddr_storage_cmp(&hdr.src, &hdr.dst) == 0 && + probe->ipid() == hdr.ipid) continue; newstate = HOST_UP; @@ -4826,6 +5116,12 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { } while (!goodone && !timedout); if (goodone && newstate != HOST_UNKNOWN) { + struct sockaddr_storage target_dst; + size_t ss_len; + + ss_len = sizeof(target_dst); + hss->target->TargetSockAddr(&target_dst, &ss_len); + if (probe->isPing()) ultrascan_ping_update(USI, hss, probeI, &USI->now, adjust_timing); else { @@ -4833,12 +5129,13 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) { /* If the host is up, we can forget our other probes. */ if (newstate == HOST_UP) hss->destroyAllOutstandingProbes(); - if (newstate == HOST_UP && ip && bytes >= 20) - setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0); + if (newstate == HOST_UP && data) + setTargetMACIfAvailable(hss->target, &linkhdr, &hdr.src, 0); hss->target->reason.reason_id = current_reason; - hss->target->reason.ttl = ip->ip_ttl; - if(ip->ip_src.s_addr != hss->target->v4host().s_addr) - hss->target->reason.ip_addr.s_addr = ip->ip_src.s_addr; + hss->target->reason.ttl = hdr.ttl; + if (sockaddr_storage_cmp(&hdr.src, &target_dst) != 0) { + hss->target->reason.ip_addr = hdr.src; + } } } @@ -4894,7 +5191,7 @@ static void begin_sniffer(UltraScanInfo *USI, vector &Targets) { } } - if((USI->pd=my_pcap_open_live(Targets[0]->deviceName(), 100, (o.spoofsource)? 1 : 0, pcap_selectable_fd_valid()? 200 : 2))==NULL) + if((USI->pd=my_pcap_open_live(Targets[0]->deviceName(), 256, (o.spoofsource)? 1 : 0, pcap_selectable_fd_valid()? 200 : 2))==NULL) fatal("%s", PCAP_OPEN_ERRMSG); if (USI->ping_scan_arp){ @@ -4919,28 +5216,40 @@ static void begin_sniffer(UltraScanInfo *USI, vector &Targets) { pcap_filter.append(macstring, 4 * 2, 2 * 2); //its not arp, so lets check if for a protocol scan. } else if(USI->prot_scan || (USI->ping_scan && USI->ptech.rawprotoscan)){ + struct sockaddr_storage source; + size_t source_len; + + source_len = sizeof(source); + Targets[0]->SourceSockAddr(&source, &source_len); + if (doIndividual){ pcap_filter="dst host "; - pcap_filter+=inet_ntoa(Targets[0]->v4source()); - pcap_filter+=" and (icmp or ("; + pcap_filter+=inet_ntop_ez(&source, sizeof(source)); + pcap_filter+=" and (icmp or icmp6 or ("; pcap_filter+=dst_hosts; pcap_filter+="))"; }else{ pcap_filter="dst host "; - pcap_filter+=inet_ntoa(Targets[0]->v4source()); + pcap_filter+=inet_ntop_ez(&source, sizeof(source)); } } else if(USI->tcp_scan || USI->udp_scan || USI->sctp_scan || USI->ping_scan) { + struct sockaddr_storage source; + size_t source_len; + + source_len = sizeof(source); + Targets[0]->SourceSockAddr(&source, &source_len); + /* Handle udp, tcp and sctp with one filter. */ if (doIndividual){ pcap_filter="dst host "; - pcap_filter+=inet_ntoa(Targets[0]->v4source()); - pcap_filter+=" and (icmp or ((tcp or udp or sctp) and ("; + pcap_filter+=inet_ntop_ez(&source, sizeof(source)); + pcap_filter+=" and (icmp or icmp6 or ((tcp or udp or sctp) and ("; pcap_filter+=dst_hosts; pcap_filter+=")))"; }else{ pcap_filter="dst host "; - pcap_filter+=inet_ntoa(Targets[0]->v4source()); - pcap_filter+=" and (icmp or tcp or udp or sctp)"; + pcap_filter+=inet_ntop_ez(&source, sizeof(source)); + pcap_filter+=" and (icmp or icmp6 or tcp or udp or sctp)"; } }else assert(0); if (o.debugging) log_write(LOG_PLAIN, "Packet capture filter (device %s): %s\n", Targets[0]->deviceFullName(), pcap_filter.c_str()); diff --git a/scan_engine.h b/scan_engine.h index d3477c430..aec707f5c 100644 --- a/scan_engine.h +++ b/scan_engine.h @@ -117,6 +117,11 @@ struct probespec_icmpdata { u8 code; }; +struct probespec_icmpv6data { + u8 type; + u8 code; +}; + #define PS_NONE 0 #define PS_TCP 1 #define PS_UDP 2 @@ -125,6 +130,7 @@ struct probespec_icmpdata { #define PS_ARP 5 #define PS_CONNECTTCP 6 #define PS_SCTP 7 +#define PS_ICMPV6 8 /* The size of this structure is critical, since there can be tens of thousands of them stored together ... */ @@ -138,6 +144,7 @@ typedef struct probespec { struct probespec_udpdata udp; /* PS_UDP */ struct probespec_sctpdata sctp; /* PS_SCTP */ struct probespec_icmpdata icmp; /* PS_ICMP */ + struct probespec_icmpv6data icmpv6; /* PS_ICMPV6 */ /* Nothing needed for PS_ARP, since src mac and target IP are avail from target structure anyway */ } pd; diff --git a/service_scan.cc b/service_scan.cc index 346260ce9..5748de6f0 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -1733,9 +1733,9 @@ static void adjustPortStateIfNecessary(ServiceNFO *svc) { if (oldstate != PORT_OPEN) { svc->target->ports.setPortState(svc->portno, svc->proto, PORT_OPEN); if (svc->proto == IPPROTO_TCP) - svc->target->ports.setStateReason(svc->portno, svc->proto, ER_TCPRESPONSE, 0, 0); + svc->target->ports.setStateReason(svc->portno, svc->proto, ER_TCPRESPONSE, 0, NULL); if (svc->proto == IPPROTO_UDP) - svc->target->ports.setStateReason(svc->portno, svc->proto, ER_UDPRESPONSE, 0, 0); + svc->target->ports.setStateReason(svc->portno, svc->proto, ER_UDPRESPONSE, 0, NULL); if (o.verbose || o.debugging > 1) { svc->target->NameIP(host, sizeof(host)); diff --git a/targets.cc b/targets.cc index d02d3f86b..d1aa581bc 100644 --- a/targets.cc +++ b/targets.cc @@ -382,9 +382,13 @@ static bool target_needs_new_hostgroup(const HostGroupState *hs, const Target *t return false; /* There are no restrictions on non-root scans. */ - if (!(o.af() == AF_INET && o.isr00t && target->deviceName() != NULL)) + if (!(o.isr00t && target->deviceName() != NULL)) return false; + /* Different address family? */ + if (hs->hostbatch[0]->af() != target->af()) + return true; + /* Different interface name? */ if (hs->hostbatch[0]->deviceName() != NULL && strcmp(hs->hostbatch[0]->deviceName(), target->deviceName()) != 0) { @@ -392,7 +396,7 @@ static bool target_needs_new_hostgroup(const HostGroupState *hs, const Target *t } /* Different source address? */ - if (hs->hostbatch[0]->v4source().s_addr != target->v4source().s_addr) + if (sockaddr_storage_cmp(hs->hostbatch[0]->SourceSockAddr(), target->SourceSockAddr()) != 0) return true; /* Different direct connectedness? */ @@ -404,7 +408,7 @@ static bool target_needs_new_hostgroup(const HostGroupState *hs, const Target *t replies. What happens is one target gets the replies for all probes referring to the same IP address. */ for (i = 0; i < hs->current_batch_sz; i++) { - if (hs->hostbatch[0]->v4host().s_addr == target->v4host().s_addr) + if (sockaddr_storage_cmp(hs->hostbatch[0]->TargetSockAddr(), target->TargetSockAddr()) == 0) return true; } @@ -453,7 +457,7 @@ Target *nexthost(HostGroupState *hs, TargetGroup *exclude_group, 2) We are doing tcp or udp pingscan OR 3) We are doing a raw-mode portscan or osscan or traceroute OR 4) We are on windows and doing ICMP ping */ - if (o.isr00t && o.af() == AF_INET && + if (o.isr00t && ((pingtype & (PINGTYPE_TCP|PINGTYPE_UDP|PINGTYPE_SCTP_INIT|PINGTYPE_PROTO|PINGTYPE_ARP)) || o.RawScan() #ifdef WIN32 || (pingtype & (PINGTYPE_ICMP_PING|PINGTYPE_ICMP_MASK|PINGTYPE_ICMP_TS)) @@ -520,6 +524,7 @@ batchfull: directly connected over ethernet. I may need the MAC addresses later anyway. */ if (hs->hostbatch[0]->ifType() == devt_ethernet && + hs->hostbatch[0]->af() == AF_INET && hs->hostbatch[0]->directlyConnected() && o.sendpref != PACKET_SEND_IP_STRONG) { arpping(hs->hostbatch, hs->current_batch_sz); diff --git a/tcpip.cc b/tcpip.cc index df525a9b6..0337d81a3 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -209,6 +209,81 @@ void PacketTrace::traceArp(pdirection pdir, const u8 *frame, u32 len, return; } +/* Takes a Neighbor Discovery packet and prints it if packet tracing is + enabled. frame must point to the IPv6 header. */ +void PacketTrace::traceND(pdirection pdir, const u8 *frame, u32 len, + struct timeval *now) { + struct timeval tv; + struct ip6_hdr *ip6; + struct icmpv6_hdr *icmpv6; + union icmpv6_msg *msg; + size_t msg_len; + const char *label; + char src[INET6_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; + char who_has[INET6_ADDRSTRLEN], tgt_is[INET_ADDRSTRLEN]; + char desc[128]; + + if (pdir == SENT) { + PktCt.sendPackets++; + PktCt.sendBytes += len; + } else { + PktCt.recvPackets++; + PktCt.recvBytes += len; + } + + if (!o.packetTrace()) + return; + + if (now) + tv = *now; + else + gettimeofday(&tv, NULL); + + if (len < sizeof(*ip6) + sizeof(*icmpv6)) { + error("Packet tracer: ND packets must be at least %lu bytes long (is %lu).", + (unsigned long) (sizeof(*ip6) + sizeof(*icmpv6)), + (unsigned long) len); + return; + } + ip6 = (struct ip6_hdr *) frame; + icmpv6 = (struct icmpv6_hdr *) (frame + sizeof(*ip6)); + msg = (union icmpv6_msg *) (frame + sizeof(*ip6) + sizeof(*icmpv6)); + msg_len = frame + len - (u8 *) msg; + + if (icmpv6->icmpv6_type == ICMPV6_NEIGHBOR_SOLICITATION) { + label = "neighbor solicitation"; + if (msg_len < 20) { + Snprintf(desc, sizeof(desc), "packet too short"); + } else { + inet_ntop(AF_INET6, &msg->nd.icmpv6_target, who_has, sizeof(who_has)); + Snprintf(desc, sizeof(desc), "who has %s", who_has); + } + } else if (icmpv6->icmpv6_type == ICMPV6_NEIGHBOR_ADVERTISEMENT) { + label = "neighbor advertisement"; + if (msg_len < 28) { + Snprintf(desc, sizeof(desc), "packet too short"); + } else if (msg->nd.icmpv6_option_length == 0 || msg->nd.icmpv6_option_type != 2) { + /* We only handle target link-layer address in the first option. */ + Snprintf(desc, sizeof(desc), "no link-layer address"); + } else { + inet_ntop(AF_INET6, &msg->nd.icmpv6_target, tgt_is, sizeof(tgt_is)); + Snprintf(desc, sizeof(desc), "%s is at %s", + tgt_is, eth_ntoa(&msg->nd.icmpv6_mac)); + } + } else { + error("Unknown ICMPV6 type in %s.", __func__); + return; + } + + inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)); + inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst)); + log_write(LOG_STDOUT | LOG_NORMAL, "%s (%.4fs) %s %s > %s %s\n", + (pdir == SENT) ? "SENT" : "RCVD", + o.TimeSinceStart(&tv), label, src, dst, desc); + + return; +} + /* Returns a buffer of ASCII information about a packet that may look like "TCP 127.0.0.1:50923 > 127.0.0.1:3 S ttl=61 id=39516 iplen=40 @@ -376,6 +451,96 @@ struct addrinfo *resolve_all(char *hostname, int pf) } +/* Send a pre-built IPv4 packet. Handles fragmentation and whether to send with + an ethernet handle or a socket. */ +static int send_ipv4_packet(int sd, struct eth_nfo *eth, u8 *packet, + unsigned int packetlen) { + struct ip *ip = (struct ip *) packet; + int res; + + assert(packet); + assert((int) packetlen > 0); + + /* Fragmentation requested && packet is bigger than MTU */ + if(o.fragscan && !(ntohs(ip->ip_off) & IP_DF) && + (packetlen - ip->ip_hl * 4 > (unsigned int) o.fragscan)){ + res = send_frag_ip_packet(sd, eth, packet, packetlen, o.fragscan); + }else{ + res = send_ip_packet_eth_or_sd(sd, eth, packet, packetlen); + } + if (res != -1) + PacketTrace::trace(PacketTrace::SENT, packet, packetlen); + + return res; +} + +static int send_ipv6_packet(int sd, struct eth_nfo *eth, const u8 *packet, + unsigned int packetlen) { + int res; + + res = send_ipv6_packet_eth_or_sd(sd, eth, packet, packetlen); + if (res != -1) + PacketTrace::trace(PacketTrace::SENT, packet, packetlen); + + return res; +} + +int send_ip_packet(int sd, struct eth_nfo *eth, u8 *packet, + unsigned int packetlen) { + struct ip *ip = (struct ip *) packet; + + /* Ensure there's enough to read ip->ip_v at least. */ + if (packetlen < 1) + return -1; + + if (ip->ip_v == 4) + return send_ipv4_packet(sd, eth, packet, packetlen); + else if (ip->ip_v == 6) + return send_ipv6_packet(sd, eth, packet, packetlen); + + fatal("%s only understands IP versions 4 and 6 (got %u)", __func__, ip->ip_v); +} + + +/* Return an IPv4 pseudoheader checksum for the given protocol and data. Unlike + ipv4_pseudoheader_cksum, this knows about STUPID_SOLARIS_CHECKSUM_BUG and + takes care of o.badsum. */ +static u16 ipv4_cksum(const struct in_addr *src, const struct in_addr *dst, + u8 proto, const void *data, u16 len) { + u16 sum; + +#if STUPID_SOLARIS_CHECKSUM_BUG + sum = len; +#else + sum = ipv4_pseudoheader_cksum(src, dst, proto, len, data); +#endif + + if (o.badsum) { + --sum; + if (proto == IPPROTO_UDP && sum == 0) + sum = 0xffff; // UDP checksum=0 means no checksum + } + + return sum; +} + +/* Return an IPv6 pseudoheader checksum for the given protocol and data. Unlike + ipv6_pseudoheader_cksum, this takes care of o.badsum. */ +static u16 ipv6_cksum(const struct in6_addr *src, const struct in6_addr *dst, + u8 nxt, const void *data, u16 len) { + u16 sum; + + sum = ipv6_pseudoheader_cksum(src, dst, nxt, len, data); + + if (o.badsum) { + --sum; + if (nxt == IPPROTO_UDP && sum == 0) + sum = 0xffff; // UDP checksum=0 means no checksum + } + + return sum; +} + // fill ip header. no error check. // This function is also changing what's needed from host to network order. static inline int fill_ip_raw(struct ip *ip, int packetlen, const u8 *ipopt, @@ -417,29 +582,118 @@ static inline int fill_ip_raw(struct ip *ip, int packetlen, const u8 *ipopt, return (sizeof(struct ip) + ipoptlen); } +/* Builds an IP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_ip_raw(const struct in_addr *source, + const struct in_addr *victim, u8 proto, int ttl, + u16 ipid, u8 tos, bool df, const u8 *ipopt, int ipoptlen, + const char *data, u16 datalen, u32 *outpacketlen) { + int packetlen = sizeof(struct ip) + ipoptlen + datalen; + u8 *packet = (u8 *) safe_malloc(packetlen); + struct ip *ip = (struct ip *) packet; + static int myttl = 0; + +/* check that required fields are there and not too silly */ + assert(source); + assert(victim); + assert(ipoptlen % 4 == 0); + +/* Time to live */ + if (ttl == -1) { + myttl = (get_random_uint() % 23) + 37; + } else { + myttl = ttl; + } + + fill_ip_raw(ip, packetlen, ipopt, ipoptlen, + tos, ipid, df ? IP_DF : 0, myttl, proto, source, victim); + + /* We should probably copy the data over too */ + if (data) + memcpy((u8 *) ip + sizeof(struct ip) + ipoptlen, data, datalen); + + *outpacketlen = packetlen; + return packet; +} + +u8 *build_ipv6_raw(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 nextheader, int hoplimit, + char *data, u16 datalen, u32 *outpacketlen) { + u8 *packet; + + assert(source != NULL); + assert(victim != NULL); + + if (hoplimit == -1) + hoplimit = (get_random_uint() % 23) + 37; + + *outpacketlen = sizeof(struct ip6_hdr) + datalen; + packet = (u8 *) safe_malloc(*outpacketlen); + + ip6_pack_hdr(packet, tc, flowlabel, datalen, nextheader, hoplimit, *source, *victim); + memcpy(packet + sizeof(struct ip6_hdr), data, datalen); + + return packet; +} -int send_tcp_raw_decoys(int sd, struct eth_nfo *eth, - const struct in_addr *victim, - int ttl, bool df, - u8 *ipopt, int ipoptlen, - u16 sport, u16 dport, - u32 seq, u32 ack, u8 reserved, u8 flags, - u16 window, u16 urp, u8 *options, int optlen, - char *data, u16 datalen) { - int decoy; +/* Build a TCP packet (no IP header). Sets tcp->th_sum to 0 so it can be filled + in by a function with knowledge of the higher-level pseudoheader. */ +static u8 *build_tcp(u16 sport, u16 dport, u32 seq, u32 ack, u8 reserved, + u8 flags, u16 window, u16 urp, + const u8 *tcpopt, int tcpoptlen, + char *data, u16 datalen, u32 *packetlen) { + struct tcp_hdr *tcp; + u8 *packet; - for (decoy = 0; decoy < o.numdecoys; decoy++) - if (send_tcp_raw(sd, eth, - &o.decoys[decoy], victim, - ttl, df, - ipopt, ipoptlen, - sport, dport, - seq, ack, reserved, flags, window, urp, - options, optlen, data, datalen) == -1) - return -1; + if (tcpoptlen % 4 != 0) + fatal("%s called with an option length argument of %d which is illegal because it is not divisible by 4. Just add \\0 padding to the end.", __func__, tcpoptlen); - return 0; + *packetlen = sizeof(*tcp) + tcpoptlen + datalen; + packet = (u8 *) safe_malloc(*packetlen); + tcp = (struct tcp_hdr *) packet; + + memset(tcp, 0, sizeof(*tcp)); + tcp->th_sport = htons(sport); + tcp->th_dport = htons(dport); + + if (seq) + tcp->th_seq = htonl(seq); + else if (flags & TH_SYN) + get_random_bytes(&(tcp->th_seq), 4); + + if (ack) + tcp->th_ack = htonl(ack); + + if (reserved) + tcp->th_x2 = reserved & 0x0F; + tcp->th_off = 5 + (tcpoptlen / 4); /* words */ + tcp->th_flags = flags; + + if (window) + tcp->th_win = htons(window); + else + tcp->th_win = htons(1024); /* Who cares */ + + if (urp) + tcp->th_urp = htons(urp); + + /* And the options */ + if (tcpoptlen) + memcpy(packet + sizeof(*tcp), tcpopt, tcpoptlen); + + /* We should probably copy the data over too */ + if (data && datalen) + memcpy(packet + sizeof(*tcp) + tcpoptlen, data, datalen); + + tcp->th_sum = 0; + + return packet; } /* Builds a TCP packet (including an IP header) by packing the fields @@ -453,79 +707,45 @@ u8 *build_tcp_raw(const struct in_addr *source, bool df, const u8 *ipopt, int ipoptlen, u16 sport, u16 dport, u32 seq, u32 ack, u8 reserved, u8 flags, u16 window, u16 urp, const u8 *tcpopt, int tcpoptlen, char *data, - u16 datalen, u32 *outpacketlen) { - int packetlen = sizeof(struct ip) + ipoptlen + sizeof(struct tcp_hdr) + tcpoptlen + datalen; - u8 *packet = (u8 *) safe_malloc(packetlen); - struct ip *ip = (struct ip *) packet; - struct tcp_hdr *tcp = (struct tcp_hdr *) ((u8 *) ip + sizeof(struct ip) + ipoptlen); - static int myttl = 0; + u16 datalen, u32 *packetlen) { + struct tcp_hdr *tcp; + u32 tcplen; + u8 *ip; - assert(victim); - assert(source); - assert(ipoptlen % 4 == 0); + tcp = (struct tcp_hdr *) build_tcp(sport, dport, seq, ack, reserved, flags, + window, urp, tcpopt, tcpoptlen, data, datalen, &tcplen); + tcp->th_sum = ipv4_cksum(source, victim, IPPROTO_TCP, tcp, tcplen); + ip = build_ip_raw(source, victim, IPPROTO_TCP, ttl, ipid, tos, df, + ipopt, ipoptlen, (char *) tcp, tcplen, packetlen); + free(tcp); - if (tcpoptlen % 4) - fatal("%s() called with an option length argument of %d which is illegal because it is not divisible by 4. Just add \\0 padding to the end.", __func__, tcpoptlen); + return ip; +} +/* Builds a TCP packet (including an IPv6 header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_tcp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, u32 seq, u32 ack, + u8 reserved, u8 flags, u16 window, u16 urp, + const u8 *tcpopt, int tcpoptlen, char *data, + u16 datalen, u32 *packetlen) { + struct tcp_hdr *tcp; + u32 tcplen; + u8 *ipv6; - /* Time to live */ - if (ttl == -1) { - myttl = (get_random_uint() % 23) + 37; - } else { - myttl = ttl; - } + tcp = (struct tcp_hdr *) build_tcp(sport, dport, seq, ack, reserved, flags, + window, urp, tcpopt, tcpoptlen, data, datalen, &tcplen); + tcp->th_sum = ipv6_cksum(source, victim, IPPROTO_TCP, tcp, tcplen); + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_TCP, hoplimit, + (char *) tcp, tcplen, packetlen); + free(tcp); - /* Fill tcp header */ - memset(tcp, 0, sizeof(struct tcp_hdr)); - tcp->th_sport = htons(sport); - tcp->th_dport = htons(dport); - if (seq) { - tcp->th_seq = htonl(seq); - } else if (flags & TH_SYN) { - get_random_bytes(&(tcp->th_seq), 4); - } - - if (ack) - tcp->th_ack = htonl(ack); - - if (reserved) - tcp->th_x2 = reserved & 0x0F; - tcp->th_off = 5 + (tcpoptlen / 4) /*words */ ; - tcp->th_flags = flags; - - if (window) - tcp->th_win = htons(window); - else - tcp->th_win = htons(1024 * (myttl % 4 + 1)); /* Who cares */ - - /* Urgent pointer */ - if (urp) - tcp->th_urp = htons(urp); - - /* And the options */ - if (tcpoptlen) - memcpy((u8 *) tcp + sizeof(struct tcp_hdr), tcpopt, tcpoptlen); - /* We should probably copy the data over too */ - if (data && datalen) - memcpy((u8 *) tcp + sizeof(struct tcp_hdr) + tcpoptlen, data, datalen); - -#if STUPID_SOLARIS_CHECKSUM_BUG - tcp->th_sum = sizeof(struct tcp_hdr) + tcpoptlen + datalen; -#else - tcp->th_sum = ipv4_pseudoheader_cksum(source, victim, IPPROTO_TCP, - sizeof(struct tcp_hdr) + tcpoptlen + - datalen, (char *) tcp); -#endif - - if (o.badsum) - --tcp->th_sum; - - fill_ip_raw(ip, packetlen, ipopt, ipoptlen, - tos, ipid, df ? IP_DF : 0, myttl, IPPROTO_TCP, - source, victim); - - *outpacketlen = packetlen; - return packet; + return ipv6; } /* You need to call sethdrinclude(sd) on the sending sd before calling this */ @@ -553,32 +773,212 @@ int send_tcp_raw(int sd, struct eth_nfo *eth, return res; } +int send_tcp_raw_decoys(int sd, struct eth_nfo *eth, + const struct in_addr *victim, + int ttl, bool df, + u8 *ipopt, int ipoptlen, + u16 sport, u16 dport, + u32 seq, u32 ack, u8 reserved, u8 flags, + u16 window, u16 urp, u8 *options, int optlen, + char *data, u16 datalen) { + int decoy; + + for (decoy = 0; decoy < o.numdecoys; decoy++) + if (send_tcp_raw(sd, eth, + &o.decoys[decoy], victim, + ttl, df, + ipopt, ipoptlen, + sport, dport, + seq, ack, reserved, flags, window, urp, + options, optlen, data, datalen) == -1) + return -1; + + return 0; +} +/* Build a UDP packet (no IP header). Sets udp->uh_sum to 0 so it can be filled + in by a function with knowledge of the higher-level pseudoheader. */ +static u8 *build_udp(u16 sport, u16 dport, const char *data, u16 datalen, + u32 *packetlen) { + struct udp_hdr *udp; + u8 *packet; -/* Send a pre-built IPv4 packet. Handles fragmentation and whether to send with - an ethernet handle or a socket. */ -int send_ip_packet(int sd, struct eth_nfo *eth, u8 *packet, - unsigned int packetlen) { - struct ip *ip = (struct ip *) packet; - int res; + *packetlen = sizeof(*udp) + datalen; + packet = (u8 *) safe_malloc(*packetlen); + udp = (struct udp_hdr *) packet; - assert(packet); - assert((int) packetlen > 0); + memset(udp, 0, sizeof(*udp)); + udp->uh_sport = htons(sport); + udp->uh_dport = htons(dport); - /* Fragmentation requested && packet is bigger than MTU */ - if(o.fragscan && !(ntohs(ip->ip_off) & IP_DF) && - (packetlen - ip->ip_hl * 4 > (unsigned int) o.fragscan)){ - res = send_frag_ip_packet(sd, eth, packet, packetlen, o.fragscan); - }else{ - res = send_ip_packet_eth_or_sd(sd, eth, packet, packetlen); - } - if (res != -1) - PacketTrace::trace(PacketTrace::SENT, packet, packetlen); + udp->uh_ulen = htons(*packetlen); + if (data && datalen) + memcpy(packet + sizeof(*udp), data, datalen); + udp->uh_sum = 0; + + return packet; +} + +/* Builds a UDP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_udp_raw(const struct in_addr *source, const struct in_addr *victim, + int ttl, u16 ipid, u8 tos, bool df, + u8 *ipopt, int ipoptlen, + u16 sport, u16 dport, + const char *data, u16 datalen, u32 *packetlen) { + struct udp_hdr *udp; + u32 udplen; + u8 *ip; + + udp = (struct udp_hdr *) build_udp(sport, dport, data, datalen, &udplen); + udp->uh_sum = ipv4_cksum(source, victim, IPPROTO_UDP, udp, udplen); + ip = build_ip_raw(source, victim, IPPROTO_UDP, ttl, ipid, tos, df, + ipopt, ipoptlen, (char *) udp, udplen, packetlen); + free(udp); + + return ip; +} + +/* Builds a UDP packet (including an IPv6 header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_udp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, + char *data, u16 datalen, u32 *packetlen) { + struct udp_hdr *udp; + u32 udplen; + u8 *ipv6; + + udp = (struct udp_hdr *) build_udp(sport, dport, data, datalen, &udplen); + udp->uh_sum = ipv6_cksum(source, victim, IPPROTO_UDP, udp, udplen); + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_UDP, hoplimit, + (char *) udp, udplen, packetlen); + free(udp); + + return ipv6; +} + +int send_udp_raw(int sd, struct eth_nfo *eth, + struct in_addr *source, const struct in_addr *victim, + int ttl, u16 ipid, + u8 *ipopt, int ipoptlen, + u16 sport, u16 dport, char *data, u16 datalen) { + unsigned int packetlen; + int res = -1; + u8 *packet = build_udp_raw(source, victim, + ttl, ipid, IP_TOS_DEFAULT, false, + ipopt, ipoptlen, + sport, dport, + data, datalen, &packetlen); + if (!packet) + return -1; + res = send_ip_packet(sd, eth, packet, packetlen); + + free(packet); return res; } +int send_udp_raw_decoys(int sd, struct eth_nfo *eth, + const struct in_addr *victim, + int ttl, u16 ipid, + u8 *ipops, int ipoptlen, + u16 sport, u16 dport, char *data, u16 datalen) { + int decoy; + + for (decoy = 0; decoy < o.numdecoys; decoy++) + if (send_udp_raw(sd, eth, &o.decoys[decoy], victim, + ttl, ipid, ipops, ipoptlen, + sport, dport, data, datalen) == -1) + return -1; + + return 0; +} + + +/* Build an SCTP packet (no IP header). */ +static u8 *build_sctp(u16 sport, u16 dport, u32 vtag, + const char *chunks, int chunkslen, + const char *data, u16 datalen, + u32 *packetlen) { + struct sctp_hdr *sctp; + u8 *packet; + + *packetlen = sizeof(*sctp) + chunkslen + datalen; + packet = (u8 *) safe_malloc(*packetlen); + sctp = (struct sctp_hdr *) packet; + + sctp->sh_sport = htons(sport); + sctp->sh_dport = htons(dport); + sctp->sh_sum = 0; + sctp->sh_vtag = htonl(vtag); + + if (chunks) + memcpy(packet + sizeof(*sctp), chunks, chunkslen); + + if (data) + memcpy(packet + sizeof(*sctp) + chunkslen, data, datalen); + + /* RFC 2960 originally defined Adler32 checksums, which was later + * revised to CRC32C in RFC 3309 and RFC 4960 respectively. + * Nmap uses CRC32C by default, unless --adler32 is given. */ + if (o.adler32) + sctp->sh_sum = htonl(nbase_adler32(packet, *packetlen)); + else + sctp->sh_sum = htonl(nbase_crc32c(packet, *packetlen)); + + if (o.badsum) + --sctp->sh_sum; + + return packet; +} + +/* Builds an SCTP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_sctp_raw(const struct in_addr *source, + const struct in_addr *victim, int ttl, u16 ipid, + u8 tos, bool df, u8 *ipopt, int ipoptlen, u16 sport, + u16 dport, u32 vtag, char *chunks, int chunkslen, + char *data, u16 datalen, u32 *packetlen) { + u8 *ip, *sctp; + u32 sctplen; + + sctp = build_sctp(sport, dport, vtag, chunks, chunkslen, data, datalen, &sctplen); + ip = build_ip_raw(source, victim, IPPROTO_SCTP, ttl, ipid, tos, df, + ipopt, ipoptlen, (char *) sctp, sctplen, packetlen); + free(sctp); + + return ip; +} + +u8 *build_sctp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, u32 vtag, + char *chunks, int chunkslen, char *data, u16 datalen, + u32 *packetlen) { + u8 *ipv6, *sctp; + u32 sctplen; + + sctp = build_sctp(sport, dport, vtag, chunks, chunkslen, data, datalen, &sctplen); + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_SCTP, hoplimit, + (char *) sctp, sctplen, packetlen); + free(sctp); + + return ipv6; +} /* Builds an ICMP packet (including an IP header) by packing the fields @@ -649,6 +1049,51 @@ u8 *build_icmp_raw(const struct in_addr *source, ipopt, ipoptlen, ping, icmplen, packetlen); } + +/* Builds an ICMPv6 packet (including an IPv6 header). */ +u8 *build_icmpv6_raw(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 seq, u16 id, u8 ptype, u8 pcode, + char *data, u16 datalen, u32 *packetlen) { + char *packet; + struct icmpv6_hdr *icmpv6; + union icmpv6_msg *msg; + unsigned int icmplen; + u8 *ipv6; + + packet = (char *) safe_malloc(sizeof(*icmpv6) + sizeof(*msg) + datalen); + icmpv6 = (struct icmpv6_hdr *) packet; + msg = (union icmpv6_msg *) (packet + sizeof(*icmpv6)); + + icmplen = sizeof(*icmpv6); + icmpv6->icmpv6_type = ptype; + icmpv6->icmpv6_code = pcode; + + if (ptype == ICMPV6_ECHO) { + msg->echo.icmpv6_seq = htons(seq); + msg->echo.icmpv6_id = htons(id); + icmplen += sizeof(msg->echo); + } + + /* At this point icmplen <= sizeof(*icmpv6) + sizeof(*msg). */ + memcpy(packet + icmplen, data, datalen); + icmplen += datalen; + + icmpv6->icmpv6_cksum = 0; + icmpv6->icmpv6_cksum = ipv6_pseudoheader_cksum(source, victim, + IPPROTO_ICMPV6, icmplen, icmpv6); + if (o.badsum) + icmpv6->icmpv6_cksum--; + + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_ICMPV6, hoplimit, + packet, icmplen, packetlen); + + free(packet); + + return ipv6; +} + + /* Builds an IGMP packet (including an IP header) by packing the fields with the given information. It allocates a new buffer to store the packet contents, and then returns that buffer. The packet is not @@ -836,211 +1281,6 @@ int readudppacket(const u8 *packet, int readdata) { return 0; } -int send_udp_raw_decoys(int sd, struct eth_nfo *eth, - const struct in_addr *victim, - int ttl, u16 ipid, - u8 *ipops, int ipoptlen, - u16 sport, u16 dport, char *data, u16 datalen) { - int decoy; - - for (decoy = 0; decoy < o.numdecoys; decoy++) - if (send_udp_raw(sd, eth, &o.decoys[decoy], victim, - ttl, ipid, ipops, ipoptlen, - sport, dport, data, datalen) == -1) - return -1; - - return 0; -} - - -/* Builds a UDP packet (including an IP header) by packing the fields - with the given information. It allocates a new buffer to store the - packet contents, and then returns that buffer. The packet is not - actually sent by this function. Caller must delete the buffer when - finished with the packet. The packet length is returned in - packetlen, which must be a valid int pointer. */ -u8 *build_udp_raw(const struct in_addr *source, const struct in_addr *victim, - int ttl, u16 ipid, u8 tos, bool df, - u8 *ipopt, int ipoptlen, - u16 sport, u16 dport, - const char *data, u16 datalen, u32 *outpacketlen) { - int packetlen = - sizeof(struct ip) + ipoptlen + sizeof(struct udp_hdr) + datalen; - u8 *packet = (u8 *) safe_malloc(packetlen); - struct ip *ip = (struct ip *) packet; - struct udp_hdr *udp = - (struct udp_hdr *) ((u8 *) ip + sizeof(struct ip) + ipoptlen); - static int myttl = 0; - - /* check that required fields are there and not too silly */ - assert(victim); - assert(source); - assert(ipoptlen % 4 == 0); - - /* Time to live */ - if (ttl == -1) { - myttl = (get_random_uint() % 23) + 37; - } else { - myttl = ttl; - } - - udp->uh_sport = htons(sport); - udp->uh_dport = htons(dport); - udp->uh_sum = 0; - udp->uh_ulen = htons(sizeof(struct udp_hdr) + datalen); - - /* We should probably copy the data over too */ - if (data) - memcpy((u8 *) udp + sizeof(struct udp_hdr), data, datalen); - - /* OK, now we should be able to compute a valid checksum */ -#if STUPID_SOLARIS_CHECKSUM_BUG - udp->uh_sum = sizeof(struct udp_hdr) + datalen; -#else - udp->uh_sum = ipv4_pseudoheader_cksum(source, victim, IPPROTO_UDP, - sizeof(struct udp_hdr) + datalen, - (char *) udp); -#endif - - if (o.badsum) { - --udp->uh_sum; - if (udp->uh_sum == 0) - udp->uh_sum = 0xffff; // UDP checksum=0 means no checksum - } - - fill_ip_raw(ip, packetlen, ipopt, ipoptlen, - tos, ipid, df ? IP_DF : 0, myttl, IPPROTO_UDP, - source, victim); - - *outpacketlen = packetlen; - return packet; -} - -int send_udp_raw(int sd, struct eth_nfo *eth, - struct in_addr *source, const struct in_addr *victim, - int ttl, u16 ipid, - u8 *ipopt, int ipoptlen, - u16 sport, u16 dport, char *data, u16 datalen) { - unsigned int packetlen; - int res = -1; - u8 *packet = build_udp_raw(source, victim, - ttl, ipid, IP_TOS_DEFAULT, false, - ipopt, ipoptlen, - sport, dport, - data, datalen, &packetlen); - if (!packet) - return -1; - res = send_ip_packet(sd, eth, packet, packetlen); - - free(packet); - return res; -} - -/* Builds an SCTP packet (including an IP header) by packing the fields - with the given information. It allocates a new buffer to store the - packet contents, and then returns that buffer. The packet is not - actually sent by this function. Caller must delete the buffer when - finished with the packet. The packet length is returned in - packetlen, which must be a valid int pointer. */ -u8 *build_sctp_raw(const struct in_addr *source, - const struct in_addr *victim, int ttl, u16 ipid, - u8 tos, bool df, u8 *ipopt, int ipoptlen, u16 sport, - u16 dport, u32 vtag, char *chunks, int chunkslen, - char *data, u16 datalen, u32 *outpacketlen) { - int packetlen = - sizeof(struct ip) + ipoptlen + sizeof(struct sctp_hdr) + chunkslen + - datalen; - u8 *packet = (u8 *) safe_malloc(packetlen); - struct ip *ip = (struct ip *) packet; - struct sctp_hdr *sctp = - (struct sctp_hdr *) ((u8 *) ip + sizeof(struct ip) + ipoptlen); - static int myttl = 0; - - /* check that required fields are there and not too silly */ - assert(victim); - assert(source); - assert(ipoptlen % 4 == 0); - - /* Time to live */ - if (ttl == -1) { - myttl = (get_random_uint() % 23) + 37; - } else { - myttl = ttl; - } - - sctp->sh_sport = htons(sport); - sctp->sh_dport = htons(dport); - sctp->sh_sum = 0; - sctp->sh_vtag = htonl(vtag); - - if (chunks) - memcpy((u8 *) sctp + sizeof(struct sctp_hdr), chunks, chunkslen); - - if (data) - memcpy((u8 *) sctp + sizeof(struct sctp_hdr) + chunkslen, data, - datalen); - - /* RFC 2960 originally defined Adler32 checksums, which was later - * revised to CRC32C in RFC 3309 and RFC 4960 respectively. - * Nmap uses CRC32C by default, unless --adler32 is given. */ - if (o.adler32) - sctp->sh_sum = htonl(nbase_adler32((unsigned char *) sctp, - sizeof(struct sctp_hdr) + - chunkslen + datalen)); - else - sctp->sh_sum = htonl(nbase_crc32c((unsigned char *) sctp, - sizeof(struct sctp_hdr) + chunkslen + - datalen)); - - if (o.badsum) - --sctp->sh_sum; - - fill_ip_raw(ip, packetlen, ipopt, ipoptlen, - tos, ipid, df ? IP_DF : 0, myttl, IPPROTO_SCTP, - source, victim); - - *outpacketlen = packetlen; - return packet; -} - -/* Builds an IP packet (including an IP header) by packing the fields - with the given information. It allocates a new buffer to store the - packet contents, and then returns that buffer. The packet is not - actually sent by this function. Caller must delete the buffer when - finished with the packet. The packet length is returned in - packetlen, which must be a valid int pointer. */ -u8 *build_ip_raw(const struct in_addr *source, - const struct in_addr *victim, u8 proto, int ttl, - u16 ipid, u8 tos, bool df, u8 *ipopt, int ipoptlen, - char *data, u16 datalen, u32 *outpacketlen) { - int packetlen = sizeof(struct ip) + ipoptlen + datalen; - u8 *packet = (u8 *) safe_malloc(packetlen); - struct ip *ip = (struct ip *) packet; - static int myttl = 0; - -/* check that required fields are there and not too silly */ - assert(source); - assert(victim); - assert(ipoptlen % 4 == 0); - -/* Time to live */ - if (ttl == -1) { - myttl = (get_random_uint() % 23) + 37; - } else { - myttl = ttl; - } - - fill_ip_raw(ip, packetlen, ipopt, ipoptlen, - tos, ipid, df ? IP_DF : 0, myttl, proto, source, victim); - - /* We should probably copy the data over too */ - if (data) - memcpy((u8 *) ip + sizeof(struct ip) + ipoptlen, data, datalen); - - *outpacketlen = packetlen; - return packet; -} - /* Used by validatepkt() to validate the TCP header (including option lengths). The options checked are MSS, WScale, SackOK, Sack, and Timestamp. */ @@ -1118,65 +1358,85 @@ static bool validateTCPhdr(u8 *tcpc, unsigned len) { * read more than the IP header says we should have so as to not pass garbage * data to the caller. */ -static bool validatepkt(u8 *ipc, unsigned len) { +static bool validatepkt(u8 *ipc, unsigned *len) { struct ip *ip = (struct ip *) ipc; - unsigned fragoff, iphdrlen, iplen; + const void *data; + unsigned int datalen, iplen; + u8 hdr; - if (len < sizeof(struct ip)) { + if (*len < 1) { if (o.debugging >= 3) - error("Rejecting tiny, supposed IP packet (size %u)", len); + error("Rejecting tiny, supposed IP packet (size %u)", *len); return false; } - if (ip->ip_v != 4) { + if (ip->ip_v == 4) { + unsigned fragoff, iplen; + + datalen = *len; + data = ipv4_get_data(ip, &datalen); + if (data == NULL) { + if (o.debugging >= 3) + error("Rejecting IP packet because of invalid length"); + return false; + } + + iplen = ntohs(ip->ip_len); + + fragoff = 8 * (ntohs(ip->ip_off) & IP_OFFMASK); + if (fragoff) { + if (o.debugging >= 3) + error("Rejecting IP fragment (offset %u)", fragoff); + return false; + } + + /* OK, since the IP header has been validated, we don't want to tell + * the caller they have more packet than they really have. This can + * be caused by the Ethernet CRC trailer being counted, for example. */ + if (*len > iplen) + *len = iplen; + + hdr = ip->ip_p; + } else if (ip->ip_v == 6) { + const struct ip6_hdr *ip6 = (struct ip6_hdr *) ipc; + + datalen = *len; + data = ipv6_get_data(ip6, &datalen, &hdr); + if (data == NULL) { + if (o.debugging >= 3) + error("Rejecting IP packet because of invalid length"); + return false; + } + + iplen = ntohs(ip6->ip6_plen); + if (datalen > iplen) + *len -= datalen - iplen; + } else { if (o.debugging >= 3) error("Rejecting IP packet because of invalid version number %u", ip->ip_v); return false; } - iphdrlen = ip->ip_hl * 4; - - if (iphdrlen < sizeof(struct ip)) { - if (o.debugging >= 3) - error("Rejecting IP packet because of invalid header length %u", iphdrlen); - return false; - } - - iplen = ntohs(ip->ip_len); - - if (iplen < iphdrlen) { - if (o.debugging >= 3) - error("Rejecting IP packet because of invalid total length %u", iplen); - return false; - } - - fragoff = 8 * (ntohs(ip->ip_off) & IP_OFFMASK); - - if (fragoff) { - if (o.debugging >= 3) - error("Rejecting IP fragment (offset %u)", fragoff); - return false; - } - - switch (ip->ip_p) { + switch (hdr) { case IPPROTO_TCP: - if (iphdrlen + sizeof(struct tcp_hdr) > len) { + if (datalen < sizeof(struct tcp_hdr)) { if (o.debugging >= 3) error("Rejecting TCP packet because of incomplete header"); return false; } - if (!validateTCPhdr(ipc + iphdrlen, len - iphdrlen)) { + if (!validateTCPhdr((u8 *) data, datalen)) { if (o.debugging >= 3) error("Rejecting TCP packet because of bad TCP header"); return false; } break; case IPPROTO_UDP: - if (iphdrlen + sizeof(struct udp_hdr) < len) - break; - if (o.debugging >= 3) - error("Rejecting UDP packet because of incomplete header"); - return false; + if (datalen < sizeof(struct udp_hdr)) { + if (o.debugging >= 3) + error("Rejecting UDP packet because of incomplete header"); + return false; + } + break; default: break; } @@ -1210,7 +1470,6 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, static char *alignedbuf = NULL; static unsigned int alignedbufsz = 0; static int warning = 0; - struct ip *iphdr; if (linknfo) { memset(linknfo, 0, sizeof(*linknfo)); @@ -1351,7 +1610,7 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, } p += offset; } - if (!p || (*p & 0xF0) != 0x40) { + if (!p) { /* Should we timeout? */ if (to_usec == 0) { timedout = 1; @@ -1362,7 +1621,218 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, } } } - } while (!timedout && (!p || (*p & 0xF0) != 0x40)); /* Go until we get IPv4 packet */ + } while (!timedout && (!p)); + + if (timedout) { + *len = 0; + return NULL; + } + *len = head.caplen - offset; + if (*len > alignedbufsz) { + alignedbuf = (char *) safe_realloc(alignedbuf, *len); + alignedbufsz = *len; + } + memcpy(alignedbuf, p, *len); + + if (validate) { + const struct ip *ip = (struct ip *) alignedbuf; + + /* Let's see if this packet passes inspection.. */ + if (!validatepkt((u8 *) alignedbuf, len) || ip->ip_v != 4) { + *len = 0; + return NULL; + } + } + // printf("Just got a packet at %li,%li\n", head.ts.tv_sec, head.ts.tv_usec); + if (rcvdtime) { + // FIXME: I eventually need to figure out why Windows head.ts time is sometimes BEFORE the time I + // sent the packet (which is according to gettimeofday() in nbase). For now, I will sadly have to + // use gettimeofday() for Windows in this case + // Actually I now allow .05 discrepancy. So maybe this isn't needed. I'll comment out for now. + // Nope: it is still needed at least for Windows. Sometimes the time from he 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 + } + + if (rcvdtime) + PacketTrace::trace(PacketTrace::RCVD, (u8 *) alignedbuf, *len, + rcvdtime); + else + PacketTrace::trace(PacketTrace::RCVD, (u8 *) alignedbuf, *len); + + return alignedbuf; +} + +char *readip46_pcap(pcap_t *pd, unsigned int *len, long to_usec, + struct timeval *rcvdtime, struct link_header *linknfo, bool validate) { + unsigned int offset = 0; + struct pcap_pkthdr head; + char *p; + int datalink; + int timedout = 0; + struct timeval tv_start, tv_end; + static char *alignedbuf = NULL; + static unsigned int alignedbufsz = 0; + static int warning = 0; + + if (linknfo) { + memset(linknfo, 0, sizeof(*linknfo)); + } + + if (!pd) + fatal("NULL packet device passed to %s", __func__); + + if (to_usec < 0) { + if (!warning) { + warning = 1; + 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) + fatal("Cannot obtain datalink information: %s", pcap_geterr(pd)); + + /* NOTE: IF A NEW OFFSET EVER EXCEEDS THE CURRENT MAX (24), ADJUST + MAX_LINK_HEADERSZ in tcpip.h */ + switch (datalink) { + case DLT_EN10MB: + offset = 14; + 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 + default: + p = (char *) pcap_next(pd, &head); + if (head.caplen == 0) { + /* Lets sleep a brief time and try again to increase the chance of seeing + a real packet ... */ + usleep(500000); + p = (char *) pcap_next(pd, &head); + } + if (head.caplen > 100000) { + fatal("FATAL: %s: bogus caplen from libpcap (%d) on interface type %d", __func__, head.caplen, datalink); + } + error("FATAL: Unknown datalink type (%d). Caplen: %d; Packet:", datalink, head.caplen); + nmap_hexdump((unsigned char *) p, head.caplen); + exit(1); + } + + if (to_usec > 0) { + gettimeofday(&tv_start, NULL); + } + + do { +#ifdef WIN32 + long to_left; + + if (to_usec > 0) { + gettimeofday(&tv_end, NULL); + to_left = MAX(1, (to_usec - TIMEVAL_SUBTRACT(tv_end, tv_start)) / 1000); + } else { + to_left = 1; + } + // Set the timeout (BUGBUG: this is cheating) + PacketSetReadTimeout(pd->adapter, to_left); +#endif + + p = NULL; + + if (pcap_select(pd, to_usec) == 0) + timedout = 1; + else + p = (char *) pcap_next(pd, &head); + + if (p) { + if (head.caplen <= offset) { + *len = 0; + return NULL; + } + if (offset && linknfo) { + linknfo->datalinktype = datalink; + linknfo->headerlen = offset; + assert(offset < MAX_LINK_HEADERSZ); + memcpy(linknfo->header, p, MIN(sizeof(linknfo->header), offset)); + } + p += offset; + } + if (!p) { + /* 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; + } + } + } + } while (!timedout && (!p)); if (timedout) { *len = 0; @@ -1377,18 +1847,10 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, if (validate) { /* Let's see if this packet passes inspection.. */ - if (!validatepkt((u8 *) alignedbuf, *len)) { + if (!validatepkt((u8 *) alignedbuf, len)) { *len = 0; return NULL; } - - iphdr = (struct ip *) alignedbuf; - - /* OK, since the IP header has been validated, we don't want to tell - * the caller they have more packet than they really have. This can - * be caused by the Ethernet CRC trailer being counted, for example. */ - if (*len > ntohs(iphdr->ip_len)) - *len = ntohs(iphdr->ip_len); } // printf("Just got a packet at %li,%li\n", head.ts.tv_sec, head.ts.tv_usec); if (rcvdtime) { @@ -1458,13 +1920,13 @@ void pcap_print_stats(int logt, pcap_t *pd) { 4) Use the routing table to try to determine rather target is directly connected to the src host running Nmap. If it is, set the MAC. - This function returns 0 if it ends up setting the MAC, nonzero otherwise - - This function assumes that ip has already been verified as - containing a complete IP header (or at least the first 20 bytes). */ + This function returns 0 if it ends up setting the MAC, nonzero otherwise. */ int setTargetMACIfAvailable(Target *target, struct link_header *linkhdr, - struct ip *ip, int overwrite) { - if (!linkhdr || !target || !ip) + const struct sockaddr_storage *src, int overwrite) { + struct sockaddr_storage addr; + size_t addr_len; + + if (!linkhdr || !target || !src) return 1; if (linkhdr->datalinktype != DLT_EN10MB || linkhdr->headerlen != 14) @@ -1473,7 +1935,9 @@ int setTargetMACIfAvailable(Target *target, struct link_header *linkhdr, if (!overwrite && target->MACAddress()) return 3; - if (ip->ip_src.s_addr != target->v4host().s_addr) + addr_len = sizeof(addr); + target->TargetSockAddr(&addr, &addr_len); + if (sockaddr_storage_cmp(src, &addr) != 0) return 4; /* Sometimes bogus MAC address still gets through, like during some localhost scans */ @@ -1547,12 +2011,23 @@ bool setTargetNextHopMAC(Target *target) { /* OK, the last choice is to send our own damn ARP request (and retransmissions if necessary) to determine the MAC */ target->SourceSockAddr(&srcss, NULL); - if (doArp(target->deviceFullName(), target->SrcMACAddress(), - &srcss, &targetss, mac, PacketTrace::traceArp)) { - arp_cache_set(&targetss, mac); - target->setNextHopMACAddress(mac); - return true; + if (target->af() == AF_INET){ + if (doArp(target->deviceFullName(), target->SrcMACAddress(), + &srcss, &targetss, mac, PacketTrace::traceArp)) { + arp_cache_set(&targetss, mac); + target->setNextHopMACAddress(mac); + return true; + } } + else if (target->af() == AF_INET6){ + if (doND(target->deviceFullName(), target->SrcMACAddress(), + &srcss, &targetss, mac, PacketTrace::traceND)) { + arp_cache_set(&targetss, mac); + target->setNextHopMACAddress(mac); + return true; + } + } + /* I'm afraid that we couldn't find it! Maybe it doesn't exist? */ return false; diff --git a/tcpip.h b/tcpip.h index 4bd0c9587..b9223b434 100644 --- a/tcpip.h +++ b/tcpip.h @@ -253,6 +253,8 @@ class PacketTrace { efficient by avoiding a gettimeofday() call. */ static void traceArp(pdirection pdir, const u8 *frame, u32 len, struct timeval *now); + static void traceND(pdirection pdir, const u8 *frame, u32 len, + struct timeval *now); }; class PacketCounter { @@ -418,22 +420,27 @@ bool routethrough(const struct sockaddr_storage * const dest, unsigned short in_cksum(u16 *ptr,int nbytes); -/* Build and send a raw tcp packet. If TTL is -1, a partially random - (but likely large enough) one is chosen */ -int send_tcp_raw( int sd, struct eth_nfo *eth, - const struct in_addr *source, const struct in_addr *victim, - int ttl, bool df, - u8* ipopt, int ipoptlen, - u16 sport, u16 dport, - u32 seq, u32 ack, u8 reserved, u8 flags, u16 window, u16 urp, - u8 *options, int optlen, - char *data, u16 datalen); -int send_udp_raw( int sd, struct eth_nfo *eth, - struct in_addr *source, const struct in_addr *victim, - int ttl, u16 ipid, - u8* ipopt, int ipoptlen, - u16 sport, u16 dport, - char *data, u16 datalen); +/* Send a pre-built IPv4 or IPv6 packet */ +int send_ip_packet(int sd, struct eth_nfo *eth, u8 *packet, + unsigned int packetlen); + +/* Builds an IP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_ip_raw(const struct in_addr *source, const struct in_addr *victim, + u8 proto, + int ttl, u16 ipid, u8 tos, bool df, + const u8* ipopt, int ipoptlen, + const char *data, u16 datalen, + u32 *packetlen); + +u8 *build_ipv6_raw(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 nextheader, int hoplimit, + char *data, u16 datalen, u32 *outpacketlen); /* Builds a TCP packet (including an IP header) by packing the fields with the given information. It allocates a new buffer to store the @@ -450,6 +457,33 @@ u8 *build_tcp_raw(const struct in_addr *source, const struct in_addr *victim, char *data, u16 datalen, u32 *packetlen); +u8 *build_tcp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, u32 seq, u32 ack, + u8 reserved, u8 flags, u16 window, u16 urp, + const u8 *tcpopt, int tcpoptlen, char *data, + u16 datalen, u32 *packetlen); + +/* Build and send a raw tcp packet. If TTL is -1, a partially random + (but likely large enough) one is chosen */ +int send_tcp_raw( int sd, struct eth_nfo *eth, + const struct in_addr *source, const struct in_addr *victim, + int ttl, bool df, + u8* ipopt, int ipoptlen, + u16 sport, u16 dport, + u32 seq, u32 ack, u8 reserved, u8 flags, u16 window, u16 urp, + u8 *options, int optlen, + char *data, u16 datalen); + +int send_tcp_raw_decoys( int sd, struct eth_nfo *eth, + const struct in_addr *victim, + int ttl, bool df, + u8* ipopt, int ipoptlen, + u16 sport, u16 dport, + u32 seq, u32 ack, u8 reserved, u8 flags, u16 window, u16 urp, + u8 *options, int optlen, + char *data, u16 datalen); + /* Builds a UDP packet (including an IP header) by packing the fields with the given information. It allocates a new buffer to store the packet contents, and then returns that buffer. The packet is not @@ -463,6 +497,25 @@ u8 *build_udp_raw(const struct in_addr *source, const struct in_addr *victim, const char *data, u16 datalen, u32 *packetlen); +u8 *build_udp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, + char *data, u16 datalen, u32 *packetlen); + +int send_udp_raw( int sd, struct eth_nfo *eth, + struct in_addr *source, const struct in_addr *victim, + int ttl, u16 ipid, + u8* ipopt, int ipoptlen, + u16 sport, u16 dport, + char *data, u16 datalen); + +int send_udp_raw_decoys( int sd, struct eth_nfo *eth, + const struct in_addr *victim, + int ttl, u16 ipid, + u8* ipops, int ip, + u16 sport, u16 dport, + char *data, u16 datalen); + /* Builds an SCTP packet (including an IP header) by packing the fields with the given information. It allocates a new buffer to store the packet contents, and then returns that buffer. The packet is not @@ -477,6 +530,12 @@ u8 *build_sctp_raw(const struct in_addr *source, const struct in_addr *victim, char *data, u16 datalen, u32 *packetlen); +u8 *build_sctp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, u32 vtag, + char *chunks, int chunkslen, char *data, u16 datalen, + u32 *packetlen); + /* Builds an ICMP packet (including an IP header) by packing the fields with the given information. It allocates a new buffer to store the packet contents, and then returns that buffer. The @@ -491,6 +550,11 @@ u8 *build_icmp_raw(const struct in_addr *source, const struct in_addr *victim, u16 seq, unsigned short id, u8 ptype, u8 pcode, char *data, u16 datalen, u32 *packetlen); +u8 *build_icmpv6_raw(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 seq, u16 id, u8 ptype, u8 pcode, + char *data, u16 datalen, u32 *packetlen); + /* Builds an IGMP packet (including an IP header) by packing the fields with the given information. It allocates a new buffer to store the packet contents, and then returns that buffer. The packet is not @@ -504,40 +568,6 @@ u8 *build_igmp_raw(const struct in_addr *source, const struct in_addr *victim, u8 ptype, u8 pcode, char *data, u16 datalen, u32 *packetlen); -/* Builds an IP packet (including an IP header) by packing the fields - with the given information. It allocates a new buffer to store the - packet contents, and then returns that buffer. The packet is not - actually sent by this function. Caller must delete the buffer when - finished with the packet. The packet length is returned in - packetlen, which must be a valid int pointer. */ -u8 *build_ip_raw(const struct in_addr *source, const struct in_addr *victim, - u8 proto, - int ttl, u16 ipid, u8 tos, bool df, - u8* ipopt, int ipoptlen, - char *data, u16 datalen, - u32 *packetlen); - -/* Send a pre-built IPv4 packet */ -int send_ip_packet(int sd, struct eth_nfo *eth, u8 *packet, - unsigned int packetlen); - -/* Decoy versions of the raw packet sending functions ... */ -int send_tcp_raw_decoys( int sd, struct eth_nfo *eth, - const struct in_addr *victim, - int ttl, bool df, - u8* ipopt, int ipoptlen, - u16 sport, u16 dport, - u32 seq, u32 ack, u8 reserved, u8 flags, u16 window, u16 urp, - u8 *options, int optlen, - char *data, u16 datalen); - -int send_udp_raw_decoys( int sd, struct eth_nfo *eth, - const struct in_addr *victim, - int ttl, u16 ipid, - u8* ipops, int ip, - u16 sport, u16 dport, - char *data, u16 datalen); - // Returns whether the packet receive time value obtaned from libpcap // (and thus by readip_pcap()) should be considered valid. When @@ -577,13 +607,10 @@ char *getFinalPacketStats(char *buf, int buflen); directly connected to the src host running Nmap. If it is, set the MAC. This function returns 0 if it ends up setting the MAC, nonzero otherwise - - This function assumes that ip has already been verified as - containing a complete IP header (or at least the first 20 bytes). */ int setTargetMACIfAvailable(Target *target, struct link_header *linkhdr, - struct ip *ip, int overwrite); + const struct sockaddr_storage *src, int overwrite); /* This function ensures that the next hop MAC address for a target is filled in. This address is the target's own MAC if it is directly @@ -608,6 +635,9 @@ int get_link_offset(char *device); char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, struct timeval *rcvdtime, struct link_header *linknfo, bool validate); +char *readip46_pcap(pcap_t *pd, unsigned int *len, long to_usec, + struct timeval *rcvdtime, struct link_header *linknfo, bool validate); + /* 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) diff --git a/traceroute.cc b/traceroute.cc index c924726c9..868fd0287 100644 --- a/traceroute.cc +++ b/traceroute.cc @@ -284,7 +284,7 @@ public: void resend(int rawsd, eth_t *ethsd, struct timeval *now = NULL); bool is_timedout(struct timeval *now = NULL) const; bool may_resend() const; - virtual unsigned char *build_packet(const struct in_addr *source, + virtual unsigned char *build_packet(const struct sockaddr_storage *source, u32 *len) const = 0; static Probe *make(HostState *host, struct probespec pspec, u8 ttl); @@ -550,13 +550,9 @@ struct probespec HostState::get_probe(const Target *target) { struct probespec probe; probe = target->pingprobe; - if (probe.type == PS_NONE || probe.type == PS_ARP) { - /* No responsive probe known? The user probably skipped both ping and - port scan. Guess ICMP echo as the most likely to get a response. */ - probe.type = PS_ICMP; - probe.proto = IPPROTO_ICMP; - probe.pd.icmp.type = ICMP_ECHO; - probe.pd.icmp.code = 0; + if (probe.type == PS_TCP || probe.type == PS_UDP || probe.type == PS_ICMP || + probe.type == PS_SCTP || probe.type == PS_ICMPV6) { + /* Nothing needed. */ } else if (probe.type == PS_PROTO) { /* If this is an IP protocol probe, fill in some fields for some common protocols. We cheat and store them in the TCP-, UDP-, SCTP- and @@ -571,6 +567,22 @@ struct probespec HostState::get_probe(const Target *target) { } else if (probe.proto == IPPROTO_ICMP) { probe.pd.icmp.type = ICMP_ECHO; } + } else { + /* No responsive probe known? The user probably skipped both ping and + port scan. Guess ICMP echo as the most likely to get a response. */ + if (target->af() == AF_INET) { + probe.type = PS_ICMP; + probe.proto = IPPROTO_ICMP; + probe.pd.icmp.type = ICMP_ECHO; + probe.pd.icmp.code = 0; + } else if (target->af() == AF_INET6) { + probe.type = PS_ICMPV6; + probe.proto = IPPROTO_ICMPV6; + probe.pd.icmp.type = ICMPV6_ECHO; + probe.pd.icmp.code = 0; + } else { + fatal("Unknown address family %d", target->af()); + } } return probe; @@ -606,18 +618,29 @@ void Probe::send(int rawsd, eth_t *ethsd, struct timeval *now) { } for (decoy = 0; decoy < o.numdecoys; decoy++) { - const struct in_addr *source; + struct sockaddr_storage source; + size_t source_len; unsigned char *packet; u32 packetlen; if (decoy == o.decoyturn) { - source = host->target->v4sourceip(); + source_len = sizeof(source); + host->target->SourceSockAddr(&source, &source_len); sent_time = get_now(now); } else { - source = &o.decoys[decoy]; + if (o.af() == AF_INET) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) &source; + sin->sin_family = AF_INET; + sin->sin_addr = o.decoys[decoy]; + } else { + /* Decoys are IPv4-only. */ + continue; + } } - packet = this->build_packet(source, &packetlen); + packet = this->build_packet(&source, &packetlen); send_ip_packet(rawsd, ethp, packet, packetlen); free(packet); } @@ -650,8 +673,11 @@ public: : Probe(host, pspec, ttl) { } - unsigned char *build_packet(const struct in_addr *source, u32 *len) const { - return build_icmp_raw(source, host->target->v4hostip(), ttl, + unsigned char *build_packet(const struct sockaddr_storage *source, u32 *len) const { + const struct sockaddr_in *sin; + assert(source->ss_family == AF_INET); + sin = (struct sockaddr_in *) source; + return build_icmp_raw(&sin->sin_addr, host->target->v4hostip(), ttl, 0x0000, 0x00, false, NULL, 0, token, global_id, pspec.pd.icmp.type, pspec.pd.icmp.code, o.extra_payload, o.extra_payload_length, len); @@ -664,7 +690,7 @@ public: TCPProbe(HostState *host, struct probespec pspec, u8 ttl) : Probe(host, pspec, ttl) { } - unsigned char *build_packet(const struct in_addr *source, u32 *len) const { + unsigned char *build_packet(const struct sockaddr_storage *source, u32 *len) const { const char *tcpopts; int tcpoptslen; u32 ack; @@ -681,11 +707,23 @@ public: } /* For TCP we encode the token in the source port. */ - return build_tcp_raw(source, host->target->v4hostip(), ttl, - get_random_u16(), get_random_u8(), false, NULL, 0, - token ^ global_id, pspec.pd.tcp.dport, get_random_u32(), ack, 0x00, - pspec.pd.tcp.flags, get_random_u16(), 0, (const u8 *) tcpopts, tcpoptslen, - o.extra_payload, o.extra_payload_length, len); + if (source->ss_family == AF_INET) { + const struct sockaddr_in *sin = (struct sockaddr_in *) source; + return build_tcp_raw(&sin->sin_addr, host->target->v4hostip(), ttl, + get_random_u16(), get_random_u8(), false, NULL, 0, + token ^ global_id, pspec.pd.tcp.dport, get_random_u32(), ack, 0x00, + pspec.pd.tcp.flags, get_random_u16(), 0, (const u8 *) tcpopts, tcpoptslen, + o.extra_payload, o.extra_payload_length, len); + } else if (source->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) source; + return build_tcp_raw_ipv6(&sin6->sin6_addr, host->target->v6hostip(), + 0, 0, ttl, + token ^ global_id, pspec.pd.tcp.dport, get_random_u32(), ack, 0x00, + pspec.pd.tcp.flags, get_random_u16(), 0, (const u8 *) tcpopts, tcpoptslen, + o.extra_payload, o.extra_payload_length, len); + } else { + fatal("Unknown address family %u in %s.", source->ss_family, __func__); + } } }; @@ -695,14 +733,18 @@ public: UDPProbe(HostState *host, struct probespec pspec, u8 ttl) : Probe(host, pspec, ttl) { } - unsigned char *build_packet(const struct in_addr *source, u32 *len) const { + unsigned char *build_packet(const struct sockaddr_storage *source, u32 *len) const { const char *payload; size_t payload_length; + const struct sockaddr_in *sin; + + assert(source->ss_family == AF_INET); + sin = (struct sockaddr_in *) source; payload = get_udp_payload(pspec.pd.udp.dport, &payload_length); /* For UDP we encode the token in the source port. */ - return build_udp_raw(source, host->target->v4hostip(), ttl, + return build_udp_raw(&sin->sin_addr, host->target->v4hostip(), ttl, get_random_u16(), get_random_u8(), false, NULL, 0, token ^ global_id, pspec.pd.udp.dport, payload, payload_length, len); @@ -715,12 +757,16 @@ public: SCTPProbe(HostState *host, struct probespec pspec, u8 ttl) : Probe(host, pspec, ttl) { } - unsigned char *build_packet(const struct in_addr *source, u32 *len) const { + unsigned char *build_packet(const struct sockaddr_storage *source, u32 *len) const { struct sctp_chunkhdr_init chunk; + const struct sockaddr_in *sin; + + assert(source->ss_family == AF_INET); + sin = (struct sockaddr_in *) source; sctp_pack_chunkhdr_init(&chunk, SCTP_INIT, 0, sizeof(chunk), get_random_u32() /*itag*/, 32768, 10, 2048, get_random_u32() /*itsn*/); - return build_sctp_raw(source, host->target->v4hostip(), ttl, + return build_sctp_raw(&sin->sin_addr, host->target->v4hostip(), ttl, get_random_u16(), get_random_u8(), false, NULL, 0, token ^ global_id, pspec.pd.sctp.dport, 0UL, (char *) &chunk, sizeof(chunk), @@ -734,14 +780,34 @@ public: IPProtoProbe(HostState *host, struct probespec pspec, u8 ttl) : Probe(host, pspec, ttl) { } - unsigned char *build_packet(const struct in_addr *source, u32 *len) const { + unsigned char *build_packet(const struct sockaddr_storage *source, u32 *len) const { + const struct sockaddr_in *sin; + assert(source->ss_family == AF_INET); + sin = (struct sockaddr_in *) source; /* For IP proto scan the token is put in the IP ID. */ - return build_ip_raw(source, host->target->v4hostip(), pspec.proto, ttl, + return build_ip_raw(&sin->sin_addr, host->target->v4hostip(), pspec.proto, ttl, token ^ global_id, get_random_u8(), false, NULL, 0, o.extra_payload, o.extra_payload_length, len); } }; +class ICMPv6Probe : public Probe +{ +public: + ICMPv6Probe(HostState *host, struct probespec pspec, u8 ttl) + : Probe(host, pspec, ttl) { + } + + unsigned char *build_packet(const struct sockaddr_storage *source, u32 *len) const { + const struct sockaddr_in6 *sin6; + assert(source->ss_family == AF_INET6); + sin6 = (struct sockaddr_in6 *) source; + return build_icmpv6_raw(&sin6->sin6_addr, host->target->v6hostip(), 0x00, 0x0000, + ttl, token, global_id, pspec.pd.icmp.type, pspec.pd.icmp.code, + o.extra_payload, o.extra_payload_length, len); + } +}; + Probe *Probe::make(HostState *host, struct probespec pspec, u8 ttl) { if (pspec.type == PS_ICMP || (pspec.type == PS_PROTO && pspec.proto == IPPROTO_ICMP)) @@ -754,6 +820,8 @@ Probe *Probe::make(HostState *host, struct probespec pspec, u8 ttl) return new SCTPProbe(host, pspec, ttl); else if (pspec.type == PS_PROTO) return new IPProtoProbe(host, pspec, ttl); + else if (pspec.type == PS_ICMPV6) + return new ICMPv6Probe(host, pspec, ttl); else fatal("Unknown probespec type in traceroute"); @@ -776,7 +844,7 @@ TracerouteState::TracerouteState(std::vector &targets) { rawsd = -1; } else { #ifdef WIN32 - win32_warn_raw_sockets(targets[0]->deviceName()); + win32_fatal_raw_sockets(targets[0]->deviceName()); #endif rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (rawsd == -1) @@ -793,7 +861,7 @@ TracerouteState::TracerouteState(std::vector &targets) { fatal("%s", PCAP_OPEN_ERRMSG); sslen = sizeof(srcaddr); targets[0]->SourceSockAddr(&srcaddr, &sslen); - n = Snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s", + n = Snprintf(pcap_filter, sizeof(pcap_filter), "(ip or ip6) and dst host %s", ss_to_string(&srcaddr)); assert(n < (int) sizeof(pcap_filter)); set_pcap_filter(targets[0]->deviceFullName(), pd, pcap_filter); @@ -1024,113 +1092,81 @@ struct Reply { u16 token; }; -static const void *ip_get_data(const struct ip *ip, unsigned int *len) -{ - unsigned int header_len; +static bool parse_encapsulated_reply(const void *ip, unsigned len, Reply *reply) { + struct abstract_ip_hdr hdr; + const void *data; - header_len = ip->ip_hl * 4; - if (header_len > *len) - return NULL; - *len -= header_len; + data = ip_get_data(ip, &len, &hdr); + if (data == NULL) + return false; - return (char *) ip + header_len; -} - -static const void *icmp_get_data(const struct icmp *icmp, unsigned int *len) -{ - unsigned int header_len; - - if (icmp->icmp_type == ICMP_TIMEXCEED || icmp->icmp_type == ICMP_UNREACH) - header_len = 8; - else - 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; -} - -static bool parse_encapsulated_reply(const struct ip *ip, unsigned int len, - Reply *reply) { - sockaddr_in *addr_in; - - if (ip->ip_p == IPPROTO_ICMP) { - const struct icmp *icmp = (struct icmp *) ip_get_data(ip, &len); - if (icmp == NULL || len < 8 || ntohs(icmp->icmp_id) != global_id) + if (hdr.version == 4 && hdr.proto == IPPROTO_ICMP) { + const struct icmp *icmp = (const struct icmp *) data; + if (len < 8 || ntohs(icmp->icmp_id) != global_id) return false; reply->token = ntohs(icmp->icmp_seq); - } else if (ip->ip_p == IPPROTO_TCP) { - const struct tcp_hdr *tcp = (struct tcp_hdr *) ip_get_data(ip, &len); - if (tcp == NULL || len < 2) + } else if (hdr.version == 6 && hdr.proto == IPPROTO_ICMPV6) { + const struct icmpv6_msg_echo *echo = (struct icmpv6_msg_echo *) ((char *) data + sizeof(struct icmpv6_hdr)); + if (len < 8 || ntohs(echo->icmpv6_id) != global_id) + return false; + reply->token = ntohs(echo->icmpv6_seq); + } else if (hdr.proto == IPPROTO_TCP) { + const struct tcp_hdr *tcp = (const struct tcp_hdr *) data; + if (len < 2) return false; reply->token = ntohs(tcp->th_sport) ^ global_id; - } else if (ip->ip_p == IPPROTO_UDP) { - const struct udp_hdr *udp = (struct udp_hdr *) ip_get_data(ip, &len); - if (udp == NULL || len < 2) + } else if (hdr.proto == IPPROTO_UDP) { + const struct udp_hdr *udp = (const struct udp_hdr *) data; + if (len < 2) return false; reply->token = ntohs(udp->uh_sport) ^ global_id; - } else if (ip->ip_p == IPPROTO_SCTP) { - const struct sctp_hdr *sctp = (struct sctp_hdr *) ip_get_data(ip, &len); - if (sctp == NULL || len < 2) + } else if (hdr.proto == IPPROTO_SCTP) { + const struct sctp_hdr *sctp = (const struct sctp_hdr *) data; + if (len < 2) return false; reply->token = ntohs(sctp->sh_sport) ^ global_id; } else { if (len < 6) return false; /* Check IP ID for proto scan. */ - reply->token = ntohs(ip->ip_id) ^ global_id; + reply->token = hdr.ipid ^ global_id; } - addr_in = (struct sockaddr_in *) &reply->target_addr; - addr_in->sin_family = AF_INET; - addr_in->sin_addr = ip->ip_dst; + reply->target_addr = hdr.dst; return true; } -static bool read_reply(Reply *reply, pcap_t *pd, long timeout) { - const struct ip *ip; - unsigned int iplen; - struct link_header linkhdr; - sockaddr_in *addr_in; +static bool decode_reply(const void *ip, unsigned int len, Reply *reply) { + struct abstract_ip_hdr hdr; + const void *data; - ip = (struct ip *) readip_pcap(pd, &iplen, timeout, &reply->rcvdtime, - &linkhdr, true); - if (ip == NULL) - return false; - if (ip->ip_v != 4) + data = ip_get_data(ip, &len, &hdr); + if (data == NULL) return false; - addr_in = (struct sockaddr_in *) &reply->from_addr; - addr_in->sin_family = AF_INET; - addr_in->sin_addr = ip->ip_src; + reply->from_addr = hdr.src; - reply->ttl = ip->ip_ttl; - - if (ip->ip_p == IPPROTO_ICMP) { + if (hdr.version == 4 && hdr.proto == IPPROTO_ICMP) { /* ICMP responses comprise all the TTL exceeded messages we expect from all probe types, as well as actual replies from ICMP probes. */ - const struct icmp *icmp; - unsigned int icmplen; - - icmplen = iplen; - icmp = (struct icmp *) ip_get_data(ip, &icmplen); - if (icmp == NULL || icmplen < 8) + const struct icmp_hdr *icmp = (const struct icmp_hdr *) data; + if (len < 8) return false; if ((icmp->icmp_type == ICMP_TIMEXCEED && icmp->icmp_code == ICMP_TIMEXCEED_INTRANS) || icmp->icmp_type == ICMP_UNREACH) { - /* Get the encapsulated ICMP packet. */ - iplen = icmplen; - ip = (struct ip *) icmp_get_data(icmp, &iplen); - if (ip == NULL || ip->ip_v != 4 || iplen < 20) - return false; - if (!parse_encapsulated_reply(ip, iplen, reply)) + /* Get the encapsulated IP packet. */ + const void *encaps = icmp_get_data(icmp, &len); + if (encaps == NULL) return false; + return parse_encapsulated_reply(encaps, len, reply); } else if (icmp->icmp_type == ICMP_ECHOREPLY || icmp->icmp_type == ICMP_MASKREPLY || icmp->icmp_type == ICMP_TSTAMPREPLY) { + /* Need this alternate form of header for icmp_id and icmp_seq. */ + const struct icmp *icmp = (const struct icmp *) data; + if (ntohs(icmp->icmp_id) != global_id) return false; reply->token = ntohs(icmp->icmp_seq); @@ -1139,33 +1175,51 @@ static bool read_reply(Reply *reply, pcap_t *pd, long timeout) { } else { return false; } - } else if (ip->ip_p == IPPROTO_TCP) { - const struct tcp_hdr *tcp; - unsigned int tcplen; + } else if (hdr.version == 6 && hdr.proto == IP_PROTO_ICMPV6) { + /* ICMPv6 responses comprise all the TTL exceeded messages we expect from + all probe types, as well as actual replies from ICMP probes. */ + const struct icmpv6_hdr *icmpv6 = (const struct icmpv6_hdr *) data; + if (len < 2) + return false; + /* TIMEXCEED, UNREACH */ + if ((icmpv6->icmpv6_type == ICMPV6_TIMEXCEED + && icmpv6->icmpv6_code == ICMPV6_TIMEXCEED_INTRANS) + || icmpv6->icmpv6_type == ICMPV6_UNREACH) { + /* Get the encapsulated IP packet. */ + const void *encaps = icmpv6_get_data(icmpv6, &len); + if (encaps == NULL) + return false; + return parse_encapsulated_reply(encaps, len, reply); + } else if (icmpv6->icmpv6_type == ICMPV6_ECHOREPLY) { + /* MASKREPLY, TSTAMPREPLY */ + const struct icmpv6_msg_echo *echo; - tcplen = iplen; - tcp = (struct tcp_hdr *) ip_get_data(ip, &tcplen); - if (tcp == NULL || tcplen < 4) + if (len < sizeof(*icmpv6) + 4) + return false; + echo = (struct icmpv6_msg_echo *) ((char *) icmpv6 + sizeof(*icmpv6)); + if (ntohs(echo->icmpv6_id) != global_id) + return false; + reply->token = ntohs(echo->icmpv6_seq); + /* Reply came directly from the target. */ + reply->target_addr = reply->from_addr; + } else { + return false; + } + } else if (hdr.proto == IPPROTO_TCP) { + const struct tcp_hdr *tcp = (const struct tcp_hdr *) data; + if (len < 4) return false; reply->token = ntohs(tcp->th_dport) ^ global_id; reply->target_addr = reply->from_addr; - } else if (ip->ip_p == IPPROTO_UDP) { - const struct udp_hdr *udp; - unsigned int udplen; - - udplen = iplen; - udp = (struct udp_hdr *) ip_get_data(ip, &udplen); - if (udp == NULL || udplen < 4) + } else if (hdr.proto == IPPROTO_UDP) { + const struct udp_hdr *udp = (const struct udp_hdr *) data; + if (len < 4) return false; reply->token = ntohs(udp->uh_dport) ^ global_id; reply->target_addr = reply->from_addr; - } else if (ip->ip_p == IPPROTO_SCTP) { - const struct sctp_hdr *sctp; - unsigned int sctplen; - - sctplen = iplen; - sctp = (struct sctp_hdr *) ip_get_data(ip, &sctplen); - if (sctp == NULL || sctplen < 4) + } else if (hdr.proto == IPPROTO_SCTP) { + const struct sctp_hdr *sctp = (const struct sctp_hdr *) data; + if (len < 4) return false; reply->token = ntohs(sctp->sh_dport) ^ global_id; reply->target_addr = reply->from_addr; @@ -1176,6 +1230,20 @@ static bool read_reply(Reply *reply, pcap_t *pd, long timeout) { return true; } +static bool read_reply(Reply *reply, pcap_t *pd, long timeout) { + const struct ip *ip; + unsigned int iplen; + struct link_header linkhdr; + + ip = (struct ip *) readip46_pcap(pd, &iplen, timeout, &reply->rcvdtime, &linkhdr, true); + if (ip == NULL) + return false; + if (ip->ip_v == 4 || ip->ip_v == 6) + return decode_reply(ip, iplen, reply); + else + return false; +} + void TracerouteState::read_replies(long timeout) { struct sockaddr_storage ss; struct timeval now; @@ -1265,12 +1333,19 @@ void TracerouteState::remove_finished_hosts() { } } +/* Dummy class to use sockaddr_storage as a map key. */ +struct lt_sockaddr_storage { + bool operator()(const struct sockaddr_storage& a, const struct sockaddr_storage& b) { + return sockaddr_storage_cmp(&a, &b) < 0; + } +}; + /* Find the reverse-DNS names of the hops. */ void TracerouteState::resolve_hops() { - std::set addrs; - std::set::iterator addr_iter; + std::set addrs; + std::set::iterator addr_iter; std::vector::iterator host_iter; - std::map name_map; + std::map name_map; Target **targets; Hop *hop; int i, n; @@ -1280,9 +1355,8 @@ void TracerouteState::resolve_hops() { inefficient. */ for (host_iter = hosts.begin(); host_iter != hosts.end(); host_iter++) { for (hop = (*host_iter)->hops; hop != NULL; hop = hop->parent) { - struct sockaddr_in *sin = (struct sockaddr_in *) &hop->addr; - if (hop->addr.ss_family == AF_INET) - addrs.insert(sin->sin_addr.s_addr); + if (hop->addr.ss_family != AF_UNSPEC) + addrs.insert(hop->addr); } } n = addrs.size(); @@ -1292,11 +1366,8 @@ void TracerouteState::resolve_hops() { i = 0; addr_iter = addrs.begin(); while (i < n) { - struct sockaddr_in sin; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = *addr_iter; targets[i] = new Target(); - targets[i]->setTargetSockAddr((struct sockaddr_storage *) &sin, sizeof(sin)); + targets[i]->setTargetSockAddr(&*addr_iter, sizeof(*addr_iter)); targets[i]->flags = HOST_UP; i++; addr_iter++; @@ -1304,17 +1375,20 @@ void TracerouteState::resolve_hops() { nmap_mass_rdns(targets, n); /* Third, make a map from addresses to names for easy lookup. */ for (i = 0; i < n; i++) { + struct sockaddr_storage ss; + size_t ss_len; const char *hostname = targets[i]->HostName(); if (*hostname == '\0') hostname = NULL; - name_map[targets[i]->v4hostip()->s_addr] = hostname; + ss_len = sizeof(ss); + targets[i]->TargetSockAddr(&ss, &ss_len); + name_map[ss] = hostname; } /* Finally, copy the names into the hops. */ for (host_iter = hosts.begin(); host_iter != hosts.end(); host_iter++) { for (hop = (*host_iter)->hops; hop != NULL; hop = hop->parent) { - struct sockaddr_in *sin = (struct sockaddr_in *) &hop->addr; - if (hop->addr.ss_family == AF_INET) { - const char *hostname = name_map[sin->sin_addr.s_addr]; + if (hop->addr.ss_family != AF_UNSPEC) { + const char *hostname = name_map[hop->addr]; if (hostname != NULL) hop->hostname = hostname; }