2 * nmrpflash - Netgear Unbrick Utility
3 * Copyright (C) 2016 Joseph Lehner <joseph.c.lehner@gmail.com>
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.
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.
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/>.
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)
33 #define ETH_P_NMRP 0x0912
36 #define PACKED __attribute__((__packed__))
39 #ifdef NMRPFLASH_WINDOWS
40 #define setenv(name, value, overwrite) SetEnvironmentVariable(name, value)
50 NMRP_C_KEEP_ALIVE_REQ = 6,
51 NMRP_C_KEEP_ALIVE_ACK = 7,
52 NMRP_C_TFTP_UL_REQ = 16
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
83 static const char *msg_code_str(uint16_t code)
85 #define MSG_CODE(x) case NMRP_C_ ## x: return #x
94 MSG_CODE(KEEP_ALIVE_REQ);
95 MSG_CODE(KEEP_ALIVE_ACK);
96 MSG_CODE(TFTP_UL_REQ);
98 snprintf(buf, sizeof(buf), "%04x", ntohs(code));
104 static uint16_t to_region_code(const char *region)
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);
120 static void msg_dump(struct nmrp_msg *msg)
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));
127 rem = ntohs(msg->len) - NMRP_HDR_LEN;
128 fprintf(stderr, "%s\n", rem ? "" : " (no opts)");
131 static void *msg_opt(struct nmrp_msg *msg, uint16_t type, uint16_t* len)
133 struct nmrp_opt* opt = (struct nmrp_opt*)msg->opts;
134 size_t rem = ntohs(msg->len) - NMRP_HDR_LEN;
138 olen = ntohs(opt->len);
139 if (olen < NMRP_OPT_HDR_LEN || olen > rem) {
143 if (ntohs(opt->type) == type) {
151 opt = (struct nmrp_opt*)(((char *)opt) + olen);
158 static char *msg_filename(struct nmrp_msg *msg)
160 static char buf[256];
162 char *p = msg_opt(msg, NMRP_O_FILE_NAME, &len);
164 len = MIN(sizeof(buf) - 1, len);
173 static inline void msg_init(struct nmrp_msg *msg, uint16_t code)
175 memset(msg, 0, sizeof(*msg));
176 msg->len = htons(NMRP_HDR_LEN);
180 static char *msg_mkopt(struct nmrp_msg *msg, char *p, uint16_t type, const void *val, size_t len)
182 struct nmrp_opt* opt = (struct nmrp_opt*)p;
186 msg->len = ntohs(msg->len);
188 if ((msg->len + len > sizeof(*msg))) {
189 fprintf(stderr, "Error: invalid option - this is a bug\n");
193 opt->type = htons(type);
194 opt->len = NMRP_OPT_HDR_LEN + len;
197 memcpy(opt->val, val, len);
200 msg->len += opt->len;
203 msg->len = htons(msg->len);
204 opt->len = htons(opt->len);
209 static void msg_mkadvertise(struct nmrp_msg *msg, const char *magic)
211 msg_init(msg, NMRP_C_ADVERTISE);
212 msg_mkopt(msg, msg->opts, NMRP_O_MAGIC_NO, magic, strlen(magic));
215 static void msg_mkconfack(struct nmrp_msg *msg, uint32_t ipaddr, uint32_t ipmask, uint16_t region)
218 uint32_t ip[2] = { ipaddr, ipmask };
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);
224 #ifdef NMRPFLASH_SET_REGION
226 p = msg_mkopt(msg, p, NMRP_O_DEV_REGION, ®ion, 2);
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_ip_add(a, b, c, d) (0)
239 #define ethsock_ip_del(a, b) (0)
240 #define ethsock_close(a) (0)
241 #define tftp_put(a) (0)
243 static uint8_t *ethsock_get_hwaddr_fake(struct ethsock* sock)
245 static uint8_t hwaddr[6] = { 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa };
249 #define NMRP_INITIAL_TIMEOUT 60
252 static int pkt_send(struct ethsock *sock, struct nmrp_pkt *pkt)
254 return ethsock_send(sock, pkt, sizeof(*pkt));
257 static int pkt_recv(struct ethsock *sock, struct nmrp_pkt *pkt)
261 memset(pkt, 0, sizeof(*pkt));
262 bytes = ethsock_recv(sock, pkt, sizeof(*pkt));
269 mlen = ntohs(pkt->msg.len);
271 if (bytes < (mlen + sizeof(pkt->eh))
272 || bytes < NMRP_MIN_PKT_LEN
273 || mlen < NMRP_HDR_LEN) {
274 fprintf(stderr, "Short packet (%d raw, %d message)\n",
275 (int)bytes, (int)mlen);
277 } else if (mlen > sizeof(pkt->msg)) {
278 printf("Truncating %d byte message.\n", (int)mlen);
279 pkt->msg.len = htons(sizeof(pkt->msg));
285 static int mac_parse(const char *str, uint8_t *hwaddr)
290 sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x%n",
291 data, data + 1, data + 2, data + 3, data + 4, data + 5, &i);
293 if (i == strlen(str)) {
294 for (i = 0; i != 6; ++i) {
299 hwaddr[i] = data[i] & 0xff;
309 struct is_valid_ip_arg
311 struct in_addr *ipaddr;
312 struct in_addr *ipmask;
316 static int is_valid_ip_cb(struct ethsock_ip_callback_args *args)
318 #define SUBNET(x) ((x)->ipaddr->s_addr & (x)->ipmask->s_addr)
319 struct is_valid_ip_arg *arg = args->arg;
320 if (SUBNET(args) == SUBNET(arg)) {
321 arg->result = args->ipaddr->s_addr != arg->ipaddr->s_addr;
329 static int is_valid_ip(struct ethsock *sock, struct in_addr *ipaddr,
330 struct in_addr *ipmask)
333 struct is_valid_ip_arg arg = {
339 status = ethsock_for_each_ip(sock, is_valid_ip_cb, &arg);
340 return status < 0 ? status : arg.result;
343 static void sigh(int sig)
348 static const char *spinner = "\\|/-";
350 int nmrp_do(struct nmrpd_args *args)
352 struct nmrp_pkt tx, rx;
353 uint8_t *src, dest[6];
357 int i, status, ulreqs, expect, upload_ok, autoip;
358 struct ethsock *sock;
359 struct ethsock_ip_undo *ip_undo = NULL;
360 struct ethsock_arp_undo *arp_undo = NULL;
362 void (*sigh_orig)(int);
363 struct in_addr ipaddr;
364 struct in_addr ipmask;
366 if (args->op != NMRP_UPLOAD_FW) {
367 fprintf(stderr, "Operation not implemented.\n");
371 if (!mac_parse(args->mac, dest)) {
372 fprintf(stderr, "Invalid MAC address '%s'.\n", args->mac);
376 ipmask.s_addr = inet_addr(args->ipmask);
377 if (ipmask.s_addr == INADDR_NONE
378 || netmask(bitcount(ipmask.s_addr)) != ipmask.s_addr) {
379 fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask);
385 /* The MAC of the device that was used to test this utility starts
386 * with a4:2b:8c, hence 164 (0xa4) and 183 (0x2b + 0x8c)
388 args->ipaddr = "10.164.183.252";
390 if (!args->ipaddr_intf) {
391 args->ipaddr_intf = "10.164.183.253";
393 } else if (args->ipaddr_intf) {
399 if ((ipaddr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) {
400 fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr);
404 if (args->ipaddr_intf && (intf_addr = inet_addr(args->ipaddr_intf)) == INADDR_NONE) {
405 fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr_intf);
409 if (args->file_local && strcmp(args->file_local, "-") && access(args->file_local, R_OK) == -1) {
410 fprintf(stderr, "Error accessing file '%s'.\n", args->file_local);
414 if (args->file_remote) {
415 if (!tftp_is_valid_filename(args->file_remote)) {
416 fprintf(stderr, "Invalid remote filename '%s'.\n",
423 region = to_region_code(args->region);
425 fprintf(stderr, "Invalid region code '%s'.\n", args->region);
434 sock = ethsock_create(args->intf, ETH_P_NMRP);
439 sigh_orig = signal(SIGINT, sigh);
442 status = is_valid_ip(sock, &ipaddr, &ipmask);
445 fprintf(stderr, "Address %s/%s cannot be used on interface %s.\n",
446 args->ipaddr, args->ipmask, args->intf);
452 printf("Adding %s to interface %s.\n", args->ipaddr_intf, args->intf);
455 if (ethsock_ip_add(sock, intf_addr, ipmask.s_addr, &ip_undo) != 0) {
460 if (ethsock_set_timeout(sock, args->rx_timeout)) {
464 src = ethsock_get_hwaddr(sock);
469 memcpy(tx.eh.ether_shost, src, 6);
470 memcpy(tx.eh.ether_dhost, dest, 6);
471 tx.eh.ether_type = htons(ETH_P_NMRP);
473 msg_mkadvertise(&tx.msg, "NTGR");
477 beg = time_monotonic();
479 while (!g_interrupted) {
480 printf("\rAdvertising NMRP server on %s ... %c",
481 args->intf, spinner[i]);
485 if (pkt_send(sock, &tx) < 0) {
490 status = pkt_recv(sock, &rx);
492 if (memcmp(rx.eh.ether_dhost, src, 6) == 0) {
494 } else if (verbosity) {
495 printf("\nIgnoring bogus response: %s -> %s.\n",
496 mac_to_str(rx.eh.ether_shost),
497 mac_to_str(rx.eh.ether_dhost));
499 } else if (status == 1) {
502 /* because we don't want nmrpflash's exit status to be zero */
504 if ((time_monotonic() - beg) >= NMRP_INITIAL_TIMEOUT) {
505 printf("\nNo response after 60 seconds. Bailing out.\n");
513 expect = NMRP_C_CONF_REQ;
516 while (!g_interrupted) {
517 if (expect != NMRP_C_NONE && rx.msg.code != expect) {
518 fprintf(stderr, "Received %s while waiting for %s!\n",
519 msg_code_str(rx.msg.code), msg_code_str(expect));
522 msg_init(&tx.msg, NMRP_C_NONE);
526 switch (rx.msg.code) {
527 case NMRP_C_ADVERTISE:
528 printf("Received NMRP advertisement from %s.\n",
529 mac_to_str(rx.eh.ether_shost));
532 case NMRP_C_CONF_REQ:
533 msg_mkconfack(&tx.msg, ipaddr.s_addr, ipmask.s_addr, region);
534 expect = NMRP_C_TFTP_UL_REQ;
536 printf("Received configuration request from %s.\n",
537 mac_to_str(rx.eh.ether_shost));
539 memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, 6);
541 printf("Sending configuration: %s, netmask %s.\n",
542 args->ipaddr, args->ipmask);
544 if (ethsock_arp_add(sock, rx.eh.ether_shost, ipaddr.s_addr, &arp_undo) != 0) {
549 case NMRP_C_TFTP_UL_REQ:
552 printf("Bailing out after %d upload requests.\n",
554 tx.msg.code = NMRP_C_CLOSE_REQ;
559 printf("Ignoring extra upload request.\n");
561 ethsock_set_timeout(sock, args->ul_timeout);
562 tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
566 filename = msg_filename(&rx.msg);
568 if (!args->file_remote) {
569 args->file_remote = filename;
571 printf("Received upload request: filename '%s'.\n", filename);
572 } else if (!args->file_remote) {
573 args->file_remote = args->file_local;
574 printf("Received upload request with empty filename.\n");
580 printf("Executing '%s' ... \n", args->tftpcmd);
581 setenv("IP", inet_ntoa(ipaddr), 1);
582 setenv("PORT", lltostr(args->port, 10), 1);
583 setenv("MAC", mac_to_str(rx.eh.ether_shost), 1);
584 setenv("NETMASK", inet_ntoa(ipmask), 1);
585 //setenv("FILENAME", args->file_remote ? args->file_remote : "", 1);
586 status = system(args->tftpcmd);
589 if (!status && args->file_local) {
591 status = is_valid_ip(sock, &ipaddr, &ipmask);
594 } else if (!status) {
595 printf("IP address of %s has changed. Please assign a "
596 "static ip to the interface.\n", args->intf);
597 tx.msg.code = NMRP_C_CLOSE_REQ;
603 printf("Using remote filename '%s'.\n",
607 if (!strcmp(args->file_local, "-")) {
608 printf("Uploading from stdin ... ");
610 printf("Uploading %s ... ", leafname(args->file_local));
613 if (!(status = tftp_put(args))) {
620 printf("Waiting for remote to respond.\n");
622 ethsock_set_timeout(sock, args->ul_timeout);
623 tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
624 expect = NMRP_C_NONE;
625 } else if (status == -2) {
626 expect = NMRP_C_TFTP_UL_REQ;
632 case NMRP_C_KEEP_ALIVE_REQ:
633 tx.msg.code = NMRP_C_KEEP_ALIVE_ACK;
634 ethsock_set_timeout(sock, args->ul_timeout);
635 printf("Received keep-alive request.\n");
637 case NMRP_C_CLOSE_REQ:
638 tx.msg.code = NMRP_C_CLOSE_ACK;
640 case NMRP_C_CLOSE_ACK:
644 fprintf(stderr, "Unknown message code 0x%02x!\n",
649 if (tx.msg.code != NMRP_C_NONE) {
650 if (pkt_send(sock, &tx) < 0) {
655 if (tx.msg.code == NMRP_C_CLOSE_REQ) {
660 if (rx.msg.code == NMRP_C_CLOSE_REQ) {
661 printf("Remote finished. Closing connection.\n");
665 status = pkt_recv(sock, &rx);
668 fprintf(stderr, "Timeout while waiting for %s.\n",
669 msg_code_str(expect));
674 ethsock_set_timeout(sock, args->rx_timeout);
678 if (!g_interrupted) {
681 printf("Reboot your device now.\n");
683 printf("No upload request received.\n");
688 signal(SIGINT, sigh_orig);
689 ethsock_arp_del(sock, &arp_undo);
690 ethsock_ip_del(sock, &ip_undo);