xref: /illumos-gate/usr/src/cmd/dlutil/dlrecv.c (revision 8077c9b112b747853b56a588796b7a819da18f2a)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2018, Joyent, Inc.
14  */
15 
16 /*
17  * Receive a raw Ethernet frame from dlsend.
18  */
19 
20 #include <stdio.h>
21 #include <errno.h>
22 #include <strings.h>
23 #include <unistd.h>
24 #include <stdarg.h>
25 #include <libgen.h>
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <netdb.h>
30 #include <libdlpi.h>
31 #include <stddef.h>
32 #include <stdint.h>
33 #include <endian.h>
34 #include <ctype.h>
35 #include <err.h>
36 
37 #include "dlsend.h"
38 
39 
40 static uint_t dlrecv_sap = DLSEND_SAP;
41 static const char *dlrecv_prog;
42 
43 static void
44 dlrecv_usage(const char *fmt, ...)
45 {
46 	if (fmt != NULL) {
47 		va_list ap;
48 
49 		(void) fprintf(stderr, "%s: ", dlrecv_prog);
50 		va_start(ap, fmt);
51 		(void) vfprintf(stderr, fmt, ap);
52 		va_end(ap);
53 	}
54 
55 	(void) fprintf(stderr, "Usage: %s [-s sap] device\n"
56 	    "\t-s sap\tspecify SAP to send on\n",
57 	    dlrecv_prog);
58 }
59 
60 static boolean_t
61 dlrecv_isvalid(dlsend_msg_t *msg)
62 {
63 	uint_t i;
64 	boolean_t nul;
65 
66 	nul = B_FALSE;
67 	for (i = 0; i < sizeof (msg->dm_host); i++) {
68 		if (!isprint(msg->dm_host[i]) &&
69 		    msg->dm_host[i] != '\0') {
70 			warnx("Encountered bad byte in dm_host[%d]",
71 			    i);
72 			return (B_FALSE);
73 		}
74 
75 		if (msg->dm_host[i] == '\0')
76 			nul = B_TRUE;
77 	}
78 
79 	if (!nul) {
80 		warnx("Missing NUL in dm_host");
81 		return (B_FALSE);
82 	}
83 
84 	nul = B_FALSE;
85 	for (i = 0; i < sizeof (msg->dm_mesg); i++) {
86 		if (!isprint(msg->dm_mesg[i]) &&
87 		    msg->dm_mesg[i] != '\0') {
88 			warnx("Encountered bad byte in dm_mesg[%d]",
89 			    i);
90 			return (B_FALSE);
91 		}
92 
93 		if (msg->dm_mesg[i] == '\0')
94 			nul = B_TRUE;
95 	}
96 
97 	if (!nul) {
98 		warnx("Missing NUL in dm_mesg");
99 		return (B_FALSE);
100 	}
101 
102 	if (strcmp(msg->dm_mesg, DLSEND_MSG) != 0) {
103 		warnx("Missing expected message (%s)", DLSEND_MSG);
104 		return (B_FALSE);
105 	}
106 
107 	return (B_TRUE);
108 }
109 
110 static void
111 dlrecv_print(dlsend_msg_t *msg, dlpi_recvinfo_t *rinfo, boolean_t invalid)
112 {
113 	uint_t i;
114 
115 	(void) printf("Received %s from ", invalid ?
116 	    "invalid message" : "Elbereth");
117 
118 	for (i = 0; i < rinfo->dri_destaddrlen; i++) {
119 		(void) printf("%02x", rinfo->dri_destaddr[i]);
120 		if (i + 1 != rinfo->dri_destaddrlen)
121 			(void) putchar(':');
122 	}
123 
124 	if (invalid) {
125 		return;
126 	}
127 
128 	(void) printf(" seq=%" PRIu64 " host=%s\n", betoh64(msg->dm_count),
129 	    msg->dm_host);
130 }
131 
132 int
133 main(int argc, char *argv[])
134 {
135 	int c, ret;
136 	char *eptr;
137 	unsigned long sap;
138 	uint_t bind_sap;
139 	dlpi_handle_t dh;
140 
141 	dlrecv_prog = basename(argv[0]);
142 
143 	while ((c = getopt(argc, argv, ":s:")) != -1) {
144 		switch (c) {
145 		case 's':
146 			errno = 0;
147 			sap = strtoul(optarg, &eptr, 10);
148 			if (errno != 0 || sap == 0 || sap >= UINT16_MAX ||
149 			    *eptr != '\0') {
150 				dlrecv_usage("Invalid value for sap (-s): %s\n",
151 				    optarg);
152 				return (2);
153 			}
154 			dlrecv_sap = sap;
155 			break;
156 		case ':':
157 			dlrecv_usage("Option -%c requires an operand\n",
158 			    optopt);
159 			return (2);
160 		case '?':
161 			dlrecv_usage("Unknown option: -%c\n", optopt);
162 			return (2);
163 		}
164 	}
165 
166 	argc -= optind;
167 	argv += optind;
168 
169 	if (argc != 1) {
170 		dlrecv_usage("missing required operands\n");
171 		return (2);
172 	}
173 
174 	if ((ret = dlpi_open(argv[0], &dh, 0)) != DLPI_SUCCESS) {
175 		warnx("failed to open %s: %s", argv[0],
176 		    dlpi_strerror(ret));
177 		exit(1);
178 	}
179 
180 	if ((ret = dlpi_bind(dh, dlrecv_sap, &bind_sap)) != DLPI_SUCCESS) {
181 		warnx("failed to bind to sap 0x%x: %s", dlrecv_sap,
182 		    dlpi_strerror(ret));
183 		exit(1);
184 	}
185 
186 	if (bind_sap != dlrecv_sap) {
187 		warnx("failed to bind to requested sap 0x%x, bound to "
188 		    "0x%x", dlrecv_sap, bind_sap);
189 		exit(1);
190 	}
191 
192 	for (;;) {
193 		dlpi_recvinfo_t rinfo;
194 		dlsend_msg_t msg;
195 		size_t msglen;
196 		boolean_t invalid = B_FALSE;
197 
198 		msglen = sizeof (msg);
199 		ret = dlpi_recv(dh, NULL, NULL, &msg, &msglen, -1, &rinfo);
200 		if (ret != DLPI_SUCCESS) {
201 			warnx("failed to receive data: %s", dlpi_strerror(ret));
202 			continue;
203 		}
204 
205 		if (msglen != rinfo.dri_totmsglen) {
206 			warnx("message truncated: expected %zu bytes, "
207 			    "got %zu", sizeof (dlsend_msg_t),
208 			    rinfo.dri_totmsglen);
209 			invalid = B_TRUE;
210 		}
211 
212 		if (msglen != sizeof (msg)) {
213 			warnx("message too short: expected %zu bytes, "
214 			    "got %zu", sizeof (dlsend_msg_t), msglen);
215 			invalid = B_TRUE;
216 		}
217 
218 		if (!invalid) {
219 			invalid = !dlrecv_isvalid(&msg);
220 		}
221 
222 		dlrecv_print(&msg, &rinfo, invalid);
223 	}
224 
225 	/* LINTED: E_STMT_NOT_REACHED */
226 	return (0);
227 }
228