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