1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2019 Alexander V. Chernikov
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#ifndef _NET_ROUTING_RTSOCK_PRINT_H_
31#define _NET_ROUTING_RTSOCK_PRINT_H_
32
33
34#define	RLOG(_fmt, ...)	printf("%s: " _fmt "\n", __func__, ##__VA_ARGS__)
35#define	RLOG_ERRNO(_fmt, ...)	do {			\
36	printf("%s: " _fmt, __func__, ##__VA_ARGS__);	\
37	printf(": %s\n", strerror(errno));		\
38} while(0)
39
40#define	RTSOCK_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...)	 do {	\
41	if (!(_cond)) {						\
42		printf("-- CONDITION FAILED, rtm dump  --\n\n");\
43		rtsock_print_message(_rtm);			\
44		rtsock_print_table(AF_INET);			\
45		rtsock_print_table(AF_INET6);			\
46		printf("===================================\n");\
47	}							\
48	ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__);		\
49} while (0);
50
51#define	RTSOCKHD_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do {	\
52	if (!(_cond)) {						\
53		printf("-- CONDITION FAILED, rtm hexdump--\n\n");\
54		rtsock_print_message_hd(_rtm);				\
55	}							\
56	ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__);		\
57} while (0);
58
59
60/* from route.c */
61static const char *const msgtypes[] = {
62	"",
63	"RTM_ADD",
64	"RTM_DELETE",
65	"RTM_CHANGE",
66	"RTM_GET",
67	"RTM_LOSING",
68	"RTM_REDIRECT",
69	"RTM_MISS",
70	"RTM_LOCK",
71	"RTM_OLDADD",
72	"RTM_OLDDEL",
73	"RTM_RESOLVE",
74	"RTM_NEWADDR",
75	"RTM_DELADDR",
76	"RTM_IFINFO",
77	"RTM_NEWMADDR",
78	"RTM_DELMADDR",
79	"RTM_IFANNOUNCE",
80	"RTM_IEEE80211",
81};
82
83static const char metricnames[] =
84    "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire"
85    "\1mtu";
86static const char routeflags[] =
87    "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE"
88    "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
89    "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3"
90    "\024FIXEDMTU\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY";
91static const char ifnetflags[] =
92    "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
93    "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
94    "\017LINK2\020MULTICAST";
95static const char addrnames[] =
96    "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
97
98static int
99_printb(char *buf, size_t bufsize, int b, const char *str)
100{
101	int i;
102	int gotsome = 0;
103
104	char *pbuf = buf;
105
106	if (b == 0) {
107		*pbuf = '\0';
108		return (0);
109	}
110	while ((i = *str++) != 0) {
111		if (b & (1 << (i-1))) {
112			if (gotsome == 0)
113				i = '<';
114			else
115				i = ',';
116			*pbuf++ = i;
117			gotsome = 1;
118			for (; (i = *str) > 32; str++)
119				*pbuf++ = i;
120		} else
121			while (*str > 32)
122				str++;
123	}
124	if (gotsome)
125		*pbuf++ = '>';
126	*pbuf = '\0';
127
128	return (int)(pbuf - buf);
129}
130
131const char *
132rtsock_print_cmdtype(int cmd)
133{
134
135	return (msgtypes[cmd]);
136}
137
138char *
139rtsock_print_rtm_flags(char *buf, int buflen, int rtm_flags)
140{
141
142	_printb(buf, buflen, rtm_flags, routeflags);
143	return (buf);
144}
145
146
147#define	_PRINTX(fmt, ...)	do {				\
148	one_len = snprintf(ptr, rem_len, fmt, __VA_ARGS__);	\
149	ptr += one_len;						\
150	rem_len -= one_len;					\
151} while(0)
152
153
154void
155sa_print_hd(char *buf, int buflen, const char *data, int len)
156{
157	char *ptr;
158	int one_len, rem_len;
159
160	ptr = buf;
161	rem_len = buflen;
162
163	const char *last_char = NULL;
164	unsigned char v;
165	int repeat_count = 0;
166	for (int i = 0; i < len; i++) {
167		if (last_char && *last_char == data[i] && data[i] == 0x00) {
168			repeat_count++;
169			continue;
170		}
171
172		if (repeat_count > 1) {
173			_PRINTX("{%d}", repeat_count);
174			repeat_count = 0;
175		}
176
177		v = ((const unsigned char *)data)[i];
178		if (last_char == NULL)
179			_PRINTX("x%02X", v);
180		else
181			_PRINTX(", x%02X", v);
182
183		last_char = &data[i];
184		repeat_count = 1;
185	}
186
187	if (repeat_count > 1)
188		snprintf(ptr, rem_len, "{%d}", repeat_count);
189}
190
191#undef _PRINTX
192
193void
194sa_print(const struct sockaddr *sa, int include_hexdump)
195{
196	char hdbuf[512], abuf[64];
197	char ifbuf[128];
198	const struct sockaddr_dl *sdl;
199	const struct sockaddr_in6 *sin6;
200	const struct sockaddr_in *sin;
201	int i;
202
203	switch (sa->sa_family) {
204		case AF_INET:
205			sin = (struct sockaddr_in *)sa;
206			inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
207			printf(" af=inet len=%d addr=%s", sa->sa_len, abuf);
208			break;
209		case AF_INET6:
210			sin6 = (struct sockaddr_in6 *)sa;
211			inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
212			int scope_id = sin6->sin6_scope_id;
213			printf(" af=inet6 len=%d addr=%s", sa->sa_len, abuf);
214			if (scope_id != 0) {
215				memset(ifbuf, 0, sizeof(ifbuf));
216				if_indextoname(scope_id, ifbuf);
217				printf(" scope_id=%d if_name=%s", scope_id, ifbuf);
218			}
219			break;
220		case AF_LINK:
221			sdl = (const struct sockaddr_dl *)sa;
222			int sdl_index = sdl->sdl_index;
223			if (sdl_index != 0) {
224				memset(ifbuf, 0, sizeof(ifbuf));
225				if_indextoname(sdl_index, ifbuf);
226				printf(" af=link len=%d sdl_index=%d if_name=%s", sdl->sdl_len, sdl_index, ifbuf);
227			}
228			if (sdl->sdl_nlen) {
229				char _ifname[IFNAMSIZ];
230				memcpy(_ifname, sdl->sdl_data, sdl->sdl_nlen);
231				_ifname[sdl->sdl_nlen] = '\0';
232				printf(" name=%s", _ifname);
233			}
234			if (sdl->sdl_alen) {
235				printf(" addr=");
236				const char *lladdr = LLADDR(sdl);
237				for (int i = 0; i < sdl->sdl_alen; i++) {
238					if (i + 1 < sdl->sdl_alen)
239						printf("%02X:", ((const unsigned char *)lladdr)[i]);
240					else
241						printf("%02X", ((const unsigned char *)lladdr)[i]);
242				}
243			}
244			break;
245		default:
246			printf(" af=%d len=%d", sa->sa_family, sa->sa_len);
247	}
248
249	if (include_hexdump) {
250		sa_print_hd(hdbuf, sizeof(hdbuf), ((char *)sa), sa->sa_len);
251		printf(" hd={%s}", hdbuf);
252	}
253	printf("\n");
254}
255
256/*
257got message of size 240 on Mon Dec 16 09:23:31 2019
258RTM_ADD: Add Route: len 240, pid: 25534, seq 2, errno 0, flags:<HOST,DONE,LLINFO,STATIC>
259locks:  inits:
260sockaddrs: <DST,GATEWAY>
261*/
262
263void
264rtsock_print_rtm(struct rt_msghdr *rtm)
265{
266	struct timeval tv;
267	struct tm tm_res;
268	char buf[64];
269
270	gettimeofday(&tv, NULL);
271	localtime_r(&tv.tv_sec, &tm_res);
272	strftime(buf, sizeof(buf), "%F %T", &tm_res);
273	printf("Got message of size %hu on %s\n", rtm->rtm_msglen, buf);
274
275	char flags_buf[256];
276	rtsock_print_rtm_flags(flags_buf, sizeof(flags_buf), rtm->rtm_flags);
277
278	printf("%s: len %hu, pid: %d, seq %d, errno %d, flags: %s\n", msgtypes[rtm->rtm_type],
279		rtm->rtm_msglen, rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno, flags_buf);
280
281	if (rtm->rtm_inits > 0) {
282		_printb(flags_buf, sizeof(flags_buf), rtm->rtm_inits, metricnames);
283		printf("metrics: %s\n", flags_buf);
284		if (rtm->rtm_inits & RTV_MTU)
285			printf("mtu: %lu\n", rtm->rtm_rmx.rmx_mtu);
286		if (rtm->rtm_inits & RTV_EXPIRE) {
287			struct timeval tv;
288			gettimeofday(&tv, NULL);
289			printf("expire: %d (%lu raw)\n",
290			    (int)(rtm->rtm_rmx.rmx_expire - tv.tv_sec), rtm->rtm_rmx.rmx_expire);
291		}
292	}
293
294	_printb(flags_buf, sizeof(flags_buf), rtm->rtm_addrs, addrnames);
295	printf("sockaddrs: 0x%X %s\n", rtm->rtm_addrs, flags_buf);
296
297	char *ptr = (char *)(rtm + 1);
298	for (int i = 0; i < RTAX_MAX; i++) {
299		if (rtm->rtm_addrs & (1 << i)) {
300			struct sockaddr *sa = (struct sockaddr *)ptr;
301			sa_print(sa, 1);
302
303			/* add */
304			ptr += ALIGN(((struct sockaddr *)ptr)->sa_len);
305		}
306	}
307
308	printf("\n");
309
310}
311
312void
313rtsock_print_ifa(struct ifa_msghdr *ifam)
314{
315	struct timeval tv;
316	struct tm tm_res;
317	char buf[64];
318
319	gettimeofday(&tv, NULL);
320	localtime_r(&tv.tv_sec, &tm_res);
321	strftime(buf, sizeof(buf), "%F %T", &tm_res);
322	printf("Got message of size %hu on %s\n", ifam->ifam_msglen, buf);
323
324	char flags_buf[256];
325	_printb(flags_buf, sizeof(flags_buf), ifam->ifam_flags, routeflags);
326
327	printf("%s: len %hu, ifindex: %d, flags: %s\n", msgtypes[ifam->ifam_type],
328		ifam->ifam_msglen, ifam->ifam_index, flags_buf);
329
330	_printb(flags_buf, sizeof(flags_buf), ifam->ifam_addrs, addrnames);
331	printf("sockaddrs: 0x%X %s\n", ifam->ifam_addrs, flags_buf);
332
333	char *ptr = (char *)(ifam + 1);
334	for (int i = 0; i < RTAX_MAX; i++) {
335		if (ifam->ifam_addrs & (1 << i)) {
336			struct sockaddr *sa = (struct sockaddr *)ptr;
337			sa_print(sa, 1);
338
339			/* add */
340			ptr += ALIGN(((struct sockaddr *)ptr)->sa_len);
341		}
342	}
343
344	printf("\n");
345
346}
347
348void
349rtsock_print_message_hd(struct rt_msghdr *rtm)
350{
351	struct timeval tv;
352	struct tm tm_res;
353	char buf[64];
354	char dumpbuf[2048];
355
356	gettimeofday(&tv, NULL);
357	localtime_r(&tv.tv_sec, &tm_res);
358	strftime(buf, sizeof(buf), "%F %T", &tm_res);
359	printf("Got message type %s of size %hu on %s\n",
360	    rtsock_print_cmdtype(rtm->rtm_type),
361	    rtm->rtm_msglen, buf);
362
363	sa_print_hd(dumpbuf, sizeof(dumpbuf), (char *)rtm, rtm->rtm_msglen);
364	printf(" %s\n", dumpbuf);
365}
366
367void
368rtsock_print_message(struct rt_msghdr *rtm)
369{
370
371	switch (rtm->rtm_type) {
372	case RTM_GET:
373	case RTM_ADD:
374	case RTM_DELETE:
375	case RTM_CHANGE:
376		rtsock_print_rtm(rtm);
377		break;
378	case RTM_DELADDR:
379	case RTM_NEWADDR:
380		rtsock_print_ifa((struct ifa_msghdr *)rtm);
381		break;
382	default:
383		printf("unknown rt message type %X\n", rtm->rtm_type);
384	}
385}
386
387static void
388print_command(char *cmd)
389{
390	char line[1024];
391
392	FILE *fp = popen(cmd, "r");
393	if (fp != NULL) {
394		while (fgets(line, sizeof(line), fp) != NULL)
395			printf("%s", line);
396		pclose(fp);
397	}
398}
399
400void
401rtsock_print_table(int family)
402{
403	char cmdbuf[128];
404	char *key = (family == AF_INET) ? "4" : "6";
405
406	snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/netstat -%srnW", key);
407	printf("==== %s ===\n", cmdbuf);
408	print_command(cmdbuf);
409	snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/netstat -%sonW", key);
410	printf("==== %s ===\n", cmdbuf);
411	print_command(cmdbuf);
412}
413
414#endif
415