forked from osmocom/wireshark
From Douglas Pratley (bug 1445):
Add a "subnets" file defining a named list of subnets. svn path=/trunk/; revision=24154
This commit is contained in:
parent
bc944d8fff
commit
a88efc9325
|
@ -203,6 +203,12 @@
|
|||
<entry>/etc/hosts, $HOME/.wireshark/hosts</entry>
|
||||
<entry>%WIRESHARK%\hosts, %APPDATA%\Wireshark\hosts</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><command>subnets</command></entry>
|
||||
<entry>IPv4 subnet name resolution.</entry>
|
||||
<entry>/etc/subnets, $HOME/.wireshark/subnets</entry>
|
||||
<entry>%WIRESHARK%\subnets, %APPDATA%\Wireshark\subnets</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><command>ipxnets</command></entry>
|
||||
<entry>IPX name resolution.</entry>
|
||||
|
@ -420,6 +426,38 @@ c0-00-ff-ff-ff-ff TR_broadcast
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><command>subnets</command></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Wireshark uses the files listed in <xref linkend="AppFilesTabFolders"/>
|
||||
to translate an IPv4 address into a subnet name. If no exact match from the
|
||||
hosts file or from DNS is found, Wireshark will attempt a partial match for the subnet
|
||||
of the address.
|
||||
</para>
|
||||
<para>
|
||||
Each line of this file consists of an IPv4 address, a subnet mask length separated
|
||||
only by a '/' and a name separated by whitespace. While the address must be a full IPv4
|
||||
address, any values beyond the mask length are subsequently ignored.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
An example is:
|
||||
<programlisting>
|
||||
# Comments must be prepended by the # sign!
|
||||
192.168.0.0/24 ws_test_network
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
A partially matched name will be printed as "subnet-name.remaining-address". For example,
|
||||
"192.168.0.1" under the subnet above would be printed as "ws_test_network.1"; if the mask length
|
||||
above had been 16 rather than 24, the printed address would be "ws_test_network.0.1".
|
||||
</para>
|
||||
<para>
|
||||
The settings from this file are read in at program start and never
|
||||
written by Wireshark.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry> <varlistentry>
|
||||
<term><command>ipxnets</command></term>
|
||||
<listitem>
|
||||
<para>
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
#include <epan/emem.h>
|
||||
|
||||
#define ENAME_HOSTS "hosts"
|
||||
#define ENAME_SUBNETS "subnets"
|
||||
#define ENAME_ETHERS "ethers"
|
||||
#define ENAME_IPXNETS "ipxnets"
|
||||
#define ENAME_MANUF "manuf"
|
||||
|
@ -138,6 +139,7 @@
|
|||
#define HASHIPXNETSIZE 256
|
||||
#define HASHMANUFSIZE 256
|
||||
#define HASHPORTSIZE 256
|
||||
#define SUBNETLENGTHSIZE 32 /*1-32 inc.*/
|
||||
|
||||
/* hash table used for IPv4 lookup */
|
||||
|
||||
|
@ -162,6 +164,13 @@ typedef struct hashipv6 {
|
|||
struct hashipv6 *next;
|
||||
} hashipv6_t;
|
||||
|
||||
/* Array of entries of subnets of different lengths */
|
||||
typedef struct {
|
||||
gsize mask_length; /*1-32*/
|
||||
guint32 mask; /* e.g. 255.255.255.*/
|
||||
hashipv4_t** subnet_addresses; /* Hash table of subnet addresses */
|
||||
} subnet_length_entry_t;
|
||||
|
||||
/* hash table used for TCP/UDP/SCTP port lookup */
|
||||
|
||||
#define HASH_PORT(port) ((port) & (HASHPORTSIZE - 1))
|
||||
|
@ -232,6 +241,9 @@ static hashmanuf_t *manuf_table[HASHMANUFSIZE];
|
|||
static hashether_t *(*wka_table[48])[HASHETHSIZE];
|
||||
static hashipxnet_t *ipxnet_table[HASHIPXNETSIZE];
|
||||
|
||||
static subnet_length_entry_t subnet_length_entries[SUBNETLENGTHSIZE]; /* Ordered array of entries */
|
||||
static gboolean have_subnet_entry = FALSE;
|
||||
|
||||
static int eth_resolution_initialized = 0;
|
||||
static int ipxnet_resolution_initialized = 0;
|
||||
static int service_resolution_initialized = 0;
|
||||
|
@ -280,6 +292,11 @@ GList *adns_queue_head = NULL;
|
|||
|
||||
#endif /* HAVE_GNU_ADNS */
|
||||
|
||||
typedef struct {
|
||||
guint32 mask;
|
||||
gsize mask_length;
|
||||
const gchar* name; /* Shallow copy */
|
||||
} subnet_entry_t;
|
||||
|
||||
/*
|
||||
* Miscellaneous functions
|
||||
|
@ -326,6 +343,8 @@ static int fgetline(char **buf, int *size, FILE *fp)
|
|||
/*
|
||||
* Local function definitions
|
||||
*/
|
||||
static subnet_entry_t subnet_lookup(const guint32 addr);
|
||||
static void subnet_entry_set(guint32 subnet_addr, guint32 mask_length, const gchar* name);
|
||||
|
||||
|
||||
static void add_service_name(hashport_t **proto_table, guint port, const char *service_name)
|
||||
|
@ -544,6 +563,48 @@ static void abort_network_query(int sig _U_)
|
|||
}
|
||||
#endif /* AVOID_DNS_TIMEOUT */
|
||||
|
||||
/* Fill in an IP4 structure with info from subnets file or just with
|
||||
* string form of address.
|
||||
*/
|
||||
static void fill_dummy_ip4(guint addr, hashipv4_t* volatile tp)
|
||||
{
|
||||
subnet_entry_t subnet_entry;
|
||||
tp->is_dummy_entry = TRUE; /* Overwrite if we get async DNS reply */
|
||||
|
||||
/* Do we have a subnet for this address? */
|
||||
subnet_entry = subnet_lookup(addr);
|
||||
if(0 != subnet_entry.mask) {
|
||||
/* Print name, then '.' then IP address after subnet mask */
|
||||
guint32 host_addr;
|
||||
gchar buffer[MAX_IP_STR_LEN];
|
||||
gchar* paddr;
|
||||
gsize i;
|
||||
|
||||
host_addr = addr & (~(guint32)subnet_entry.mask);
|
||||
ip_to_str_buf((guint8 *)&host_addr, buffer, MAX_IP_STR_LEN);
|
||||
paddr = buffer;
|
||||
|
||||
/* Skip to first octet that is not totally masked
|
||||
* If length of mask is 32, we chomp the whole address.
|
||||
* If the address string starts '.' (should not happen?),
|
||||
* we skip that '.'.
|
||||
*/
|
||||
i = subnet_entry.mask_length / 8;
|
||||
while(*(paddr) != '\0' && i > 0) {
|
||||
if(*(++paddr) == '.') {
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
/* There are more efficient ways to do this, but this is safe if we
|
||||
* trust g_snprintf and MAXNAMELEN
|
||||
*/
|
||||
g_snprintf(tp->name, MAXNAMELEN, "%s%s", subnet_entry.name, paddr);
|
||||
} else {
|
||||
ip_to_str_buf((guint8 *)&addr, tp->name, MAXNAMELEN);
|
||||
}
|
||||
}
|
||||
|
||||
static gchar *host_name_lookup(guint addr, gboolean *found)
|
||||
{
|
||||
int hash_idx;
|
||||
|
@ -591,8 +652,10 @@ static gchar *host_name_lookup(guint addr, gboolean *found)
|
|||
qmsg->submitted = FALSE;
|
||||
adns_queue_head = g_list_append(adns_queue_head, (gpointer) qmsg);
|
||||
|
||||
tp->is_dummy_entry = TRUE;
|
||||
ip_to_str_buf((guint8 *)&addr, tp->name, MAXNAMELEN);
|
||||
/* XXX found is set to TRUE, which seems a bit odd, but I'm not
|
||||
* going to risk changing the semantics.
|
||||
*/
|
||||
fill_dummy_ip4(addr, tp);
|
||||
return tp->name;
|
||||
}
|
||||
#endif /* HAVE_GNU_ADNS */
|
||||
|
@ -636,11 +699,9 @@ static gchar *host_name_lookup(guint addr, gboolean *found)
|
|||
}
|
||||
|
||||
/* unknown host or DNS timeout */
|
||||
|
||||
ip_to_str_buf((guint8 *)&addr, tp->name, MAXNAMELEN);
|
||||
tp->is_dummy_entry = TRUE;
|
||||
*found = FALSE;
|
||||
|
||||
fill_dummy_ip4(addr, tp);
|
||||
return (tp->name);
|
||||
|
||||
} /* host_name_lookup */
|
||||
|
@ -1765,6 +1826,250 @@ read_hosts_file (const char *hostspath)
|
|||
return TRUE;
|
||||
} /* read_hosts_file */
|
||||
|
||||
|
||||
/* Read in a list of subnet definition - name pairs.
|
||||
* <line> = <comment> | <entry> | <whitespace>
|
||||
* <comment> = <whitespace>#<any>
|
||||
* <entry> = <subnet_definition> <whitespace> <subnet_name> [<comment>|<whitespace><any>]
|
||||
* <subnet_definition> = <ipv4_address> / <subnet_mask_length>
|
||||
* <ipv4_address> is a full address; it will be masked to get the subnet-ID.
|
||||
* <subnet_mask_length> is a decimal 1-31
|
||||
* <subnet_name> is a string containing no whitespace.
|
||||
* <whitespace> = (space | tab)+
|
||||
* Any malformed entries are ignored.
|
||||
* Any trailing data after the subnet_name is ignored.
|
||||
*
|
||||
* XXX Support IPv6
|
||||
*/
|
||||
static gboolean
|
||||
read_subnets_file (const char *subnetspath)
|
||||
{
|
||||
FILE *hf;
|
||||
char *line = NULL;
|
||||
int size = 0;
|
||||
gchar *cp, *cp2;
|
||||
guint32 host_addr; /* IPv4 ONLY */
|
||||
int mask_length;
|
||||
|
||||
if ((hf = eth_fopen(subnetspath, "r")) == NULL)
|
||||
return FALSE;
|
||||
|
||||
while (fgetline(&line, &size, hf) >= 0) {
|
||||
if ((cp = strchr(line, '#')))
|
||||
*cp = '\0';
|
||||
|
||||
if ((cp = strtok(line, " \t")) == NULL)
|
||||
continue; /* no tokens in the line */
|
||||
|
||||
|
||||
/* Expected format is <IP4 address>/<subnet length> */
|
||||
cp2 = strchr(cp, '/');
|
||||
if(NULL == cp2) {
|
||||
//No length
|
||||
continue;
|
||||
}
|
||||
*cp2 = '\0'; /* Cut token */
|
||||
++cp2 ;
|
||||
|
||||
/* Check if this is a valid IPv4 address */
|
||||
if (inet_pton(AF_INET, cp, &host_addr) != 1) {
|
||||
continue; /* no */
|
||||
}
|
||||
|
||||
mask_length = atoi(cp2);
|
||||
if(0 >= mask_length || mask_length > 31) {
|
||||
continue; /* invalid mask length */
|
||||
}
|
||||
|
||||
if ((cp = strtok(NULL, " \t")) == NULL)
|
||||
continue; /* no subnet name */
|
||||
|
||||
subnet_entry_set(host_addr, (guint32)mask_length, cp);
|
||||
}
|
||||
if (line != NULL)
|
||||
g_free(line);
|
||||
|
||||
fclose(hf);
|
||||
return TRUE;
|
||||
} /* read_subnets_file */
|
||||
|
||||
static subnet_entry_t subnet_lookup(const guint32 addr)
|
||||
{
|
||||
subnet_entry_t subnet_entry;
|
||||
guint32 i;
|
||||
|
||||
/* Search mask lengths linearly, longest first */
|
||||
|
||||
i = SUBNETLENGTHSIZE;
|
||||
while(have_subnet_entry && i > 0) {
|
||||
guint32 masked_addr;
|
||||
subnet_length_entry_t* length_entry;
|
||||
|
||||
/* Note that we run from 31 (length 32) to 0 (length 1) */
|
||||
--i;
|
||||
g_assert(i < SUBNETLENGTHSIZE);
|
||||
|
||||
|
||||
length_entry = &subnet_length_entries[i];
|
||||
|
||||
if(NULL != length_entry->subnet_addresses) {
|
||||
hashipv4_t * tp;
|
||||
guint32 hash_idx;
|
||||
|
||||
masked_addr = addr & length_entry->mask;
|
||||
hash_idx = HASH_IPV4_ADDRESS(masked_addr);
|
||||
|
||||
tp = length_entry->subnet_addresses[hash_idx];
|
||||
while(tp != NULL && tp->addr != masked_addr) {
|
||||
tp = tp->next;
|
||||
}
|
||||
|
||||
if(NULL != tp) {
|
||||
subnet_entry.mask = length_entry->mask;
|
||||
subnet_entry.mask_length = i + 1; /* Length is offset + 1 */
|
||||
subnet_entry.name = tp->name;
|
||||
return subnet_entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subnet_entry.mask = 0;
|
||||
subnet_entry.mask_length = 0;
|
||||
subnet_entry.name = NULL;
|
||||
|
||||
return subnet_entry;
|
||||
}
|
||||
|
||||
/* Add a subnet-definition - name pair to the set.
|
||||
* The definition is taken by masking the address passed in with the mask of the
|
||||
* given length.
|
||||
*/
|
||||
static void subnet_entry_set(guint32 subnet_addr, guint32 mask_length, const gchar* name)
|
||||
{
|
||||
subnet_length_entry_t* entry;
|
||||
hashipv4_t * tp;
|
||||
gsize hash_idx;
|
||||
|
||||
g_assert(mask_length > 0 && mask_length <= 32);
|
||||
|
||||
entry = &subnet_length_entries[mask_length - 1];
|
||||
|
||||
subnet_addr &= entry->mask;
|
||||
|
||||
hash_idx = HASH_IPV4_ADDRESS(subnet_addr);
|
||||
|
||||
if(NULL == entry->subnet_addresses) {
|
||||
entry->subnet_addresses = g_new0(hashipv4_t*,HASHHOSTSIZE);
|
||||
}
|
||||
|
||||
if(NULL != (tp = entry->subnet_addresses[hash_idx])) {
|
||||
if(tp->addr == subnet_addr) {
|
||||
return; /* XXX provide warning that an address was repeated? */
|
||||
} else {
|
||||
hashipv4_t * new_tp = g_new(hashipv4_t,1);
|
||||
tp->next = new_tp;
|
||||
tp = new_tp;
|
||||
}
|
||||
} else {
|
||||
tp = entry->subnet_addresses[hash_idx] = g_new(hashipv4_t,1);
|
||||
}
|
||||
|
||||
tp->next = NULL;
|
||||
tp->addr = subnet_addr;
|
||||
tp->is_dummy_entry = FALSE; /*Never used again...*/
|
||||
strncpy(tp->name, name, MAXNAMELEN); /* This is longer than subnet names can actually be */
|
||||
have_subnet_entry = TRUE;
|
||||
}
|
||||
|
||||
static guint32 get_subnet_mask(guint32 mask_length) {
|
||||
|
||||
static guint32 masks[SUBNETLENGTHSIZE];
|
||||
static gboolean initialised = FALSE;
|
||||
|
||||
if(!initialised) {
|
||||
memset(masks, 0, sizeof(masks));
|
||||
|
||||
initialised = TRUE;
|
||||
|
||||
/* XXX There must be a better way to do this than
|
||||
* hand-coding the values, but I can't seem to
|
||||
* come up with one!
|
||||
*/
|
||||
|
||||
inet_pton(AF_INET, "128.0.0.0", &masks[0]);
|
||||
inet_pton(AF_INET, "192.0.0.0", &masks[1]);
|
||||
inet_pton(AF_INET, "224.0.0.0", &masks[2]);
|
||||
inet_pton(AF_INET, "240.0.0.0", &masks[3]);
|
||||
inet_pton(AF_INET, "248.0.0.0", &masks[4]);
|
||||
inet_pton(AF_INET, "252.0.0.0", &masks[5]);
|
||||
inet_pton(AF_INET, "254.0.0.0", &masks[6]);
|
||||
inet_pton(AF_INET, "255.0.0.0", &masks[7]);
|
||||
|
||||
inet_pton(AF_INET, "255.128.0.0", &masks[8]);
|
||||
inet_pton(AF_INET, "255.192.0.0", &masks[9]);
|
||||
inet_pton(AF_INET, "255.224.0.0", &masks[10]);
|
||||
inet_pton(AF_INET, "255.240.0.0", &masks[11]);
|
||||
inet_pton(AF_INET, "255.248.0.0", &masks[12]);
|
||||
inet_pton(AF_INET, "255.252.0.0", &masks[13]);
|
||||
inet_pton(AF_INET, "255.254.0.0", &masks[14]);
|
||||
inet_pton(AF_INET, "255.255.0.0", &masks[15]);
|
||||
|
||||
inet_pton(AF_INET, "255.255.128.0", &masks[16]);
|
||||
inet_pton(AF_INET, "255.255.192.0", &masks[17]);
|
||||
inet_pton(AF_INET, "255.255.224.0", &masks[18]);
|
||||
inet_pton(AF_INET, "255.255.240.0", &masks[19]);
|
||||
inet_pton(AF_INET, "255.255.248.0", &masks[20]);
|
||||
inet_pton(AF_INET, "255.255.252.0", &masks[21]);
|
||||
inet_pton(AF_INET, "255.255.254.0", &masks[22]);
|
||||
inet_pton(AF_INET, "255.255.255.0", &masks[23]);
|
||||
|
||||
inet_pton(AF_INET, "255.255.255.128", &masks[24]);
|
||||
inet_pton(AF_INET, "255.255.255.192", &masks[25]);
|
||||
inet_pton(AF_INET, "255.255.255.224", &masks[26]);
|
||||
inet_pton(AF_INET, "255.255.255.240", &masks[27]);
|
||||
inet_pton(AF_INET, "255.255.255.248", &masks[28]);
|
||||
inet_pton(AF_INET, "255.255.255.252", &masks[29]);
|
||||
inet_pton(AF_INET, "255.255.255.254", &masks[30]);
|
||||
inet_pton(AF_INET, "255.255.255.255", &masks[31]);
|
||||
}
|
||||
|
||||
if(mask_length == 0 || mask_length > SUBNETLENGTHSIZE) {
|
||||
g_assert_not_reached();
|
||||
return 0;
|
||||
} else {
|
||||
return masks[mask_length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
static void subnet_name_lookup_init()
|
||||
{
|
||||
gchar* subnetspath;
|
||||
|
||||
guint32 i;
|
||||
for(i = 0; i < SUBNETLENGTHSIZE; ++i) {
|
||||
guint32 length = i + 1;
|
||||
|
||||
subnet_length_entries[i].subnet_addresses = NULL;
|
||||
subnet_length_entries[i].mask_length = length;
|
||||
subnet_length_entries[i].mask = get_subnet_mask(length);
|
||||
}
|
||||
|
||||
subnetspath = get_persconffile_path(ENAME_SUBNETS, FALSE, FALSE);
|
||||
if (!read_subnets_file(subnetspath) && errno != ENOENT) {
|
||||
report_open_failure(subnetspath, errno, FALSE);
|
||||
}
|
||||
g_free(subnetspath);
|
||||
|
||||
/*
|
||||
* Load the global subnets file, if we have one.
|
||||
*/
|
||||
subnetspath = get_datafile_path(ENAME_SUBNETS);
|
||||
if (!read_subnets_file(subnetspath) && errno != ENOENT) {
|
||||
report_open_failure(subnetspath, errno, FALSE);
|
||||
}
|
||||
g_free(subnetspath);
|
||||
}
|
||||
|
||||
/*
|
||||
* External Functions
|
||||
*/
|
||||
|
@ -1845,6 +2150,8 @@ host_name_lookup_init(void) {
|
|||
gnu_adns_initialized = TRUE;
|
||||
adns_currently_queued = 0;
|
||||
#endif /* HAVE_GNU_ADNS */
|
||||
|
||||
subnet_name_lookup_init();
|
||||
}
|
||||
|
||||
#ifdef HAVE_GNU_ADNS
|
||||
|
|
Loading…
Reference in New Issue