diff --git a/CHANGELOG b/CHANGELOG index ef4fa504a..91c255db8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,24 @@ [NOT YET RELEASED] +o [NSE] Raw packet sending at the IP layer is now supported, in addition to + the Ethernet sending functionality. Packets to send start with an IPv4 + header and can be sent to arbitrary hosts. [Kris] + +o [NSE] Added the ipidseq script to classify a host's IP ID sequence numbers + in the same way Nmap does. This can be used to test hosts' suitability for + Nmap's Idle Scan (-sI), i.e. check if a host is an idle zombie. This is + the first script to use the new raw IP sending functionality in NSE. [Kris] + +o [NSE] Added the function nmap.is_privileged() to tell a script if, as far + as Nmap's concerned, it can do privileged operations. For instance, this + can be used to see if a script should be able to open a raw socket or + Ethernet interface. [Kris] + +o [NSE] Added the function nmap.get_ports() to allow a script to iterate + over a host's port tables matching a certain protocol and state. [Kris, + Patrick] + o [Ncat] Fixed a handle leak with --exec and --sh-exec on Windows, found by Jon Greaves. One thread handle was being leaked per child process invocation. [David] diff --git a/docs/scripting.xml b/docs/scripting.xml index 012715a7c..fc0ece2e0 100644 --- a/docs/scripting.xml +++ b/docs/scripting.xml @@ -1379,7 +1379,7 @@ LUALIB_API int luaopen_openssl(lua_State *L) { The port table is passed to an NSE service script (i.e. only those with a portrule rather than a hostrule) in the same fashion as the host table. It contains information about the port against which the script is running. While this table is not passed to host scripts, port states on the target can still be requested from Nmap - using the nmap.get_port_state() call. + using the nmap.get_port_state() and nmap.get_ports() calls. @@ -1509,8 +1509,8 @@ LUALIB_API int luaopen_openssl(lua_State *L) { port.state generally contains one of those values. Other values might appear if the port table is a result of the - get_port_state function. You can - adjust the port state using the + get_port_state or get_ports + functions. You can adjust the port state using the nmap.set_port_state() call. This is normally done when an open|filtered port is determined to be open. @@ -1624,24 +1624,33 @@ socket:close() with socket_object:close—just like with the connection-based network I/O. - - Receiving raw packets is important, but sending them is a key feature as well. To accomplish this, NSE can - access a wrapper around the - libdnet library.libdnet Raw packet writes do not - use a standard socket object like reads do. Instead, call the function - nmap.new_dnet to create a dnet object - with ethernet sending methods. Then open an interface with the - ethernet_open method. Raw ethernet - frames can then be sent - with ethernet_send. When you're done, - close the ethernet handle - with ethernet_close. + While receiving packets is important, sending them is certainly + a key feature as well. To accomplish this, NSE provides access to + sending at the IP and Ethernet layers. Raw packet writes do not use + the same socket object as raw packet reads, so the nmap.new_dnet + function is called to create the required object for sending. After + this, a raw socket or Ethernet interface handle can be opened for use. - Sometimes the easiest ways to understand complex APIs - is by example. The sniffer-detect.nse - script included with Nmap uses raw packet capture and - sending in an attempt to detect promiscuous-mode machines on - the network (those running sniffers). + Once the dnet object is created, the function ip_open + can be called to initialize the object for IP sending. ip_send + sends the actual raw packet, which must start with the IPv4 header. + The dnet object places no restrictions on which IP hosts may be sent + to, so the same object may be used to send to many different hosts + while it is open. To close the raw socket, call ip_close. + + For sending at a lower level than IP, NSE provides functions for + writing Ethernet frames. ethernet_open initializes + the dnet object for sending by opening an Ethernet interface. The raw + frame is sent with ethernet_send. To close the + handle, call ethernet_close. + + Sometimes the easiest ways to understand complex APIs is by + example. The ipidseq.nse script included with + Nmap uses raw IP packets to test hosts for suitability for Nmap's + Idle Scan (). The sniffer-detect.nse + script also included with Nmap uses raw Ethernet frames in an attempt + to detect promiscuous-mode machines on the network (those running + sniffers). diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc index fa2c48358..3bb83c946 100644 --- a/nse_nmaplib.cc +++ b/nse_nmaplib.cc @@ -448,6 +448,34 @@ Port *get_port (lua_State *L, Target *target, Port *port, int index) return p; } +/* Generates an array of port data for the given host and leaves it on + * the top of the stack + */ +static int l_get_ports (lua_State *L) +{ + static const char *op1[] = {"tcp", "udp", "sctp", NULL}; + static const int states1[] = {IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP}; + static const char *op2[] = {"open", "filtered", "unfiltered", "closed", + "open|filtered", "closed|filtered", NULL}; + static const int states2[] = {PORT_OPEN, PORT_FILTERED, PORT_UNFILTERED, + PORT_CLOSED, PORT_OPENFILTERED, PORT_CLOSEDFILTERED}; + Port *p = NULL, port; + Target *target = get_target(L, 1); + int protocol = states1[luaL_checkoption(L, 3, NULL, op1)]; + int state = states2[luaL_checkoption(L, 4, NULL, op2)]; + + if (!lua_isnil(L, 2)) + p = get_port(L, target, &port, 2); + + if (!(p = target->ports.nextPort(p, &port, protocol, state))) { + lua_pushnil(L); + } else { + lua_newtable(L); + set_portinfo(L, target, p); + } + return 1; +} + /* this function can be called from lua to obtain the port state * of a port different from the one the script rule is matched * against @@ -658,10 +686,17 @@ static int l_get_dns_servers (lua_State *L) return 1; } +static int l_is_privileged(lua_State *L) +{ + lua_pushboolean(L, o.isr00t); + return 1; +} + int luaopen_nmap (lua_State *L) { static const luaL_reg nmaplib [] = { {"get_port_state", l_get_port_state}, + {"get_ports", l_get_ports}, {"set_port_state", l_set_port_state}, {"set_port_version", l_set_port_version}, {"new_socket", l_nsock_new}, @@ -676,6 +711,7 @@ int luaopen_nmap (lua_State *L) {"fetchfile", l_fetchfile}, {"timing_level", l_get_timing_level}, {"get_dns_servers", l_get_dns_servers}, + {"is_privileged", l_is_privileged}, {NULL, NULL} }; diff --git a/nse_nsock.cc b/nse_nsock.cc index 90b0cbfaf..0907beaf0 100644 --- a/nse_nsock.cc +++ b/nse_nsock.cc @@ -1876,15 +1876,19 @@ int l_get_ssl_certificate(lua_State *L) /****************** DNET ******************************************************/ static int l_dnet_open_ethernet(lua_State * L); - static int l_dnet_close_ethernet(lua_State * L); - static int l_dnet_send_ethernet(lua_State * L); +static int l_dnet_open_ip(lua_State * L); +static int l_dnet_close_ip(lua_State * L); +static int l_dnet_send_ip(lua_State * L); static luaL_reg l_dnet[] = { {"ethernet_open", l_dnet_open_ethernet}, {"ethernet_close", l_dnet_close_ethernet}, {"ethernet_send", l_dnet_send_ethernet}, + {"ip_open", l_dnet_open_ip}, + {"ip_close", l_dnet_close_ip}, + {"ip_send", l_dnet_send_ip}, {NULL, NULL} }; @@ -1903,6 +1907,7 @@ struct l_dnet_udata { char *interface; eth_t *eth; + int sock; // raw ip socket }; int l_dnet_new(lua_State * L) @@ -1915,6 +1920,7 @@ int l_dnet_new(lua_State * L) lua_setmetatable(L, -2); udata->interface = NULL; udata->eth = NULL; + udata->sock = -1; return 1; } @@ -2055,3 +2061,147 @@ static int l_dnet_send_ethernet(lua_State * L) eth_send(udata->eth, packet, packetsz); return 0; } + +static int l_dnet_open_ip(lua_State * L) +{ + l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); + + udata->sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + + if (udata->sock == -1) { + lua_pushboolean(L, false); + lua_pushfstring(L, "Failed to open raw socket: %s (errno %d)", socket_strerror(errno), errno); + return 2; + } + + broadcast_socket(udata->sock); +#ifndef WIN32 + sethdrinclude(udata->sock); +#endif + + lua_pushboolean(L, true); + return 1; +} + +static int l_dnet_close_ip(lua_State * L) +{ + l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); + + if (udata->sock == -1) { + lua_pushboolean(L, false); + lua_pushstring(L, "Raw socket all ready closed"); + return 2; + } + + close(udata->sock); + + if (udata->eth && udata->interface) { + ldnet_eth_close_cached(udata->interface); + free(udata->interface); + udata->eth = NULL; + udata->interface = NULL; + } + + lua_pushboolean(L, true); + return 1; +} + +static int l_dnet_send_ip(lua_State * L) +{ + l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); + size_t packetsz = 0; + const char *packet = luaL_checklstring(L, 2, &packetsz); + char dev[16]; + int ret; + + if (udata->sock == -1) { + lua_pushboolean(L, false); + lua_pushstring(L, "Raw socket not open to send"); + return 2; + } + + if (packetsz < sizeof(struct ip)) { + lua_pushboolean(L, false); + lua_pushstring(L, "Won't send: IP packet too short"); + return 2; + } + + *dev = 0; + + if ((o.sendpref & PACKET_SEND_ETH)) { + struct route_nfo route; + struct sockaddr_storage srcss, dstss, *nexthop; + struct sockaddr_in *srcsin = (struct sockaddr_in *) &srcss; + struct sockaddr_in *dstsin = (struct sockaddr_in *) &dstss; + struct ip *ip = (struct ip *) packet; + u8 dstmac[6]; + eth_nfo eth; + + /* build sockaddr for target from user packet and determine route */ + memset(&dstss, 0, sizeof(dstss)); + dstsin->sin_family = AF_INET; + dstsin->sin_addr.s_addr = ip->ip_dst.s_addr; + + if (!route_dst(&dstss, &route)) + goto usesock; + + strncpy(dev, route.ii.devname, sizeof(dev)); + + if (route.ii.device_type != devt_ethernet) + goto usesock; + + /* above we fallback to using the raw socket if we can't find an (ethernet) + * route to the host. From here on out it's ethernet all the way. + */ + + /* build sockaddr for source from user packet to determine next hop mac */ + memset(&srcss, 0, sizeof(srcss)); + srcsin->sin_family = AF_INET; + srcsin->sin_addr.s_addr = ip->ip_src.s_addr; + + if (route.direct_connect) + nexthop = &dstss; + else + nexthop = &route.nexthop; + + if (!getNextHopMAC(route.ii.devfullname, route.ii.mac, &srcss, nexthop, dstmac)) { + lua_pushboolean(L, false); + lua_pushstring(L, "Failed to determine next hop MAC address"); + return 2; + } + + /* Use cached ethernet device, and use udata's eth and interface to keep + * track of if we're reusing the same device from the previous packet, and + * close the cached device if not. + */ + memset(ð, 0, sizeof(eth)); + memcpy(ð.srcmac, route.ii.mac, sizeof(eth.srcmac)); + memcpy(ð.dstmac, dstmac, sizeof(eth.dstmac)); + eth.ethsd = ldnet_eth_open_cached(dev); + if (!udata->eth) { + udata->eth = eth.ethsd; + udata->interface = strdup(route.ii.devname); + } else if (udata->eth != eth.ethsd) { + ldnet_eth_close_cached(udata->interface); + free(udata->interface); + udata->eth = eth.ethsd; + udata->interface = strdup(route.ii.devname); + } + ret = send_ip_packet(udata->sock, ð, (u8 *) packet, packetsz); + } else { +usesock: +#ifdef WIN32 + if (strlen(dev)) + win32_warn_raw_sockets(dev); +#endif + ret = send_ip_packet(udata->sock, NULL, (u8 *) packet, packetsz); + } + if (ret == -1) { + lua_pushboolean(L, false); + lua_pushfstring(L, "Error while sending: %s (errno %d)", socket_strerror(errno), errno); + return 2; + } + lua_pushboolean(L, true); + return 1; +} + diff --git a/nselib/nmap.luadoc b/nselib/nmap.luadoc index 48c720117..3e6604f83 100644 --- a/nselib/nmap.luadoc +++ b/nselib/nmap.luadoc @@ -31,6 +31,11 @@ function have_ssl() -- @usage if nmap.verbosity() > 0 then ... end function verbosity() +--- Returns whether a script should be able to perform privileged operations +-- +-- @return True if Nmap is running privileged, false otherwise. +function is_privileged() + --- Searches for the specified file and returns a string containing its path if -- it is found and readable (to the process). -- @@ -73,6 +78,25 @@ function timing_level() -- @usage p = nmap.get_port_state({ip="127.0.0.1"}, {number="80", protocol="tcp"}) function get_port_state(host, port) +--- Iterates over port tables matching protocol and state for a given host +-- +-- This function takes a host table, previous port table, port protocol and +-- port state to return matching port tables on a host. +-- +-- The first time you call this function, pass nil for the port +-- parameter to get the first matching port table. From then on, pass the +-- previous port table returned by this function to the port parameter for the +-- next matching port table. +-- +-- @param host Host table, containing an ip field +-- @param port Port table, containing a number field; or nil +-- for first port +-- @param proto Port protocol, such as "tcp" +-- @param state Port state, such as "open" +-- @return Next port table for host, or nil when exhausted +-- @usage port = nmap.get_ports(host, port, "tcp", "open") +function get_ports(host, port, proto, state) + --- Sets the state of a port on a given host. -- -- Using this function, the final port state, reflected in Nmap's results, can @@ -630,6 +654,26 @@ function ethernet_send(packet) -- @usage dnet:ethernet_close() function ethernet_close() +--- Opens a socket for raw IPv4 packet sending. +-- @see new_dnet +-- @usage dnet:ip_open() +function ip_open() + +--- Sends a raw IPv4 packet. +-- +-- The dnet object must be associated with a previously opened socket. The +-- packet must begin with an IP header. If there was no previous valid call +-- to ip_open an error is thrown. +-- @param packet An IP packet to send. +-- @see new_dnet +-- @usage dnet:ip_send(packet) +function ip_send(packet) + +--- Closes a raw IPv4 socket. +-- @see new_dnet, ip_open +-- @usage dnet:ip_close() +function ip_close() + --- Writes to a log file. -- -- Writes string to file ("stdout" or "stderr"). diff --git a/scripts/ipidseq.nse b/scripts/ipidseq.nse new file mode 100644 index 000000000..7f85995d3 --- /dev/null +++ b/scripts/ipidseq.nse @@ -0,0 +1,247 @@ +description = [[ +Classifies a host's IP ID sequence (e.g. test for Idle Scan suitability). + +Sends six probes to obtain IP IDs from the target and classifies them +similiarly to Nmap's method. This is useful for finding suitable zombies +for Nmap's Idle Scan (-sI) as Nmap itself doesn't provide a way to scan +*for* these hosts. +]] + +--- +-- @usage +-- nmap --script ipidseq [--script-args probeport=port] target +-- @args probeport Set destination port to probe +-- @output +-- Host script results: +-- |_ipidseq: Incremental! [used port 80] + +-- I also implemented this in Metasploit as auxiliary/scanner/ip/ipidseq, but +-- this NSE script was actually written first (unfortunately it only worked +-- with vanilla Nmap using dnet ethernet sending.. ugh) +-- +-- Originally written 05/24/2008; revived 01/24/2010 + +author = "Kris Katterjohn " + +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" + +categories = {"safe", "discovery"} + +require 'bin' +require 'packet' + +local NUMPROBES = 6 + +--- Pcap callback +-- @return Destination and source IP addresses and TCP ports +local callback = function(size, layer2, layer3) + local ip = packet.Packet:new(layer3, layer3:len()) + return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport) +end + +--- Updates a TCP Packet object +-- @param tcp The TCP object +local updatepkt = function(tcp) + tcp:tcp_set_sport(math.random(0x401, 0xffff)) + tcp:tcp_set_seq(math.random(1, 0x7fffffff)) + tcp:tcp_count_checksum(tcp.ip_len) + tcp:ip_count_checksum() +end + +--- Create a TCP Packet object +-- @param host Host object +-- @param port Port number +-- @return TCP Packet object +local genericpkt = function(host, port) + local pkt = bin.pack("H", + "4500 002c 55d1 0000 8006 0000 0000 0000" .. + "0000 0000 0000 0000 0000 0000 0000 0000" .. + "6002 0c00 0000 0000 0204 05b4" + ) + + local tcp = packet.Packet:new(pkt, pkt:len()) + + tcp:ip_set_bin_src(host.bin_ip_src) + tcp:ip_set_bin_dst(host.bin_ip) + tcp:tcp_set_dport(port) + + updatepkt(tcp) + + return tcp +end + +--- Classifies a series of IP ID numbers like get_ipid_sequence() in osscan2.cc +-- @param ipids Table of IP IDs +local ipidseqclass = function(ipids) + local diffs = {} + local allzeros = true + local allsame = true + local mul256 = true + local inc = true + + if #ipids < 2 then + return "Unknown" + end + + local i = 2 + + while i <= #ipids do + if ipids[i-1] ~= 0 or ipids[i] ~= 0 then + allzeros = false + end + + if ipids[i-1] <= ipids[i] then + diffs[i-1] = ipids[i] - ipids[i-1] + else + diffs[i-1] = ipids[i] - ipids[i-1] + 65536 + end + + if #ipids > 2 and diffs[i-1] > 20000 then + return "Randomized" + end + + i = i + 1 + end + + if allzeros then + return "All zeros" + end + + i = 1 + + while i <= #diffs do + if diffs[i] ~= 0 then + allsame = false + end + + if (diffs[i] > 1000) and ((diffs[i] % 256) ~= 0 or + ((diffs[i] % 256) == 0 and diffs[i] > 25600)) then + return "Random Positive Increments" + end + + if diffs[i] > 5120 or (diffs[i] % 256) ~= 0 then + mul256 = false + end + + if diffs[i] >= 10 then + inc = false + end + + i = i + 1 + end + + if allsame then + return "Constant" + end + + if mul256 then + return "Broken incremental!" + end + + if inc then + return "Incremental!" + end + + return "Unknown" +end + +--- Determines what port to probe +-- @param host Host object +local getport = function(host) + for _, k in ipairs({"ipidseq.probeport", "probeport"}) do + if nmap.registry.args[k] then + return tonumber(nmap.registry.args[k]) + end + end + + --local states = { "open", "closed", "unfiltered", "open|filtered", "closed|filtered" } + local states = { "open", "closed" } + local port = nil + + for _, s in ipairs(states) do + port = nmap.get_ports(host, nil, "tcp", s) + if port then + break + end + end + + if not port then + return nil + end + + return port.number +end + +--- Sets probe port number in registry +-- @param host Host object +-- @param port Port number +local setreg = function(host, port) + if not nmap.registry[host.ip] then + nmap.registry[host.ip] = {} + end + nmap.registry[host.ip]['ipidseqprobe'] = port +end + +hostrule = function(host) + if not nmap.is_privileged() then + return false + end + if not host.interface then + return false + end + local port = getport(host) + if not port then + return false + end + setreg(host, port) + return true +end + +action = function(host) + local i = 1 + local ipids = {} + local sock = nmap.new_dnet() + local pcap = nmap.new_socket() + local port = nmap.registry[host.ip]['ipidseqprobe'] + local saddr = packet.toip(host.bin_ip_src) + local daddr = packet.toip(host.bin_ip) + local try = nmap.new_try() + + try(sock:ip_open()) + + try = nmap.new_try(function() sock:ip_close() end) + + pcap:pcap_open(host.interface, 104, 0, callback, "tcp and dst host " .. saddr .. " and src host " .. daddr .. " and src port " .. port) + + pcap:set_timeout(3000) + + local tcp = genericpkt(host, port) + + while i <= NUMPROBES do + try(sock:ip_send(tcp.buf)) + + pcap:pcap_register(bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)) + + local status, len, _, pkt = pcap:pcap_receive() + + if status then + table.insert(ipids, packet.u16(pkt, 4)) + end + + updatepkt(tcp) + + i = i + 1 + end + + pcap:close() + sock:ip_close() + + local output = ipidseqclass(ipids) + + if nmap.debugging() > 0 then + output = output .. " [used port " .. port .. "]" + end + + return output +end + diff --git a/scripts/script.db b/scripts/script.db index 4112ad15e..76404a2b6 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -38,6 +38,7 @@ Entry { filename = "http-userdir-enum.nse", categories = { "discovery", "intrusi Entry { filename = "http-vmware-path-vuln.nse", categories = { "default", "safe", "vuln", } } Entry { filename = "iax2-version.nse", categories = { "version", } } Entry { filename = "imap-capabilities.nse", categories = { "default", "safe", } } +Entry { filename = "ipidseq.nse", categories = { "discovery", "safe", } } Entry { filename = "irc-info.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "ldap-brute.nse", categories = { "auth", "intrusive", } } Entry { filename = "ldap-rootdse.nse", categories = { "discovery", "safe", } } @@ -79,7 +80,7 @@ Entry { filename = "smb-server-stats.nse", categories = { "discovery", "intrusiv Entry { filename = "smb-system-info.nse", categories = { "discovery", "intrusive", } } Entry { filename = "smbv2-enabled.nse", categories = { "default", "safe", } } Entry { filename = "smtp-commands.nse", categories = { "default", "discovery", "safe", } } -Entry { filename = "smtp-open-relay.nse", categories = { "demo", } } +Entry { filename = "smtp-open-relay.nse", categories = { "discovery", "external", "intrusive", } } Entry { filename = "smtp-strangeport.nse", categories = { "malware", "safe", } } Entry { filename = "sniffer-detect.nse", categories = { "discovery", "intrusive", } } Entry { filename = "snmp-brute.nse", categories = { "auth", "intrusive", } } @@ -95,6 +96,7 @@ Entry { filename = "sql-injection.nse", categories = { "intrusive", "vuln", } } Entry { filename = "ssh-hostkey.nse", categories = { "default", "intrusive", "safe", } } Entry { filename = "sshv1.nse", categories = { "default", "safe", } } Entry { filename = "ssl-cert.nse", categories = { "discovery", "safe", } } +Entry { filename = "ssl-enum-ciphers.nse", categories = { "discovery", "intrusive", } } Entry { filename = "sslv2.nse", categories = { "default", "safe", } } Entry { filename = "telnet-brute.nse", categories = { "auth", "intrusive", } } Entry { filename = "upnp-info.nse", categories = { "default", "safe", } } diff --git a/tcpip.cc b/tcpip.cc index 8fb7d0494..921c550a4 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -2835,6 +2835,37 @@ bool setTargetNextHopMAC(Target *target) { return false; } +/* Like to getTargetNextHopMAC(), but for arbitrary hosts (not Targets) */ +bool getNextHopMAC(char *iface, u8 *srcmac, struct sockaddr_storage *srcss, + struct sockaddr_storage *dstss, u8 *dstmac) +{ + arp_t *a; + struct arp_entry ae; + + /* Nmap's ARP cache */ + if (NmapArpCache(ARPCACHE_GET, dstss, dstmac)) + return true; + + /* System ARP cache */ + a = arp_open(); + addr_ston((sockaddr *) dstss, &ae.arp_pa); + if (arp_get(a, &ae) == 0) { + NmapArpCache(ARPCACHE_SET, dstss, ae.arp_ha.addr_eth.data); + memcpy(dstmac, ae.arp_ha.addr_eth.data, 6); + arp_close(a); + return true; + } + arp_close(a); + + /* Send ARP */ + if (doArp(iface, srcmac, srcss, dstss, dstmac)) { + NmapArpCache(ARPCACHE_SET, dstss, dstmac); + return true; + } + + return false; +} + /* Set a pcap filter */ void set_pcap_filter(const char *device, pcap_t *pd, const char *bpf, ...) { va_list ap; diff --git a/tcpip.h b/tcpip.h index 5db065bc6..da5304574 100644 --- a/tcpip.h +++ b/tcpip.h @@ -703,6 +703,9 @@ int setTargetMACIfAvailable(Target *target, struct link_header *linkhdr, after an ARP scan if many directly connected machines are involved. */ bool setTargetNextHopMAC(Target *target); +bool getNextHopMAC(char *iface, u8 *srcmac, struct sockaddr_storage *srcss, + struct sockaddr_storage *dstss, u8 *dstmac); + int islocalhost(const struct in_addr * const addr); int isipprivate(const struct in_addr * const addr);