Commit a7b19e0a authored by Paul Warren's avatar Paul Warren
Browse files

Look up connections in Linux's NAT connection tracking table.

Looks up connections in Linux's connection tracking (NAT) table.
Handy to iftop the WAN interface on my router and still see LAN IPs.

Luke Dashjr <luke@dashjr.org>
parent 8d76ef06
......@@ -6,6 +6,9 @@ Unattributed items are by Paul Warren and Chris Lightfoot.
1.0
* Support looking up connection mappings in Linux conntrack table
Luke Dashjr <luke+iftop@dashjr.org>
* Choose first running interface, rather than first "up" interface (Redhat #1403025)
Robert Scheck <robert@fedoraproject.org>
......
......@@ -23,6 +23,7 @@
char * config_directives[] = {
"interface",
"conntrack-resolution",
"dns-resolution",
"port-resolution",
"filter-code",
......
......@@ -434,6 +434,14 @@ if test x"$enable_default_promiscuous" = x"yes"; then
AC_DEFINE([NEED_PROMISCUOUS_FOR_OUTGOING],1,[Enable default promiscuous mode to capture outgoing packets])
fi
PKG_CHECK_MODULES([libnetfilter_conntrack], [libnetfilter_conntrack], [
PKG_CHECK_MODULES([libmnl], [libmnl], [
LIBS="$LIBS $libnetfilter_conntrack_LIBS $libmnl_LIBS"
CFLAGS="$CFLAGS $libnetfilter_conntrack_CFLAGS $libmnl_CFLAGS"
AC_DEFINE(HAVE_LIBNETFILTER_CONNTRACK, 1, [libnetfilter_conntrack available])
])
])
dnl
dnl Wahey! This might even work.
dnl
......
......@@ -52,6 +52,9 @@ slow?
\fB-h\fP
Print a summary of usage.
.TP
\fB-C\fP
Look for connections in conntrack table and use mapped IPs instead.
.TP
\fB-n\fP
Don't do hostname lookups.
.TP
......@@ -212,6 +215,9 @@ sets the network interface. The following config directives are supported:
\fBinterface:\fP \fIif\fP
Sets the network interface to \fIif\fP.
.TP
\fBconntrack-resolution:\fP \fI(yes|no)\fP
Controls lookup of connections in conntrack table.
.TP
\fBdns-resolution:\fP \fI(yes|no)\fP
Controls reverse lookup of IP addresses.
.TP
......
......@@ -86,6 +86,99 @@ static void finish(int sig) {
#ifdef HAVE_LIBNETFILTER_CONNTRACK
#include <arpa/inet.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <linux/netfilter/nf_conntrack_tcp.h>
struct mnl_socket *nlsock = NULL;
static void conntrack_init() {
nlsock = mnl_socket_open(NETLINK_NETFILTER);
if (nlsock) {
if (mnl_socket_bind(nlsock, 0, MNL_SOCKET_AUTOPID) < 0) {
mnl_socket_close(nlsock);
nlsock = NULL;
}
}
}
static int conntrack_cb(const struct nlmsghdr *nlh, void *data) {
addr_pair* const ap = data;
struct nf_conntrack *ct = nfct_new();
if (!ct) {
return MNL_CB_OK;
}
nfct_nlmsg_parse(nlh, ct);
if (nfct_get_attr_u8(ct, ATTR_L3PROTO) == AF_INET) {
ap->src.s_addr = nfct_get_attr_u32(ct, ATTR_IPV4_SRC);
ap->dst.s_addr = nfct_get_attr_u32(ct, ATTR_IPV4_DST);
ap->protocol = nfct_get_attr_u8(ct, ATTR_L4PROTO);
ap->src_port = ntohs(nfct_get_attr_u16(ct, ATTR_PORT_SRC));
ap->dst_port = ntohs(nfct_get_attr_u16(ct, ATTR_PORT_DST));
}
nfct_destroy(ct);
return MNL_CB_OK;
}
void resolve_conntrack_mappings(addr_pair* const ap) {
if (!nlsock) return;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET;
nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
nlh->nlmsg_seq = 0;
struct nfgenmsg *nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
nfh->nfgen_family = ap->af;
nfh->version = NFNETLINK_V0;
nfh->res_id = 0;
struct nf_conntrack *ct = nfct_new();
if (!ct) {
return;
}
nfct_set_attr_u8(ct, ATTR_L3PROTO, ap->af);
if (ap->af != AF_INET) {
// TODO: IPv6 support
return;
}
nfct_set_attr_u32(ct, ATTR_IPV4_SRC, ap->dst.s_addr);
nfct_set_attr_u32(ct, ATTR_IPV4_DST, ap->src.s_addr);
nfct_set_attr_u8(ct, ATTR_L4PROTO, ap->protocol);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, htons(ap->dst_port));
nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(ap->src_port));
nfct_nlmsg_build(nlh, ct);
int ret = mnl_socket_sendto(nlsock, nlh, nlh->nlmsg_len);
nfct_destroy(ct);
if (ret == -1) {
return;
}
ret = mnl_socket_recvfrom(nlsock, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, mnl_socket_get_portid(nlsock), conntrack_cb, ap);
if (ret <= MNL_CB_STOP) {
break;
}
ret = mnl_socket_recvfrom(nlsock, buf, sizeof(buf));
}
}
#endif
/* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2
* bytes of tcp/udp header */
/* Increase with a further 20 to account for IPv6 header length. */
......@@ -419,6 +512,9 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir, int pld_len)
switch (IP_V(iptr)) {
case 4:
ap.protocol = iptr->ip_p;
#ifdef HAVE_LIBNETFILTER_CONNTRACK
resolve_conntrack_mappings(&ap);
#endif
/* Add the addresses to be resolved */
/* The IPv4 address is embedded in a in6_addr structure,
* so it need be copied, and delivered to resolve(). */
......@@ -828,6 +924,12 @@ int main(int argc, char **argv) {
pthread_mutex_init(&tick_mutex, NULL);
#ifdef HAVE_LIBNETFILTER_CONNTRACK
if (options.conntrackresolution) {
conntrack_init();
}
#endif
packet_init();
init_history();
......
......@@ -30,7 +30,7 @@
options_t options;
char optstr[] = "+i:f:nNF:G:lhpbBu:Pm:c:s:tL:o:";
char optstr[] = "+i:f:CnNF:G:lhpbBu:Pm:c:s:tL:o:";
/* Global options. */
......@@ -137,6 +137,7 @@ void options_set_defaults() {
inet_pton(AF_INET6, "fe80::", &options.netfilter6net); /* Link-local */
inet_pton(AF_INET6, "ffff::", &options.netfilter6mask);
options.link_local = 0;
options.conntrackresolution = 0;
options.dnsresolution = 1;
options.portresolution = 1;
#ifdef NEED_PROMISCUOUS_FOR_OUTGOING
......@@ -186,10 +187,11 @@ static void usage(FILE *fp) {
fprintf(fp,
"iftop: display bandwidth usage on an interface by host\n"
"\n"
"Synopsis: iftop -h | [-npblNBP] [-i interface] [-f filter code]\n"
"Synopsis: iftop -h | [-CnpblNBP] [-i interface] [-f filter code]\n"
" [-F net/mask] [-G net6/mask6]\n"
"\n"
" -h display this message\n"
" -C look for connections in conntrack table\n"
" -n don't do hostname lookups\n"
" -N don't convert port numbers to services\n"
" -p run in promiscuous mode (show traffic between other\n"
......@@ -234,6 +236,10 @@ void options_read_args(int argc, char **argv) {
usage(stdout);
exit(0);
case 'C':
config_set_string("conntrack-resolution","true");
break;
case 'n':
config_set_string("dns-resolution","false");
break;
......@@ -566,6 +572,7 @@ int options_config_get_net_filter6() {
void options_make() {
options_config_get_string("interface", &options.interface);
options_config_get_bool("conntrack-resolution", &options.conntrackresolution);
options_config_get_bool("dns-resolution", &options.dnsresolution);
options_config_get_bool("port-resolution", &options.portresolution);
options_config_get_string("filter-code", &options.filtercode);
......
......@@ -48,6 +48,7 @@ typedef struct {
/* interface on which to listen */
char *interface;
int conntrackresolution;
int dnsresolution;
int portresolution;
/* pcap filter code */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment