Commit 6197223f by pdw

Applied patch from Mats Erik Andersson <mats.andersson@gisladisker.se>

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=427852#30

Adds support for IPv6

Also fixes:

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=477928 (minor typo)

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=595169 (performance problem
with address hashing)

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=598367 (failing link address
detection for GNU/kfreebsd)
1 parent 6d594acb
......@@ -11,6 +11,19 @@
int compare(void* a, void* b) {
addr_pair* aa = (addr_pair*)a;
addr_pair* bb = (addr_pair*)b;
if (aa->af != bb->af)
return 0;
if (aa->af == AF_INET6) {
return (IN6_ARE_ADDR_EQUAL(&aa->src6, &bb->src6)
&& aa->src_port == bb->src_port
&& IN6_ARE_ADDR_EQUAL(&aa->dst6, &bb->dst6)
&& aa->dst_port == bb->dst_port
&& aa->protocol == bb->protocol);
}
/* AF_INET or unknown. */
return (aa->src.s_addr == bb->src.s_addr
&& aa->src_port == bb->src_port
&& aa->dst.s_addr == bb->dst.s_addr
......@@ -18,25 +31,42 @@ int compare(void* a, void* b) {
&& aa->protocol == bb->protocol);
}
static int __inline__ hash_uint32(uint32_t n) {
return ((n & 0x000000FF)
+ ((n & 0x0000FF00) >> 8)
+ ((n & 0x00FF0000) >> 16)
+ ((n & 0xFF000000) >> 24));
}
int hash(void* key) {
int hash;
long addr;
addr_pair* ap = (addr_pair*)key;
addr = (long)ap->src.s_addr;
hash = ((addr & 0x000000FF)
+ (addr & 0x0000FF00 >> 8)
+ (addr & 0x00FF0000 >> 16)
+ (addr & 0xFF000000 >> 24)
+ ap->src_port) % 0xFF;
addr = (long)ap->dst.s_addr;
hash = ( hash + (addr & 0x000000FF)
+ (addr & 0x0000FF00 >> 8)
+ (addr & 0x00FF0000 >> 16)
+ (addr & 0xFF000000 >> 24)
+ ap->dst_port) % 0xFF;
if (ap->af == AF_INET6) {
uint32_t* addr6 = ap->src6.s6_addr32;
hash = ( hash_uint32(addr6[0])
+ hash_uint32(addr6[1])
+ hash_uint32(addr6[2])
+ hash_uint32(addr6[3])
+ ap->src_port) % 0xFF;
addr6 = ap->dst6.s6_addr32;
hash = ( hash + hash_uint32(addr6[0])
+ hash_uint32(addr6[1])
+ hash_uint32(addr6[2])
+ hash_uint32(addr6[3])
+ ap->dst_port) % 0xFF;
} else {
in_addr_t addr = ap->src.s_addr;
hash = ( hash_uint32(addr)
+ ap->src_port) % 0xFF;
addr = ap->dst.s_addr;
hash = ( hash + hash_uint32(addr)
+ ap->dst_port) % 0xFF;
}
return hash;
}
......
......@@ -12,11 +12,18 @@
#include "hash.h"
typedef struct {
int af;
unsigned short int protocol;
unsigned short int src_port;
struct in_addr src;
union {
struct in_addr src;
struct in6_addr src6;
};
unsigned short int dst_port;
struct in_addr dst;
union {
struct in_addr dst;
struct in6_addr dst6;
};
} addr_pair;
typedef addr_pair key_type; /* index into hash table */
......
......@@ -18,12 +18,17 @@
#include <net/if.h>
#include <netinet/in.h>
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ \
|| ( defined __GNUC__ && ! defined __linux__ )
#include <sys/param.h>
#include <sys/sysctl.h>
#include <net/if_dl.h>
#endif
#ifdef USE_GETIFADDRS
#include <ifaddrs.h>
#endif
#include "iftop.h"
/*
......@@ -40,16 +45,20 @@
*/
int
get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr, struct in6_addr *if_ip6_addr)
{
int s;
struct ifreq ifr = {};
int got_hw_addr = 0;
int got_ip_addr = 0;
int got_ip6_addr = 0;
#ifdef USE_GETIFADDRS
struct ifaddrs *ifa, *ifas;
#endif
/* -- */
s = socket(PF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
s = socket(AF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
if (s == -1) {
perror("socket");
......@@ -71,7 +80,8 @@ get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
got_hw_addr = 1;
}
#else
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ \
|| ( defined __GNUC__ && ! defined __linux__ )
{
int sysctlparam[6] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
size_t needed = 0;
......@@ -109,7 +119,45 @@ get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
#endif
/* Get the IP address of the interface */
#ifdef SIOCGIFADDR
#ifdef USE_GETIFADDRS
if (getifaddrs(&ifas) == -1) {
fprintf(stderr, "Unable to get IP address for interface: %s\n", interface);
perror("getifaddrs()");
}
else {
for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
if (got_ip_addr && got_ip6_addr)
break; /* Search is already complete. */
if (strcmp(ifa->ifa_name, interface))
continue; /* Not our interface. */
if ( (ifa->ifa_addr->sa_family != AF_INET)
&& (ifa->ifa_addr->sa_family != AF_INET6) )
continue; /* AF_PACKET is beyond our scope. */
if ( (ifa->ifa_addr->sa_family == AF_INET)
&& !got_ip_addr ) {
got_ip_addr = 2;
memcpy(if_ip_addr,
&(((struct sockaddr_in *) ifa->ifa_addr)->sin_addr),
sizeof(*if_ip_addr));
continue;
}
/* Must be a IPv6 address at this point. */
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
|| IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
continue;
/* A useful IPv6 address. */
memcpy(if_ip6_addr, &(sa6->sin6_addr), sizeof(*if_ip6_addr));
got_ip6_addr = 4;
}
freeifaddrs(ifas);
} /* getifaddrs() */
#elif defined(SIOCGIFADDR)
(*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_INET;
if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
fprintf(stderr, "Unable to get IP address for interface: %s\n", interface);
......@@ -125,5 +173,5 @@ get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
close(s);
return got_hw_addr + got_ip_addr;
return got_hw_addr + got_ip_addr + got_ip6_addr;
}
......@@ -36,6 +36,8 @@ char * config_directives[] = {
"log-scale",
"max-bandwidth",
"net-filter",
"net-filter6",
"link-local",
"port-display",
NULL
};
......
......@@ -11,8 +11,8 @@ iftop - display bandwidth usage on an interface by host
.SH SYNOPSIS
\fBiftop\fP \fB-h\fP |
[\fB-nNpbBP\fP] [\fB-i\fP \fIinterface\fP] [\fB-f\fP \fIfilter code\fP] [\fB-F\fP \fInet\fP/\fImask\fP]
[\fB-nNpblBP\fP] [\fB-i\fP \fIinterface\fP] [\fB-f\fP \fIfilter code\fP] [\fB-F\fP \fInet\fP/\fImask\fP]
[\fB-G\fP \fInet6\fP/\fImask6\fP]
.SH DESCRIPTION
\fBiftop\fP listens to network traffic on a named \fIinterface\fP, or on the
first interface it can find which looks like an external interface if none is
......@@ -65,6 +65,10 @@ the specified interface is also counted.
\fB-P\fP
Turn on port display.
.TP
\fB-l\fP
Display and count datagrams addressed to or from link-local IPv6 addresses.
The default is not to display that address category.
.TP
\fB-b\fP
Don't display bar graphs of traffic.
.TP
......@@ -79,12 +83,17 @@ Use \fIfilter code\fP to select the packets to count. Only IP packets are ever
counted, so the specified code is evaluated as \fB(\fP\fIfilter code\fP\fB) and ip\fP.
.TP
\fB-F\fP \fInet\fP/\fImask\fP
Specifies a network for traffic analysis. If specified, iftop will only
Specifies an IPv4 network for traffic analysis. If specified, iftop will only
include packets flowing in to or out of the given network, and packet direction
is determined relative to the network boundary, rather than to the interface.
You may specify \fImask\fP as a dotted quad, such as /255.255.255.0, or as a
single number specifying the number of bits set in the netmask, such as /24.
.TP
\fB-G\fP \fInet6\fP/\fImask6\fP
Specifies an IPv6 network for traffic analysis. The value of \fImask6\fP can be
given as a prefix length or as a numerical address string for more compound
bitmasking.
.TP
\fB-c\fP \fIconfig file\fP
Specifies an alternate config file. If not specified, iftop will use
\fB~/.iftoprc\fP if it exists. See below for a description of config files
......@@ -213,6 +222,9 @@ Puts the interface into promiscuous mode.
\fBport-display:\fP \fI(off|source-only|destination-only|on)\fP
Controls display of port numbers.
.TP
\fBlink-local:\fP \fI(yes|no)\fP
Determines displaying of link-local IPv6 addresses.
.TP
\fBhide-source:\fP \fI(yes|no)\fP
Hides source host names.
.TP
......@@ -240,6 +252,9 @@ Fixes the maximum for the bar graph scale to \fIbw\fP, e.g. "10M". Note that the
\fBnet-filter:\fP \fInet/mask\fP
Defines an IP network boundary for determining packet direction.
.TP
\fBnet-filter6:\fP \fInet6/mask6\fP
Defines an IPv6 network boundary for determining packet direction.
.TP
\fBscreen-filter:\fP \fIregexp\fP
Sets a regular expression to filter screen output.
......
......@@ -46,6 +46,7 @@
#include "cfgfile.h"
#include "ppp.h"
#include <netinet/ip6.h>
/* ethernet address of interface. */
int have_hw_addr = 0;
......@@ -53,7 +54,9 @@ unsigned char if_hw_addr[6];
/* IP address of interface */
int have_ip_addr = 0;
int have_ip6_addr = 0;
struct in_addr if_ip_addr;
struct in6_addr if_ip6_addr;
extern options_t options;
......@@ -78,7 +81,8 @@ static void finish(int sig) {
/* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header */
#define CAPTURE_LENGTH 72
/* Increase with a further 20 to account for IPv6 header length. */
#define CAPTURE_LENGTH 92
void init_history() {
history = addr_hash_create();
......@@ -148,10 +152,14 @@ int in_filter_net(struct in_addr addr) {
return ret;
}
int ip_addr_match(struct in_addr addr) {
int __inline__ ip_addr_match(struct in_addr addr) {
return addr.s_addr == if_ip_addr.s_addr;
}
int __inline__ ip6_addr_match(struct in6_addr *addr) {
return IN6_ARE_ADDR_EQUAL(addr, &if_ip6_addr);
}
/**
* Creates an addr_pair from an ip (and tcp/udp) header, swapping src and dst
* if required
......@@ -160,6 +168,11 @@ void assign_addr_pair(addr_pair* ap, struct ip* iptr, int flip) {
unsigned short int src_port = 0;
unsigned short int dst_port = 0;
/* Arrange for predictable values. */
memset(ap, '\0', sizeof(*ap));
if(IP_V(iptr) == 4) {
ap->af = AF_INET;
/* Does this protocol use ports? */
if(iptr->ip_p == IPPROTO_TCP || iptr->ip_p == IPPROTO_UDP) {
/* We take a slight liberty here by treating UDP the same as TCP */
......@@ -182,7 +195,33 @@ void assign_addr_pair(addr_pair* ap, struct ip* iptr, int flip) {
ap->dst = iptr->ip_src;
ap->dst_port = src_port;
}
} /* IPv4 */
else if (IP_V(iptr) == 6) {
/* IPv6 packet seen. */
struct ip6_hdr *ip6tr = (struct ip6_hdr *) iptr;
ap->af = AF_INET6;
if( (ip6tr->ip6_nxt == IPPROTO_TCP) || (ip6tr->ip6_nxt == IPPROTO_UDP) ) {
struct tcphdr *thdr = ((void *) ip6tr) + 40;
src_port = ntohs(thdr->th_sport);
dst_port = ntohs(thdr->th_dport);
}
if(flip == 0) {
memcpy(&ap->src6, &ip6tr->ip6_src, sizeof(ap->src6));
ap->src_port = src_port;
memcpy(&ap->dst6, &ip6tr->ip6_dst, sizeof(ap->dst6));
ap->dst_port = dst_port;
}
else {
memcpy(&ap->src6, &ip6tr->ip6_dst, sizeof(ap->src6));
ap->src_port = dst_port;
memcpy(&ap->dst6, &ip6tr->ip6_src, sizeof(ap->dst6));
ap->dst_port = src_port;
}
}
}
static void handle_ip_packet(struct ip* iptr, int hw_dir)
......@@ -194,9 +233,16 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
void **void_pp;
} u_ht = { &ht };
addr_pair ap;
int len;
unsigned int len = 0;
struct in6_addr scribdst; /* Scratch pad. */
struct in6_addr scribsrc; /* Scratch pad. */
/* Reinterpret packet type. */
struct ip6_hdr* ip6tr = (struct ip6_hdr *) iptr;
memset(&ap, '\0', sizeof(ap));
if(options.netfilter == 0) {
if( (IP_V(iptr) ==4 && options.netfilter == 0)
|| (IP_V(iptr) == 6 && options.netfilter6 == 0) ) {
/*
* Net filter is off, so assign direction based on MAC address
*/
......@@ -213,12 +259,22 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
/* Packet direction is not given away by h/ware layer. Try IP
* layer
*/
else if(have_ip_addr && ip_addr_match(iptr->ip_src)) {
else if((IP_V(iptr) == 4) && have_ip_addr && ip_addr_match(iptr->ip_src)) {
/* outgoing */
assign_addr_pair(&ap, iptr, 0);
direction = 1;
}
else if(have_ip_addr && ip_addr_match(iptr->ip_dst)) {
else if((IP_V(iptr) == 4) && have_ip_addr && ip_addr_match(iptr->ip_dst)) {
/* incoming */
assign_addr_pair(&ap, iptr, 1);
direction = 0;
}
else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_src)) {
/* outgoing */
assign_addr_pair(&ap, iptr, 0);
direction = 1;
}
else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_dst)) {
/* incoming */
assign_addr_pair(&ap, iptr, 1);
direction = 0;
......@@ -232,16 +288,18 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
else if (options.promiscuous_but_choosy) {
return; /* junk it */
}
else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
else if((IP_V(iptr) == 4) && (iptr->ip_src.s_addr < iptr->ip_dst.s_addr)) {
assign_addr_pair(&ap, iptr, 1);
direction = 0;
}
else {
else if(IP_V(iptr) == 4) {
assign_addr_pair(&ap, iptr, 0);
direction = 0;
}
/* Drop other uncertain packages. */
}
else {
if(IP_V(iptr) == 4 && options.netfilter != 0) {
/*
* Net filter on, assign direction according to netmask
*/
......@@ -261,22 +319,96 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
}
}
ap.protocol = iptr->ip_p;
if(IP_V(iptr) == 6 && options.netfilter6 != 0) {
/*
* Net filter IPv6 active.
*/
int j;
//else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_dst)) {
/* First reduce the participating addresses using the netfilter prefix.
* We need scratch pads to do this.
*/
for (j=0; j < 4; ++j) {
scribdst.s6_addr32[j] = ip6tr->ip6_dst.s6_addr32[j]
& options.netfilter6mask.s6_addr32[j];
scribsrc.s6_addr32[j] = ip6tr->ip6_src.s6_addr32[j]
& options.netfilter6mask.s6_addr32[j];
}
/* Now look for any hits. */
//if(in_filter_net(iptr->ip_src) && !in_filter_net(iptr->ip_dst)) {
if (IN6_ARE_ADDR_EQUAL(&scribsrc, &options.netfilter6net)
&& ! IN6_ARE_ADDR_EQUAL(&scribdst, &options.netfilter6net)) {
/* out of network */
assign_addr_pair(&ap, iptr, 0);
direction = 1;
}
//else if(in_filter_net(iptr->ip_dst) && !in_filter_net(iptr->ip_src)) {
else if (! IN6_ARE_ADDR_EQUAL(&scribsrc, &options.netfilter6net)
&& IN6_ARE_ADDR_EQUAL(&scribdst, &options.netfilter6net)) {
/* into network */
assign_addr_pair(&ap, iptr, 1);
direction = 0;
}
else {
/* drop packet */
return ;
}
}
#if 1
/* Test if link-local IPv6 packets should be dropped. */
if( IP_V(iptr) == 6 && !options.link_local
&& (IN6_IS_ADDR_LINKLOCAL(&ip6tr->ip6_dst)
|| IN6_IS_ADDR_LINKLOCAL(&ip6tr->ip6_src)) )
return;
#endif
/* Do address resolving. */
switch (IP_V(iptr)) {
case 4:
ap.protocol = iptr->ip_p;
/* 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(). */
memset(&scribdst, '\0', sizeof(scribdst));
memcpy(&scribdst, &iptr->ip_dst, sizeof(struct in_addr));
resolve(ap.af, &scribdst, NULL, 0);
memset(&scribsrc, '\0', sizeof(scribsrc));
memcpy(&scribsrc, &iptr->ip_src, sizeof(struct in_addr));
resolve(ap.af, &scribsrc, NULL, 0);
break;
case 6:
ap.protocol = ip6tr->ip6_nxt;
/* Add the addresses to be resolved */
resolve(ap.af, &ip6tr->ip6_dst, NULL, 0);
resolve(ap.af, &ip6tr->ip6_src, NULL, 0);
default:
break;
}
/* Add the addresses to be resolved */
resolve(&iptr->ip_dst, NULL, 0);
resolve(&iptr->ip_src, NULL, 0);
if(hash_find(history, &ap, u_ht.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
ht = history_create();
hash_insert(history, &ap, ht);
}
len = ntohs(iptr->ip_len);
/* Do accounting. */
switch (IP_V(iptr)) {
case 4:
len = ntohs(iptr->ip_len);
break;
case 6:
len = ntohs(ip6tr->ip6_plen) + 40;
default:
break;
}
/* Update record */
ht->last_write = history_pos;
if(iptr->ip_src.s_addr == ap.src.s_addr) {
if( ((IP_V(iptr) == 4) && (iptr->ip_src.s_addr == ap.src.s_addr))
|| ((IP_V(iptr) == 6) && !memcmp(&ip6tr->ip6_src, &ap.src6, sizeof(ap.src6))) )
{
ht->sent[history_pos] += len;
ht->total_sent += len;
}
......@@ -390,7 +522,7 @@ static void handle_ppp_packet(unsigned char* args, const struct pcap_pkthdr* pkt
packet += 2;
length -= 2;
if(proto == PPP_IP || proto == ETHERTYPE_IP) {
if(proto == PPP_IP || proto == ETHERTYPE_IP || proto == ETHERTYPE_IPV6) {
handle_ip_packet((struct ip*)packet, -1);
}
}
......@@ -436,7 +568,7 @@ static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkt
payload += sizeof(struct vlan_8021q_header);
}
if(ether_type == ETHERTYPE_IP) {
if(ether_type == ETHERTYPE_IP || ether_type == ETHERTYPE_IPV6) {
struct ip* iptr;
int dir = -1;
......@@ -456,6 +588,7 @@ static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkt
dir = 0;
}
/* Distinguishing ip_hdr and ip6_hdr will be done later. */
iptr = (struct ip*)(payload); /* alignment? */
handle_ip_packet(iptr, dir);
}
......@@ -468,10 +601,10 @@ static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkt
char *set_filter_code(const char *filter) {
char *x;
if (filter) {
x = xmalloc(strlen(filter) + sizeof "() and ip");
sprintf(x, "(%s) and ip", filter);
x = xmalloc(strlen(filter) + sizeof "() and (ip or ip6)");
sprintf(x, "(%s) and (ip or ip6)", filter);
} else
x = xstrdup("ip");
x = xstrdup("ip or ip6");
if (pcap_compile(pd, &pcap_filter, x, 1, 0) == -1) {
xfree(x);
return pcap_geterr(pd);
......@@ -501,19 +634,28 @@ void packet_init() {
#ifdef HAVE_DLPI
result = get_addrs_dlpi(options.interface, if_hw_addr, &if_ip_addr);
#else
result = get_addrs_ioctl(options.interface, if_hw_addr, &if_ip_addr);
result = get_addrs_ioctl(options.interface, if_hw_addr,
&if_ip_addr, &if_ip6_addr);
#endif
if (result < 0) {
exit(1);
}
have_hw_addr = result & 1;
have_ip_addr = result & 2;
have_hw_addr = result & 0x01;
have_ip_addr = result & 0x02;
have_ip6_addr = result & 0x04;
if(have_ip_addr) {
fprintf(stderr, "IP address is: %s\n", inet_ntoa(if_ip_addr));
}
if(have_ip6_addr) {
char ip6str[INET6_ADDRSTRLEN];
ip6str[0] = '\0';
inet_ntop(AF_INET6, &if_ip6_addr, ip6str, sizeof(ip6str));
fprintf(stderr, "IPv6 address is: %s\n", ip6str);
}
if(have_hw_addr) {
fprintf(stderr, "MAC address is:");
......
......@@ -2,6 +2,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
......@@ -14,29 +15,36 @@
#define hash_table_size 256
int ns_hash_compare(void* a, void* b) {
struct in_addr* aa = (struct in_addr*)a;
struct in_addr* bb = (struct in_addr*)b;
return (aa->s_addr == bb->s_addr);
struct in6_addr* aa = (struct in6_addr*)a;
struct in6_addr* bb = (struct in6_addr*)b;
return IN6_ARE_ADDR_EQUAL(aa, bb);
}
static int __inline__ hash_uint32(uint32_t n) {
return ((n & 0x000000FF)
+ ((n & 0x0000FF00) >> 8)
+ ((n & 0x00FF0000) >> 16)
+ ((n & 0xFF000000) >> 24));
}
int ns_hash_hash(void* key) {
int hash;
long addr;
addr = (long)((struct in_addr*)key)->s_addr;
uint32_t* addr6 = ((struct in6_addr *) key)->s6_addr32;
hash = ((addr & 0x000000FF)
+ (addr & 0x0000FF00 >> 8)
+ (addr & 0x00FF0000 >> 16)
+ (addr & 0xFF000000 >> 24)) % 0xFF;
hash = ( hash_uint32(addr6[0])
+ hash_uint32(addr6[1])
+ hash_uint32(addr6[2])
+ hash_uint32(addr6[3])) % 0xFF;
return hash;
}
void* ns_hash_copy_key(void* orig) {
struct in_addr* copy;
struct in6_addr* copy;
copy = xmalloc(sizeof *copy);
*copy = *(struct in_addr*)orig;
memcpy(copy, orig, sizeof *copy);
return copy;
}
......
......@@ -30,7 +30,7 @@
options_t options;
char optstr[] = "+i:f:nNF:hpbBPm:c:";
char optstr[] = "+i:f:nNF:G:lhpbBPm:c:";
/* Global options. */
......@@ -118,6 +118,10 @@ void options_set_defaults() {
options.netfilter = 0;
inet_aton("10.0.1.0", &options.netfilternet);
inet_aton("255.255.255.0", &options.netfiltermask);
options.netfilter6 = 0;
inet_pton(AF_INET6, "fe80::", &options.netfilter6net); /* Link-local */
inet_pton(AF_INET6, "ffff::", &options.netfilter6mask);
options.link_local = 0;
options.dnsresolution = 1;
options.portresolution = 1;
#ifdef NEED_PROMISCUOUS_FOR_OUTGOING
......@@ -237,7 +241,8 @@ static void usage(FILE *fp) {
fprintf(fp,
"iftop: display bandwidth usage on an interface by host\n"
"\n"
"Synopsis: iftop -h | [-npbBP] [-i interface] [-f filter code] [-N net/mask]\n"
"Synopsis: iftop -h | [-npblBP] [-i interface] [-f filter code]\n"
" [-F net/mask] [-G net6/mask6]\n"
"\n"
" -h display this message\n"
" -n don't do hostname lookups\n"
......@@ -249,7 +254,9 @@ static void usage(FILE *fp) {
" -i interface listen on named interface\n"
" -f filter code use filter code to select packets to count\n"
" (default: none, but only IP packets are counted)\n"
" -F net/mask show traffic flows in/out of network\n"
" -F net/mask show traffic flows in/out of IPv4 network\n"
" -G net6/mask6 show traffic flows in/out of IPv6 network\n"
" -l display and count link-local IPv6 traffic (default: off)\n"
" -P show ports as well as hosts\n"
" -m limit sets the upper limit for the bandwidth scale\n"
" -c config file specifies an alternative configuration file\n"
......@@ -285,6 +292,10 @@ void options_read_args(int argc, char **argv) {
config_set_string("filter-code", optarg);
break;
case 'l':
config_set_string("link-local", "true");
break;
case 'p':
config_set_string("promiscuous", "true");
break;
......@@ -297,6 +308,10 @@ void options_read_args(int argc, char **argv) {
config_set_string("net-filter", optarg);
break;
case 'G':
config_set_string("net-filter6", optarg);
break;
case 'm':
config_set_string("max-bandwidth", optarg);
break;
......@@ -438,6 +453,8 @@ int options_config_get_net_filter() {
if(s) {
char* mask;
options.netfilter = 0;
mask = strchr(s, '/');
if (mask == NULL) {
fprintf(stderr, "Could not parse net/mask: %s\n", s);
......@@ -455,7 +472,7 @@ int options_config_get_net_filter() {
int n;
n = atoi(mask);
if (n > 32) {
fprintf(stderr, "Invalid netmask: %s\n", s);
fprintf(stderr, "Invalid netmask length: %s\n", mask);
}
else {
if(n == 32) {
......@@ -470,17 +487,86 @@ int options_config_get_net_filter() {
options.netfiltermask.s_addr = htonl(~mm);
}
}
options.netfilter = 1;
}
else if (inet_aton(mask, &options.netfiltermask) == 0) {
fprintf(stderr, "Invalid netmask: %s\n", s);
else {
if (inet_aton(mask, &options.netfiltermask) != 0)
options.netfilter = 1;
else {
fprintf(stderr, "Invalid netmask: %s\n", s);
return 0;
}
}
options.netfilternet.s_addr = options.netfilternet.s_addr & options.netfiltermask.s_addr;
options.netfilter = 1;
return 1;
}
return 0;
}
/*
* Read the net filter IPv6 option.
*/
int options_config_get_net_filter6() {
char* s;
int j;
s = config_get_string("net-filter6");
if(s) {
char* mask;
options.netfilter6 = 0;
mask = strchr(s, '/');
if (mask == NULL) {
fprintf(stderr, "Could not parse IPv6 net/prefix: %s\n", s);
return 0;
}
*mask = '\0';
mask++;
if (inet_pton(AF_INET6, s, &options.netfilter6net) == 0) {
fprintf(stderr, "Invalid IPv6 network address: %s\n", s);
return 0;
}
/* Accept prefix lengths and address expressions. */
if (mask[strspn(mask, "0123456789")] == '\0') {
/* Whole string is numeric */
unsigned int n;
n = atoi(mask);
if (n > 128 || n < 1) {
fprintf(stderr, "Invalid IPv6 prefix length: %s\n", mask);
}
else {
int bl, rem;
const uint32_t mm = 0xffffffff;
uint32_t part = mm;
bl = n / 32;
rem = n % 32;
part <<= 32 - rem;
for (j=0; j < bl; ++j)
options.netfilter6mask.s6_addr32[j] = htonl(mm);
if (rem > 0)
options.netfilter6mask.s6_addr32[bl] = htonl(part);
options.netfilter6 = 1;
}
}
else {
if (inet_pton(AF_INET6, mask, &options.netfilter6mask) != 0)
options.netfilter6 = 1;
else {
fprintf(stderr, "Invalid IPv6 netmask: %s\n", s);
return 0;
}
}
/* Prepare any comparison by masking the provided filtered net. */
for (j=0; j < 4; ++j)
options.netfilter6net.s6_addr32[j] &= options.netfilter6mask.s6_addr32[j];
return 1;
}
return 0;
}
void options_make() {
options_config_get_string("interface", &options.interface);
......@@ -499,5 +585,7 @@ void options_make() {
options_config_get_bw_rate("max-bandwidth", &options.max_bandwidth);
options_config_get_enum("port-display", showports_enumeration, (int*)&options.showports);
options_config_get_string("screen-filter", &options.screenfilter);
options_config_get_bool("link-local", &options.link_local);
options_config_get_net_filter();
options_config_get_net_filter6();
};
......@@ -78,6 +78,13 @@ typedef struct {
struct in_addr netfilternet;
struct in_addr netfiltermask;
int netfilter6;
struct in6_addr netfilter6net;
struct in6_addr netfilter6mask;
/* Account for link-local traffic. */
int link_local;
char *config_file;
int config_file_specified;
......
......@@ -25,7 +25,7 @@
#define RESOLVE_QUEUE_LENGTH 20
struct in_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
struct in6_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
pthread_cond_t resolver_queue_cond;
pthread_mutex_t resolver_queue_mutex;
......@@ -55,18 +55,48 @@ extern options_t options;
* as NetBSD break the RFC and implement it in a non-thread-safe fashion, so
* for the moment, the configure script won't try to use it.
*/
char *do_resolve(struct in_addr *addr) {
struct sockaddr_in sin = {0};
char *do_resolve(struct in6_addr *addr) {
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
char buf[NI_MAXHOST]; /* 1025 */
int res;
sin.sin_family = AF_INET;
sin.sin_addr = *addr;
sin.sin_port = 0;
if (getnameinfo((struct sockaddr*)&sin, sizeof sin, buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
return xstrdup(buf);
else
return NULL;
int res, af;
uint32_t* probe;
memset(&sin, '\0', sizeof(sin));
memset(&sin6, '\0', sizeof(sin6));
/* If the upper three (network byte order) uint32-parts
* are null, then there ought to be an IPv4 address here.
* Any such IPv6 would have to be 'xxxx::'. Neglectable? */
probe = (uint32_t *) addr;
af = (probe[1] || probe[2] || probe[3]) ? AF_INET6 : AF_INET;
switch (af) {
case AF_INET:
sin.sin_family = af;
sin.sin_port = 0;
memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));
if (getnameinfo((struct sockaddr*)&sin, sizeof sin,
buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
return xstrdup(buf);
else
return NULL;
break;
case AF_INET6:
sin6.sin6_family = af;
sin6.sin6_port = 0;
memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr));
if (getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
return xstrdup(buf);
else
return NULL;
break;
default:
return NULL;
}
}
#elif defined(USE_GETHOSTBYADDR_R)
......@@ -376,7 +406,7 @@ void resolver_worker(void* ptr) {