massping is the function that sends probes to determine if targets are up before scanning them. Here's how it is called:
massping (targets.cc)
nexthost (targets.cc)
nmap_main (nmap.cc)
main (main.cc)
nmap_main calls nexthost repeatedly to fill up a scan group one by one. nexthost has its own buffer (in hss) of up to 2048 targets that it refills when exhausted. Any time it refills the buffer, it calls massping on the whole buffer of targets. So when nmap_main calls nexthost, it either gets a cached target that has already been pinged, or it causes a brand new round of pings to start and gets the first one from that round. nexthost returns a target regardless of whether it's up or down; nmap_main is responsible for ignoring it if it's down.
static void massping(Target *hostbatch[], int num_hosts,
struct scan_lists *ports, int pingtype);
You might think that ports is the lists of TCP and UDP ports to ping to, à la -PS22,80 -PU, but it's not. ports is never actually used anywhere. It's only mentioned in a comment in get_ping_results.
The ping ports actually come from the global NmapOps o data structure. The relevent members are
int num_ping_synprobes; /* The "synprobes" are also used when doing a connect() ping */ u16 ping_synprobes[MAX_PROBE_PORTS]; int num_ping_ackprobes; u16 ping_ackprobes[MAX_PROBE_PORTS]; int num_ping_udpprobes; u16 ping_udpprobes[MAX_PROBE_PORTS];
pingtype is a bitmap of these values in nmap.h:
#define PINGTYPE_UNKNOWN 0 #define PINGTYPE_NONE 1 #define PINGTYPE_ICMP_PING 2 #define PINGTYPE_ICMP_MASK 4 #define PINGTYPE_ICMP_TS 8 #define PINGTYPE_TCP 16 #define PINGTYPE_TCP_USE_ACK 32 #define PINGTYPE_TCP_USE_SYN 64 #define PINGTYPE_RAWTCP 128 #define PINGTYPE_CONNECTTCP 256 #define PINGTYPE_UDP 512 #define PINGTYPE_ARP 1024
It looks like PINGTYPE_CONNECTTCP is never used. I don't think PINGTYPE_RAWTCP is used either.
HostGroupState
class HostGroupState {
public:
HostGroupState(int lookahead, int randomize, char *target_expressions[],
int num_expressions);
~HostGroupState();
Target **hostbatch;
int max_batch_sz; /* The size of the hostbatch[] array */
int current_batch_sz; /* The number of VALID members of hostbatch[] */
int next_batch_no; /* The index of the next hostbatch[] member to be given
back to the user */
int randomize; /* Whether each batch should be "shuffled" prior to the ping
scan (they will also be out of order when given back one
at a time to the client program */
char **target_expressions; /* An array of target expression strings, passed
to us by the client (client is also in charge
of deleting it AFTER it is done with the
hostgroup_state */
int num_expressions; /* The number of valid expressions in
target_expressions member above */
int next_expression; /* The index of the next expression we have
to handle */
TargetGroup current_expression; /* For batch chunking -- targets in queue */
};
HostGroupState is a list of some number of targets from the current expression, and a list of target expressions (like "192.168.0.0/24" or "scanme.nmap.org"). HostGroupState has a TargetGroup. TargetGroup is a container for an expression, and provides a get_next_host method to get hosts that match an expression one by one.
masspingIn order to reimplement massping, I have to list all of its inputs. Only some of its behavior comes from its parameters.
Targets
pingtype bitmap
u16 ping_synprobes[] (ports from -PS)
u16 ping_ackprobes[] (ports from -PA)
u16 ping_udpprobes[] (ports from -PU)
Is that it? A list of targets, a ping type specification, and lists of ports for the ping types that were specified?
get_ping_results in targets.cc had this, which changes the timeout based on whether we're waiting on ICMP only:
/* Decide on the timeout, based on whether we need to also watch for TCP stuff */
if (ptech->icmpscan && !ptech->rawtcpscan && !ptech->rawudpscan) {
/* We only need to worry about pings, so we set timeout for the whole she-bang! */
myto.tv_sec = to->timeout / 1000000;
myto.tv_usec = to->timeout % 1000000;
} else {
myto.tv_sec = 0;
myto.tv_usec = 20000;
I removed it in the migration. Does ultra_scan handle this situation adequately?
There was and still is a function get_pcap_result that reads packets and uses them to alter port states. To handle massping, I created a new function get_ping_pcap_result that does the same thing, only it updates host states and is called only when USI->ping_scan and not USI->ping_scan_arp. These functions could conceivably be combined to make just one pcap handler, but they're both big enough as it is, so I left them separate.
Lots of helpful assert statements in scan_engine.cc made the migration easier than it could have been.
There's a lot of code duplication that should be factored out, like
probeI = hss->probes_outstanding.end();
listsz = hss->num_probes_outstanding();
goodone = false;
for(probenum = 0; probenum < listsz && !goodone; probenum++) {
Some quick empirical testing of 100 random hosts shows the migrated massping to be slower than the old one. Is it because of different timeouts? Also a lack of short-cicuiting, probably easy to fix.
The migration has improved host discovery in at least one case. UDP ping scan (-PU) against 81.74.101.164 used to give
Bogus trynum, sequence number or unexpected IP address in ICMP error message
(because the sending system swapped the bytes in the IP ID? 0xda0f instead of 0x0fda?). But with the new system it's correctly detected as up.
The check for an ICMP TTL exceeded message seems wrong. It's processed only if it was sent by the target, and the host is marked down. But in that case the host should be marked up, but that case seems to be impossible.
Here's a big reason why it's so slow. ultra_scan is used to working with a maximum of perhaps 50 hosts at a time. massping calls it with up to 2048. UltraScanInfo::numIncompleteHosts in scan_engine.cc uses std::list::size, which is an O(n) function. numIncompleteHosts accounted for 90% of the total running time in one of my tests. When I made some small changes to recude the number of times it is called, it dropped to 8%.
What does this mean? (In doAnyPings.)
/* Next come global pings */
/****NOT IMPLEMENTED YET *****
if (USI->gstats->numprobes < 30 &&
USI->gstats->lastping_sent_numprobes + 20 &&
TIMEVAL_SUBTRACT(USI->now, USI-gstats->lastrcvd) > USI->perf.pingtime &&
TIMEVAL_SUBTRACT(USI->now, USI-gstats->lastping_sent) > USI->perf.pingtime &&
USI->gstats->sendOK()) {
sendGlobalPingProbe(USI);
} ***/