Update README.md
[oweals/nmrpflash.git] / nmrp.c
1 /**
2  * nmrpflash - Netgear Unbrick Utility
3  * Copyright (C) 2016 Joseph Lehner <joseph.c.lehner@gmail.com>
4  *
5  * nmrpflash is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * nmrpflash is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with nmrpflash.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <time.h>
27 #include "nmrpd.h"
28
29 #define NMRP_HDR_LEN 6
30 #define NMRP_OPT_HDR_LEN 4
31 #define NMRP_MIN_PKT_LEN (sizeof(struct eth_hdr) +  NMRP_HDR_LEN)
32
33 #define ETH_P_NMRP 0x0912
34
35 #ifndef PACKED
36 #define PACKED __attribute__((__packed__))
37 #endif
38
39 #ifdef NMRPFLASH_WINDOWS
40 #define setenv(name, value, overwrite) SetEnvironmentVariable(name, value)
41 #endif
42
43 enum nmrp_code {
44         NMRP_C_NONE = 0,
45         NMRP_C_ADVERTISE = 1,
46         NMRP_C_CONF_REQ = 2,
47         NMRP_C_CONF_ACK = 3,
48         NMRP_C_CLOSE_REQ = 4,
49         NMRP_C_CLOSE_ACK = 5,
50         NMRP_C_KEEP_ALIVE_REQ = 6,
51         NMRP_C_KEEP_ALIVE_ACK = 7,
52         NMRP_C_TFTP_UL_REQ = 16
53 };
54
55 enum nmrp_opt_type {
56         NMRP_O_MAGIC_NO = 0x0001,
57         NMRP_O_DEV_IP = 0x0002,
58         NMRP_O_DEV_REGION = 0x0004,
59         NMRP_O_FW_UP = 0x0101,
60         NMRP_O_ST_UP = 0x0102,
61         NMRP_O_FILE_NAME = 0x0181
62 };
63
64 struct nmrp_opt {
65         uint16_t type;
66         uint16_t len;
67         char val[1];
68 } PACKED;
69
70 struct nmrp_msg {
71         uint16_t reserved;
72         uint8_t code;
73         uint8_t id;
74         uint16_t len;
75         char opts[44];
76 } PACKED;
77
78 struct nmrp_pkt {
79         struct eth_hdr eh;
80         struct nmrp_msg msg;
81 } PACKED;
82
83 static const char *msg_code_str(uint16_t code)
84 {
85 #define MSG_CODE(x) case NMRP_C_ ## x: return #x
86         static char buf[16];
87
88         switch (code) {
89                 MSG_CODE(ADVERTISE);
90                 MSG_CODE(CONF_REQ);
91                 MSG_CODE(CONF_ACK);
92                 MSG_CODE(CLOSE_REQ);
93                 MSG_CODE(CLOSE_ACK);
94                 MSG_CODE(KEEP_ALIVE_REQ);
95                 MSG_CODE(KEEP_ALIVE_ACK);
96                 MSG_CODE(TFTP_UL_REQ);
97                 default:
98                         snprintf(buf, sizeof(buf), "%04x", ntohs(code));
99                         return buf;
100         }
101 #undef MSG_CODE
102 }
103
104 static uint16_t to_region_code(const char *region)
105 {
106 #define REGION_CODE(r, c) if (!strcasecmp(region, r)) return htons(c)
107         REGION_CODE("NA", 0x0001);
108         REGION_CODE("WW", 0x0002);
109         REGION_CODE("GR", 0x0003);
110         REGION_CODE("PR", 0x0004);
111         REGION_CODE("RU", 0x0005);
112         REGION_CODE("BZ", 0x0006);
113         REGION_CODE("IN", 0x0007);
114         REGION_CODE("KO", 0x0008);
115         REGION_CODE("JP", 0x0009);
116 #undef REGION_CODE
117         return 0;
118 }
119
120 static void msg_dump(struct nmrp_msg *msg)
121 {
122         int rem;
123
124         fprintf(stderr, "res=0x%04x, code=0x%02x, id=0x%02x, len=%u",
125                         ntohs(msg->reserved), msg->code, msg->id, ntohs(msg->len));
126
127         rem = ntohs(msg->len) - NMRP_HDR_LEN;
128         fprintf(stderr, "%s\n", rem ? "" : " (no opts)");
129 }
130
131 static void *msg_opt(struct nmrp_msg *msg, uint16_t type, uint16_t* len)
132 {
133         struct nmrp_opt* opt = (struct nmrp_opt*)msg->opts;
134         size_t rem = ntohs(msg->len) - NMRP_HDR_LEN;
135         uint16_t olen;
136
137         do {
138                 olen = ntohs(opt->len);
139                 if (olen < NMRP_OPT_HDR_LEN || olen > rem) {
140                         break;
141                 }
142
143                 if (ntohs(opt->type) == type) {
144                         if (len) {
145                                 *len = olen;
146                         }
147
148                         return opt->val;
149                 }
150
151                 opt = (struct nmrp_opt*)(((char *)opt) + olen);
152                 rem -= olen;
153         } while (rem);
154
155         return NULL;
156 }
157
158 static char *msg_filename(struct nmrp_msg *msg)
159 {
160         static char buf[256];
161         uint16_t len;
162         char *p = msg_opt(msg, NMRP_O_FILE_NAME, &len);
163         if (p) {
164                 len = MIN(sizeof(buf) - 1, len);
165                 memcpy(buf, p, len);
166                 buf[len] = '\0';
167                 return buf;
168         }
169
170         return NULL;
171 }
172
173 static inline void msg_init(struct nmrp_msg *msg, uint16_t code)
174 {
175         memset(msg, 0, sizeof(*msg));
176         msg->len = htons(NMRP_HDR_LEN);
177         msg->code = code;
178 }
179
180 static char *msg_mkopt(struct nmrp_msg *msg, char *p, uint16_t type, const void *val, size_t len)
181 {
182         struct nmrp_opt* opt = (struct nmrp_opt*)p;
183
184         len &= 0xffff;
185
186         msg->len = ntohs(msg->len);
187
188         if ((msg->len + len > sizeof(*msg))) {
189                 fprintf(stderr, "Error: invalid option - this is a bug\n");
190                 exit(1);
191         }
192
193         opt->type = htons(type);
194         opt->len = NMRP_OPT_HDR_LEN + len;
195
196         if (val) {
197                 memcpy(opt->val, val, len);
198         }
199
200         msg->len += opt->len;
201         p += opt->len;
202
203         msg->len = htons(msg->len);
204         opt->len = htons(opt->len);
205
206         return p;
207 }
208
209 static void msg_mkadvertise(struct nmrp_msg *msg, const char *magic)
210 {
211         msg_init(msg, NMRP_C_ADVERTISE);
212         msg_mkopt(msg, msg->opts, NMRP_O_MAGIC_NO, magic, strlen(magic));
213 }
214
215 static void msg_mkconfack(struct nmrp_msg *msg, uint32_t ipaddr, uint32_t ipmask, uint16_t region)
216 {
217         char *p;
218         uint32_t ip[2] = { ipaddr, ipmask };
219
220         msg_init(msg, NMRP_C_CONF_ACK);
221         p = msg_mkopt(msg, msg->opts, NMRP_O_DEV_IP, &ip, 8);
222         p = msg_mkopt(msg, p, NMRP_O_FW_UP, NULL, 0);
223
224 #ifdef NMRPFLASH_SET_REGION
225         if (region) {
226                 p = msg_mkopt(msg, p, NMRP_O_DEV_REGION, &region, 2);
227         }
228 #endif
229 }
230
231 #ifdef NMRPFLASH_FUZZ
232 #define NMRP_INITIAL_TIMEOUT 0
233 #define ethsock_create(a, b) ((struct ethsock*)1)
234 #define ethsock_get_hwaddr(a) ethsock_get_hwaddr_fake(a)
235 #define ethsock_recv(sock, buf, len) read(STDIN_FILENO, buf, len)
236 #define ethsock_send(a, b, c) (0)
237 #define ethsock_set_timeout(a, b) (0)
238 #define ethsock_arp_add(a, b, c, d) (0)
239 #define ethsock_arp_del(a, b) (0)
240 #define ethsock_ip_add(a, b, c, d) (0)
241 #define ethsock_ip_del(a, b) (0)
242 #define ethsock_close(a) (0)
243 #define tftp_put(a) (0)
244
245 static uint8_t *ethsock_get_hwaddr_fake(struct ethsock* sock)
246 {
247         static uint8_t hwaddr[6] = { 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa };
248         return hwaddr;
249 }
250 #else
251 #define NMRP_INITIAL_TIMEOUT 60
252 #endif
253
254 static int pkt_send(struct ethsock *sock, struct nmrp_pkt *pkt)
255 {
256         return ethsock_send(sock, pkt, sizeof(*pkt));
257 }
258
259 static int pkt_recv(struct ethsock *sock, struct nmrp_pkt *pkt)
260 {
261         ssize_t bytes, mlen;
262
263         memset(pkt, 0, sizeof(*pkt));
264         bytes = ethsock_recv(sock, pkt, sizeof(*pkt));
265         if (bytes < 0) {
266                 return 1;
267         } else if (!bytes) {
268                 return 2;
269         }
270
271         mlen = ntohs(pkt->msg.len);
272
273         if (bytes < (mlen + sizeof(pkt->eh))
274                         || bytes < NMRP_MIN_PKT_LEN
275                         || mlen < NMRP_HDR_LEN) {
276                 fprintf(stderr, "Short packet (%d raw, %d message)\n",
277                                 (int)bytes, (int)mlen);
278                 return 1;
279         } else if (mlen > sizeof(pkt->msg)) {
280                 printf("Truncating %d byte message.\n", (int)mlen);
281                 pkt->msg.len = htons(sizeof(pkt->msg));
282         }
283
284         return 0;
285 }
286
287 static int mac_parse(const char *str, uint8_t *hwaddr)
288 {
289         int i;
290         unsigned data[6];
291
292         sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x%n",
293                         data, data + 1, data + 2, data + 3, data + 4, data + 5, &i);
294
295         if (i == strlen(str)) {
296                 for (i = 0; i != 6; ++i) {
297                         if (data[i] > 255) {
298                                 break;
299                         }
300
301                         hwaddr[i] = data[i] & 0xff;
302                 }
303
304                 if (i == 6) {
305                         return 1;
306                 }
307         }
308         return 0;
309 }
310
311 struct is_valid_ip_arg
312 {
313         struct in_addr *ipaddr;
314         struct in_addr *ipmask;
315         int result;
316 };
317
318 static int is_valid_ip_cb(struct ethsock_ip_callback_args *args)
319 {
320 #define SUBNET(x) ((x)->ipaddr->s_addr & (x)->ipmask->s_addr)
321         struct is_valid_ip_arg *arg = args->arg;
322         if (SUBNET(args) == SUBNET(arg)) {
323                 arg->result = args->ipaddr->s_addr != arg->ipaddr->s_addr;
324                 return 0;
325         }
326
327         return 1;
328 #undef SUBNET
329 }
330
331 static int is_valid_ip(struct ethsock *sock, struct in_addr *ipaddr,
332                 struct in_addr *ipmask)
333 {
334         int status;
335         struct is_valid_ip_arg arg = {
336                 .ipaddr = ipaddr,
337                 .ipmask = ipmask,
338                 .result = 0
339         };
340
341         status = ethsock_for_each_ip(sock, is_valid_ip_cb, &arg);
342         return status < 0 ? status : arg.result;
343 }
344
345 static void sigh(int sig)
346 {
347         g_interrupted = 1;
348 }
349
350 static const char *spinner = "\\|/-";
351
352 int nmrp_do(struct nmrpd_args *args)
353 {
354         struct nmrp_pkt tx, rx;
355         uint8_t *src, dest[6];
356         uint16_t region;
357         char *filename;
358         time_t beg;
359         int i, timeout, status, ulreqs, expect, upload_ok, autoip, ka_reqs, fake;
360         struct ethsock *sock;
361         struct ethsock_ip_undo *ip_undo = NULL;
362         struct ethsock_arp_undo *arp_undo = NULL;
363         uint32_t intf_addr = 0;
364         void (*sigh_orig)(int);
365         struct in_addr ipaddr;
366         struct in_addr ipmask;
367
368         if (args->op != NMRP_UPLOAD_FW) {
369                 fprintf(stderr, "Operation not implemented.\n");
370                 return 1;
371         }
372
373         if (!mac_parse(args->mac, dest)) {
374                 fprintf(stderr, "Invalid MAC address '%s'.\n", args->mac);
375                 return 1;
376         }
377
378         ipmask.s_addr = inet_addr(args->ipmask);
379         if (ipmask.s_addr == INADDR_NONE
380                         || netmask(bitcount(ipmask.s_addr)) != ipmask.s_addr) {
381                 fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask);
382                 return 1;
383         }
384
385         if (!args->ipaddr) {
386                 autoip = true;
387                 /* A random IP address. The MAC of the first device that was
388                  * used to test this utility starts with a4:2b:8c, so we use
389                  * 164 (0xa4) and 183 (0x2b + 0x8c).
390                  *
391                  * These addresses should not cause collisions on most networks,
392                  * and if they do, the user is probably "poweruser" enough to
393                  * be able to use the -a and -A options.
394                  */
395                 args->ipaddr = "10.164.183.252";
396
397                 if (!args->ipaddr_intf) {
398                         args->ipaddr_intf = "10.164.183.253";
399                 }
400         } else if (args->ipaddr_intf) {
401                 autoip = true;
402         } else {
403                 autoip = false;
404         }
405
406         if ((ipaddr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) {
407                 fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr);
408                 return 1;
409         }
410
411         if (args->ipaddr_intf && (intf_addr = inet_addr(args->ipaddr_intf)) == INADDR_NONE) {
412                 fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr_intf);
413                 return 1;
414         }
415
416         if (args->file_local && strcmp(args->file_local, "-") && access(args->file_local, R_OK) == -1) {
417                 fprintf(stderr, "Error accessing file '%s'.\n", args->file_local);
418                 return 1;
419         }
420
421         if (args->file_remote) {
422                 if (!tftp_is_valid_filename(args->file_remote)) {
423                         fprintf(stderr, "Invalid remote filename '%s'.\n",
424                                         args->file_remote);
425                         return 1;
426                 }
427         }
428
429         if (args->region) {
430                 region = to_region_code(args->region);
431                 if (!region) {
432                         fprintf(stderr, "Invalid region code '%s'.\n", args->region);
433                         return 1;
434                 }
435         } else {
436                 region = 0;
437         }
438
439         status = 1;
440
441         sock = ethsock_create(args->intf, ETH_P_NMRP);
442         if (!sock) {
443                 return 1;
444         }
445
446         sigh_orig = signal(SIGINT, sigh);
447
448         if (!autoip) {
449                 status = is_valid_ip(sock, &ipaddr, &ipmask);
450                 if (status <= 0) {
451                         if (!status) {
452                                 fprintf(stderr, "Address %s/%s cannot be used on interface %s.\n",
453                                                 args->ipaddr, args->ipmask, args->intf);
454                         }
455                         goto out;
456                 }
457         } else {
458                 if (verbosity) {
459                         printf("Adding %s to interface %s.\n", args->ipaddr_intf, args->intf);
460                 }
461
462                 if (ethsock_ip_add(sock, intf_addr, ipmask.s_addr, &ip_undo) != 0) {
463                         goto out;
464                 }
465         }
466
467         if (ethsock_set_timeout(sock, args->rx_timeout)) {
468                 goto out;
469         }
470
471         src = ethsock_get_hwaddr(sock);
472         if (!src) {
473                 goto out;
474         }
475
476         memcpy(tx.eh.ether_shost, src, 6);
477         memcpy(tx.eh.ether_dhost, dest, 6);
478         tx.eh.ether_type = htons(ETH_P_NMRP);
479
480         msg_mkadvertise(&tx.msg, "NTGR");
481
482         i = 0;
483         upload_ok = 0;
484         fake = 0;
485         timeout = args->blind ? 10 : NMRP_INITIAL_TIMEOUT;
486         beg = time_monotonic();
487
488         while (!g_interrupted) {
489                 printf("\rAdvertising NMRP server on %s ... %c",
490                                 args->intf, spinner[i]);
491                 fflush(stdout);
492                 i = (i + 1) & 3;
493
494                 if (pkt_send(sock, &tx) < 0) {
495                         goto out;
496                 }
497
498                 status = pkt_recv(sock, &rx);
499                 if (status == 0) {
500                         if (memcmp(rx.eh.ether_dhost, src, 6) == 0) {
501                                 break;
502                         } else if (verbosity) {
503                                 printf("\nIgnoring bogus response: %s -> %s.\n",
504                                                 mac_to_str(rx.eh.ether_shost),
505                                                 mac_to_str(rx.eh.ether_dhost));
506                         }
507                 } else if (status == 1) {
508                         goto out;
509                 } else {
510                         /* because we don't want nmrpflash's exit status to be zero */
511                         status = 1;
512                         if ((time_monotonic() - beg) >= timeout) {
513                                 printf("\nNo response after %d seconds. ", timeout);
514                                 if (!args->blind) {
515                                         printf("Bailing out.\n");
516                                         goto out;
517                                 } else {
518                                         // we're blind, so fake a response from the MAC specified by -m
519                                         memcpy(rx.eh.ether_shost, dest, 6);
520                                         msg_init(&rx.msg, NMRP_C_CONF_REQ);
521                                         printf("Continuing blindly.");
522                                         fake = 1;
523                                         break;
524                                 }
525                         }
526                 }
527         }
528
529         printf("\n");
530
531         memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, 6);
532
533         if (ethsock_arp_add(sock, rx.eh.ether_shost, ipaddr.s_addr, &arp_undo) != 0) {
534                 goto out;
535         }
536
537         expect = NMRP_C_CONF_REQ;
538         ulreqs = 0;
539         ka_reqs = 0;
540
541         while (!g_interrupted) {
542                 if (expect != NMRP_C_NONE && rx.msg.code != expect) {
543                         fprintf(stderr, "Received %s while waiting for %s!\n",
544                                         msg_code_str(rx.msg.code), msg_code_str(expect));
545                 }
546
547                 msg_init(&tx.msg, NMRP_C_NONE);
548
549                 status = 1;
550
551                 switch (rx.msg.code) {
552                         case NMRP_C_ADVERTISE:
553                                 printf("Received NMRP advertisement from %s.\n",
554                                                 mac_to_str(rx.eh.ether_shost));
555                                 status = 1;
556                                 goto out;
557                         case NMRP_C_CONF_REQ:
558                                 msg_mkconfack(&tx.msg, ipaddr.s_addr, ipmask.s_addr, region);
559                                 expect = NMRP_C_TFTP_UL_REQ;
560
561                                 if (!fake) {
562                                         printf("Received configuration request from %s.\n",
563                                                         mac_to_str(rx.eh.ether_shost));
564                                 }
565
566                                 printf("Sending configuration: %s/%d.\n",
567                                                 args->ipaddr, bitcount(ipmask.s_addr));
568
569                                 break;
570                         case NMRP_C_TFTP_UL_REQ:
571                                 if (!upload_ok) {
572                                         if (++ulreqs > 5) {
573                                                 printf("Bailing out after %d upload requests.\n",
574                                                                 ulreqs);
575                                                 tx.msg.code = NMRP_C_CLOSE_REQ;
576                                                 break;
577                                         }
578                                 } else {
579                                         if (verbosity) {
580                                                 printf("Ignoring extra upload request.\n");
581                                         }
582                                         ethsock_set_timeout(sock, args->ul_timeout);
583                                         tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
584                                         break;
585                                 }
586
587                                 filename = msg_filename(&rx.msg);
588                                 if (filename) {
589                                         if (!args->file_remote) {
590                                                 args->file_remote = filename;
591                                         }
592                                         printf("Received upload request: filename '%s'.\n", filename);
593                                 } else if (!args->file_remote) {
594                                         args->file_remote = leafname(args->file_local);
595                                         if (!fake) {
596                                                 printf("Received upload request without filename.\n");
597                                         }
598                                 }
599
600                                 status = 0;
601
602                                 if (args->tftpcmd) {
603                                         printf("Executing '%s' ... \n", args->tftpcmd);
604                                         setenv("IP", inet_ntoa(ipaddr), 1);
605                                         setenv("PORT", lltostr(args->port, 10), 1);
606                                         setenv("MAC", mac_to_str(rx.eh.ether_shost), 1);
607                                         setenv("NETMASK", inet_ntoa(ipmask), 1);
608                                         //setenv("FILENAME", args->file_remote ? args->file_remote : "", 1);
609                                         status = system(args->tftpcmd);
610                                 }
611
612                                 if (!status && args->file_local) {
613                                         if (!autoip) {
614                                                 status = is_valid_ip(sock, &ipaddr, &ipmask);
615                                                 if (status < 0) {
616                                                         goto out;
617                                                 } else if (!status) {
618                                                         printf("IP address of %s has changed. Please assign a "
619                                                                         "static ip to the interface.\n", args->intf);
620                                                         tx.msg.code = NMRP_C_CLOSE_REQ;
621                                                         break;
622                                                 }
623                                         }
624
625                                         if (verbosity) {
626                                                 printf("Using remote filename '%s'.\n",
627                                                                 args->file_remote);
628                                         }
629
630                                         if (!strcmp(args->file_local, "-")) {
631                                                 printf("Uploading from stdin ... ");
632                                         } else {
633                                                 printf("Uploading %s ... ", leafname(args->file_local));
634                                         }
635                                         fflush(stdout);
636                                         if (!(status = tftp_put(args))) {
637                                                 printf("OK\n");
638                                         }
639
640                                 }
641
642                                 if (!status) {
643                                         if (args->blind) {
644                                                 goto out;
645                                         }
646
647                                         printf("Waiting for remote to respond.\n");
648                                         upload_ok = 1;
649                                         ethsock_set_timeout(sock, args->ul_timeout);
650                                         tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
651                                         expect = NMRP_C_NONE;
652                                 } else if (status == -2) {
653                                         expect = NMRP_C_TFTP_UL_REQ;
654                                 } else {
655                                         goto out;
656                                 }
657
658                                 break;
659                         case NMRP_C_KEEP_ALIVE_REQ:
660                                 tx.msg.code = NMRP_C_KEEP_ALIVE_ACK;
661                                 ethsock_set_timeout(sock, args->ul_timeout);
662                                 printf("\rReceived keep-alive request (%d).  ", ++ka_reqs);
663                                 break;
664                         case NMRP_C_CLOSE_REQ:
665                                 tx.msg.code = NMRP_C_CLOSE_ACK;
666                                 break;
667                         case NMRP_C_CLOSE_ACK:
668                                 status = 0;
669                                 goto out;
670                         default:
671                                 fprintf(stderr, "Unknown message code 0x%02x!\n",
672                                                 rx.msg.code);
673                                 msg_dump(&rx.msg);
674                 }
675
676                 if (tx.msg.code != NMRP_C_NONE) {
677                         if (pkt_send(sock, &tx) != 0 || tx.msg.code == NMRP_C_CLOSE_REQ) {
678                                 goto out;
679                         }
680                 }
681
682                 if (rx.msg.code == NMRP_C_CLOSE_REQ) {
683                         if (ka_reqs) {
684                                 printf("\n");
685                         }
686
687                         printf("Remote finished. Closing connection.\n");
688                         break;
689                 }
690
691                 status = pkt_recv(sock, &rx);
692                 if (status) {
693                         if (status == 2) {
694                                 if (!args->blind) {
695                                         fprintf(stderr, "Timeout while waiting for %s.\n",
696                                                         msg_code_str(expect));
697                                         goto out;
698                                 }
699
700                                 // fake response
701                                 msg_init(&rx.msg, expect);
702                                 memcpy(rx.eh.ether_shost, tx.eh.ether_dhost, 6);
703                                 fake = 1;
704                         } else {
705                                 goto out;
706                         }
707                 } else {
708                         fake = 0;
709                 }
710
711                 ethsock_set_timeout(sock, args->rx_timeout);
712
713         }
714
715         if (!g_interrupted) {
716                 status = 0;
717                 if (ulreqs) {
718                         printf("Reboot your device now.\n");
719                 } else {
720                         printf("No upload request received.\n");
721                 }
722         }
723
724 out:
725         signal(SIGINT, sigh_orig);
726         ethsock_arp_del(sock, &arp_undo);
727         ethsock_ip_del(sock, &ip_undo);
728         ethsock_close(sock);
729         return status;
730 }