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
dlrecv_usage(const char * fmt,...)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
dlrecv_isvalid(dlsend_msg_t * msg)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
dlrecv_print(dlsend_msg_t * msg,dlpi_recvinfo_t * rinfo,boolean_t invalid)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
main(int argc,char * argv[])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