文章归档

一种更快速的本地端口存活检测方法

本地端口存活检测通常有两种做法:

  1. netstat
  2. nmap & telnet

netstat的问题在于,它的原理是扫描整个/proc/net/tcp、/proc/net/tcp6文件,如果本机链接数非常多(不一定是正常的连接,也可能是异常连接,例如处于TIME_WAIT状态或者FIN状态)的情况下,/proc/net/tcp文件会非常大,扫描几个小时也扫不完,而且扫描/proc/net/tcp文件这个过程是属于内核态的,可能会hang住

另外nmap虽然是一个很成熟标准的端口存活检测机制,nmap的方式是通过连接端口来判断的,这种侵入式方法的问题在于,对于一些实现的不是很健壮的程序,可能会因此挂掉。

最近从内核/proc/net/tcp的实现中找到了一种更简单的方法,优点:

  • 非侵入式
  • 性能不受连接数影响

/proc/net/tcp、/proc/net/tcp6文件有一个很特殊的特点,就是listen的端口一定是在最前面的,那么我们扫描的时候,只需要扫描前面listen端口,遇到第一个非listen的端口退出即可。

/proc/net/tcp在内核中是一个seq文件

static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	struct tcp_iter_state *st = seq->private;
	void *rc = NULL;

	if (v == SEQ_START_TOKEN) {
		rc = tcp_get_idx(seq, 0);
		goto out;
	}

	switch (st->state) {
	case TCP_SEQ_STATE_OPENREQ:
	case TCP_SEQ_STATE_LISTENING:
		rc = listening_get_next(seq, v);
		if (!rc) {
			st->state = TCP_SEQ_STATE_ESTABLISHED;
			st->bucket = 0;
			st->offset = 0;
			rc = established_get_first(seq);
		}
		break;
	case TCP_SEQ_STATE_ESTABLISHED:
		rc = established_get_next(seq, v);
		break;
	}
out:
	++*pos;
	st->last_pos = *pos;
	return rc;
}

可以明显看到,/proc/net/tcp文件在读取时,会优先输出listen端口。

扫描方法可以参考如下代码:

# Get all listen port from proc file system
my @net_tcp_files = ('/proc/net/tcp', '/proc/net/tcp6');
my @net_tcp_listen_ports = ();
foreach my $net_tcp (@net_tcp_files) {
    my $fh;
    if (!open($fh, $net_tcp)) {
    print "[Fail] can't open $net_tcp, ignore ..\n";
    next;
    }
    while ( ! eof($fh) ) {
    defined( $_ = <$fh> ) or die "readline failed: $!";
    $_ =~ s/:/ /g;
    $_ =~ s/[\s ]+/ /g;
    if (index($_, "local_address") != -1) {
        # ignore the first title line
        next;
    }
    my @net_stat = split(' ', $_);
    my $st = $net_stat[5];
    if ($st eq "st" || $st eq "03") {
        next;
    } elsif ($st ne "0A") {
        last;
    }
    my $local_port = hex($net_stat[2]);
    push @net_tcp_listen_ports, $local_port;
    }
    close($fh);
}

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>