xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2017, Joyent, Inc.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/sockio.h>
33 #include <net/if.h>
34 #include <netinet/in_systm.h>
35 #include <netinet/in.h>
36 #include <netinet/if_ether.h>
37 #include <netinet/ip.h>
38 #include <netdb.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <setjmp.h>
42 #include <arpa/inet.h>
43 #include <sys/time.h>
44 #include "snoop.h"
45 
46 static sigjmp_buf nisjmp;
47 static hrtime_t snoop_lastwarn;		/* Last time NS warning fired */
48 static unsigned snoop_warninter = 60;	/* Time in seconds between warnings */
49 
50 #define	MAXHASH 1024  /* must be a power of 2 */
51 
52 #define	SEPARATORS " \t\n"
53 
54 struct hostdata {
55 	struct hostdata	*h_next;
56 	char		*h_hostname;
57 	int		h_pktsout;
58 	int		h_pktsin;
59 };
60 
61 struct hostdata4 {
62 	struct hostdata4	*h4_next;
63 	char		*h4_hostname;
64 	int		h4_pktsout;
65 	int		h4_pktsin;
66 	struct in_addr	h4_addr;
67 };
68 
69 struct hostdata6 {
70 	struct hostdata6	*h6_next;
71 	char		*h6_hostname;
72 	int		h6_pktsout;
73 	int		h6_pktsin;
74 	struct in6_addr	h6_addr;
75 };
76 
77 static struct hostdata *addhost(int, const void *, const char *, char **);
78 
79 static struct hostdata4 *h_table4[MAXHASH];
80 static struct hostdata6 *h_table6[MAXHASH];
81 
82 #define	iphash(e)  ((e) & (MAXHASH-1))
83 
84 /* ARGSUSED */
85 static void
86 wakeup(int n)
87 {
88 	siglongjmp(nisjmp, 1);
89 }
90 
91 extern char *inet_ntoa();
92 
93 static void
94 snoop_nswarn(void)
95 {
96 	hrtime_t now = gethrtime();
97 
98 	if (now - snoop_lastwarn >= snoop_warninter * NANOSEC) {
99 		snoop_lastwarn = now;
100 		(void) fprintf(stderr, "snoop: warning: packets captured, but "
101 		    "name service lookups are timing out. Use snoop -r to "
102 		    "disable name service lookups\n");
103 	}
104 }
105 
106 static struct hostdata *
107 iplookup(struct in_addr ipaddr)
108 {
109 	register struct hostdata4 *h;
110 	struct hostent *hp = NULL;
111 	struct netent *np;
112 	int error_num;
113 	struct hostdata *retval;
114 
115 	for (h = h_table4[iphash(ipaddr.s_addr)]; h; h = h->h4_next) {
116 		if (h->h4_addr.s_addr == ipaddr.s_addr)
117 			return ((struct hostdata *)h);
118 	}
119 
120 	/* not found.  Put it in */
121 
122 	if (ipaddr.s_addr == htonl(INADDR_BROADCAST))
123 		return (addhost(AF_INET, &ipaddr, "BROADCAST", NULL));
124 	if (ipaddr.s_addr == htonl(INADDR_ANY))
125 		return (addhost(AF_INET, &ipaddr, "OLD-BROADCAST", NULL));
126 
127 	/*
128 	 * Set an alarm here so we don't get held up by
129 	 * an unresponsive name server.
130 	 * Give it 3 sec to do its work.
131 	 */
132 	if (!rflg) {
133 		if (sigsetjmp(nisjmp, 1) == 0) {
134 			(void) snoop_alarm(3, wakeup);
135 			hp = getipnodebyaddr((char *)&ipaddr, sizeof (int),
136 			    AF_INET, &error_num);
137 			if (hp == NULL && inet_lnaof(ipaddr) == 0) {
138 				np = getnetbyaddr(inet_netof(ipaddr), AF_INET);
139 				if (np)
140 					return (addhost(AF_INET, &ipaddr,
141 					    np->n_name, np->n_aliases));
142 			}
143 			(void) snoop_alarm(0, wakeup);
144 		} else {
145 			snoop_nswarn();
146 		}
147 	}
148 
149 	retval = addhost(AF_INET, &ipaddr,
150 	    hp ? hp->h_name : inet_ntoa(ipaddr),
151 	    hp ? hp->h_aliases : NULL);
152 	if (hp != NULL)
153 		freehostent(hp);
154 	return (retval);
155 }
156 
157 static struct hostdata *
158 ip6lookup(const struct in6_addr *ip6addr)
159 {
160 	struct hostdata6 *h;
161 	struct hostent *hp = NULL;
162 	int error_num;
163 	char addrstr[INET6_ADDRSTRLEN];
164 	char *addname;
165 	struct hostdata *retval;
166 
167 	for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h;
168 	    h = h->h6_next) {
169 		if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr))
170 			return ((struct hostdata *)h);
171 	}
172 
173 	/* not in the hash table, put it in */
174 	if (IN6_IS_ADDR_UNSPECIFIED(ip6addr))
175 		return (addhost(AF_INET6, ip6addr, "UNSPECIFIED", NULL));
176 
177 	/*
178 	 * Set an alarm here so we don't get held up by
179 	 * an unresponsive name server.
180 	 * Give it 3 sec to do its work.
181 	 */
182 	if (!rflg) {
183 		if (sigsetjmp(nisjmp, 1) == 0) {
184 			(void) snoop_alarm(3, wakeup);
185 			hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr),
186 			    AF_INET6, &error_num);
187 			(void) snoop_alarm(0, wakeup);
188 		} else {
189 			snoop_nswarn();
190 		}
191 	} else {
192 		hp = NULL;
193 	}
194 
195 	if (hp != NULL)
196 		addname = hp->h_name;
197 	else {
198 		(void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN);
199 		addname = addrstr;
200 	}
201 
202 	retval = addhost(AF_INET6, ip6addr, addname, hp ? hp->h_aliases : NULL);
203 	if (hp != NULL)
204 		freehostent(hp);
205 	return (retval);
206 }
207 
208 static struct hostdata *
209 addhost(int family, const void *ipaddr, const char *name, char **aliases)
210 {
211 	struct hostdata **hp, *n = NULL;
212 	extern FILE *namefile;
213 	int hashval;
214 	static char aname[128];
215 	char *np;
216 	static struct hostdata h;
217 	int ind;
218 
219 	switch (family) {
220 	case AF_INET:
221 		n = (struct hostdata *)malloc(sizeof (struct hostdata4));
222 		if (n == NULL)
223 			goto alloc_failed;
224 
225 		memset(n, 0, sizeof (struct hostdata4));
226 		n->h_hostname = strdup(name);
227 		if (n->h_hostname == NULL)
228 			goto alloc_failed;
229 
230 		((struct hostdata4 *)n)->h4_addr =
231 		    *(const struct in_addr *)ipaddr;
232 		hashval = ((struct in_addr *)ipaddr)->s_addr;
233 		hp = (struct hostdata **)&h_table4[iphash(hashval)];
234 		break;
235 	case AF_INET6:
236 		n = (struct hostdata *)malloc(sizeof (struct hostdata6));
237 		if (n == NULL)
238 			goto alloc_failed;
239 
240 		memset(n, 0, sizeof (struct hostdata6));
241 		n->h_hostname = strdup(name);
242 		if (n->h_hostname == NULL)
243 			goto alloc_failed;
244 
245 		memcpy(&((struct hostdata6 *)n)->h6_addr, ipaddr,
246 		    sizeof (struct in6_addr));
247 		hashval = ((const int *)ipaddr)[3];
248 		hp = (struct hostdata **)&h_table6[iphash(hashval)];
249 		break;
250 	default:
251 		fprintf(stderr, "snoop: ERROR: Unknown address family: %d",
252 		    family);
253 		exit(1);
254 	}
255 
256 	n->h_next = *hp;
257 	*hp = n;
258 
259 	if (namefile != NULL) {
260 		if (family == AF_INET) {
261 			np = inet_ntoa(*(const struct in_addr *)ipaddr);
262 			if (np) {
263 				(void) fprintf(namefile, "%s\t%s", np, name);
264 				if (aliases) {
265 					for (ind = 0;
266 					    aliases[ind] != NULL;
267 					    ind++) {
268 						(void) fprintf(namefile, " %s",
269 						    aliases[ind]);
270 					}
271 				}
272 				(void) fprintf(namefile, "\n");
273 			}
274 		} else if (family == AF_INET6) {
275 			np = (char *)inet_ntop(AF_INET6, (void *)ipaddr, aname,
276 			    sizeof (aname));
277 			if (np) {
278 				(void) fprintf(namefile, "%s\t%s", np, name);
279 				if (aliases) {
280 					for (ind = 0;
281 					    aliases[ind] != NULL;
282 					    ind++) {
283 						(void) fprintf(namefile, " %s",
284 						    aliases[ind]);
285 					}
286 				}
287 				(void) fprintf(namefile, "\n");
288 			}
289 		} else {
290 			(void) fprintf(stderr, "addhost: unknown family %d\n",
291 			    family);
292 		}
293 	}
294 	return (n);
295 
296 alloc_failed:
297 	if (n)
298 		free(n);
299 	(void) fprintf(stderr, "addhost: no mem\n");
300 
301 	aname[0] = '\0';
302 	memset(&h, 0, sizeof (struct hostdata));
303 	h.h_hostname = aname;
304 	return (&h);
305 }
306 
307 char *
308 addrtoname(int family, const void *ipaddr)
309 {
310 	switch (family) {
311 	case AF_INET:
312 		return (iplookup(*(const struct in_addr *)ipaddr)->h_hostname);
313 	case AF_INET6:
314 		return (ip6lookup((const struct in6_addr *)ipaddr)->h_hostname);
315 	}
316 	(void) fprintf(stderr, "snoop: ERROR: unknown address family: %d\n",
317 	    family);
318 	exit(1);
319 	/* NOTREACHED */
320 }
321 
322 void
323 load_names(char *fname)
324 {
325 	char buf[1024];
326 	char *addr, *name, *alias;
327 	FILE *f;
328 	unsigned int addrv4;
329 	struct in6_addr addrv6;
330 	int family;
331 	void *naddr;
332 
333 	(void) fprintf(stderr, "Loading name file %s\n", fname);
334 	f = fopen(fname, "r");
335 	if (f == NULL) {
336 		perror(fname);
337 		return;
338 	}
339 
340 	while (fgets(buf, 1024, f) != NULL) {
341 		addr = strtok(buf, SEPARATORS);
342 		if (addr == NULL || *addr == '#')
343 			continue;
344 		if (inet_pton(AF_INET6, addr, (void *)&addrv6) == 1) {
345 			family = AF_INET6;
346 			naddr = (void *)&addrv6;
347 		} else if ((addrv4 = inet_addr(addr)) != (ulong_t)-1) {
348 			family = AF_INET;
349 			naddr = (void *)&addrv4;
350 		}
351 		name = strtok(NULL, SEPARATORS);
352 		if (name == NULL)
353 			continue;
354 		while ((alias = strtok(NULL, SEPARATORS)) != NULL &&
355 		    (*alias != '#')) {
356 			(void) addhost(family, naddr, alias, NULL);
357 		}
358 		(void) addhost(family, naddr, name, NULL);
359 		/* Note: certain addresses such as broadcast are skipped */
360 	}
361 
362 	(void) fclose(f);
363 }
364 
365 /*
366  * lgetipnodebyname: looks up hostname in cached address data. This allows
367  * filtering on hostnames from the .names file to work properly, and
368  * avoids name clashes between domains. Note that only the first of the
369  * ipv4, ipv6, or v4mapped address will be returned, because the
370  * cache does not contain information on multi-homed hosts.
371  */
372 /*ARGSUSED*/
373 struct hostent *
374 lgetipnodebyname(const char *name, int af, int flags, int *error_num)
375 {
376 	int i;
377 	struct hostdata4 *h;
378 	struct hostdata6 *h6;
379 	static struct hostent he;		/* host entry */
380 	static struct in6_addr h46_addr[MAXADDRS];	/* v4mapped address */
381 	static char h_name[MAXHOSTNAMELEN];	/* hostname */
382 	static char *list[MAXADDRS];		/* addr_list array */
383 	struct hostent *hp = &he;
384 	int ind;
385 
386 	(void) memset((char *)hp, 0, sizeof (struct hostent));
387 	hp->h_name = h_name;
388 	h_name[0] = '\0';
389 	strcpy(h_name, name);
390 
391 	hp->h_addrtype = AF_INET6;
392 
393 	hp->h_addr_list = list;
394 	for (i = 0; i < MAXADDRS; i++)
395 		hp->h_addr_list[i] = NULL;
396 	ind = 0;
397 
398 	/* ipv6 lookup */
399 	if (af == AF_INET6) {
400 		hp->h_length = sizeof (struct in6_addr);
401 		for (i = 0; i < MAXHASH; i++) {
402 			for (h6 = h_table6[i]; h6; h6 = h6->h6_next) {
403 				if (strcmp(name, h6->h6_hostname) == 0) {
404 					if (ind >= MAXADDRS - 1) {
405 						/* too many addresses */
406 						return (hp);
407 					}
408 					/* found ipv6 addr */
409 					hp->h_addr_list[ind] =
410 					    (char *)&h6->h6_addr;
411 					ind++;
412 				}
413 			}
414 		}
415 	}
416 	/* ipv4 or v4mapped lookup */
417 	if (af == AF_INET || (flags & AI_ALL)) {
418 		for (i = 0; i < MAXHASH; i++) {
419 			for (h = h_table4[i]; h; h = h->h4_next) {
420 				if (strcmp(name, h->h4_hostname) == 0) {
421 					if (ind >= MAXADDRS - 1) {
422 						/* too many addresses */
423 						return (hp);
424 					}
425 					if (af == AF_INET) {
426 						/* found ipv4 addr */
427 						hp->h_addrtype = AF_INET;
428 						hp->h_length =
429 						    sizeof (struct in_addr);
430 						hp->h_addr_list[ind] =
431 						    (char *)&h->h4_addr;
432 						ind++;
433 					} else {
434 						/* found v4mapped addr */
435 						hp->h_length =
436 						    sizeof (struct in6_addr);
437 						hp->h_addr_list[ind] =
438 						    (char *)&h46_addr[ind];
439 						IN6_INADDR_TO_V4MAPPED(
440 						    &h->h4_addr,
441 						    &h46_addr[ind]);
442 						ind++;
443 					}
444 				}
445 			}
446 		}
447 	}
448 	return (ind > 0 ? hp : NULL);
449 }
450