Merge branches

This commit is contained in:
Joseph C. Lehner 2016-08-09 17:16:21 +02:00
commit bc6007beb7
8 changed files with 295 additions and 131 deletions

View file

@ -29,7 +29,7 @@ install: nmrpflash
install -m 755 nmrpflash $(PREFIX)/bin install -m 755 nmrpflash $(PREFIX)/bin
release/osx: release/osx:
CFLAGS="-arch i686 -arch x86_64 -mmacosx-version-min=10.6" make release CFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.6" make release
zip nmrpflash-osx.zip nmrpflash zip nmrpflash-osx.zip nmrpflash
release/linux: release release/linux: release

View file

@ -10,24 +10,26 @@ Prebuilt binaries for Linux, OS X and Windows are available
[here](https://github.com/jclehner/nmrpflash/releases) [here](https://github.com/jclehner/nmrpflash/releases)
([WinPcap](https://www.winpcap.org/install/default.htm) is required on Windows). ([WinPcap](https://www.winpcap.org/install/default.htm) is required on Windows).
```` ```
Usage: nmrpflash [OPTIONS...] Usage: nmrpflash [OPTIONS...]
Options (-a, -i and -f are mandatory): Options (-a, -i and -f and/or -c are mandatory):
-a <ipaddr> IP address to assign to target device -a <ipaddr> IP address to assign to target device
-c <command> Command to run before (or instead of) TFTP upload
-f <firmware> Firmware file -f <firmware> Firmware file
-F <filename> Remote filename to use during TFTP upload
-i <interface> Network interface directly connected to device -i <interface> Network interface directly connected to device
-m <mac> MAC address of target device (xx:xx:xx:xx:xx:xx) -m <mac> MAC address of target device (xx:xx:xx:xx:xx:xx)
-M <netmask> Subnet mask to assign to target device -M <netmask> Subnet mask to assign to target device
-t <timeout> Timeout (in milliseconds) for regular messages -t <timeout> Timeout (in milliseconds) for regular messages
-T <timeout> Time to wait after successfull TFTP upload -T <timeout> Time (seconds) to wait after successfull TFTP upload
-p <port> Port to use for TFTP upload -p <port> Port to use for TFTP upload
-U Test TFTP upload -R <region> Set device region (NA, WW, GR, PR, RU, BZ, IN, KO, JP)
-v Be verbose -v Be verbose
-V Print version and exit -V Print version and exit
-L List network interfaces -L List network interfaces
-h Show this screen -h Show this screen
```` ```
### Using nmrpflash ### Using nmrpflash
@ -40,10 +42,10 @@ First, we have to assign a static IP address to our network interface.
In this example, we'll use `192.168.1.2`. All available network interfaces In this example, we'll use `192.168.1.2`. All available network interfaces
can be listed using can be listed using
```` ```
$ nmrpflash -L $ nmrpflash -L
eth0 192.168.1.2 f2:11:a1:02:03:b1 eth0 192.168.1.2 f2:11:a1:02:03:b1
```` ```
Now we can `nmrpflash`. The argument for the `-a` option needs Now we can `nmrpflash`. The argument for the `-a` option needs
to be a *free* IP address from the same subnet as the one used by your to be a *free* IP address from the same subnet as the one used by your
@ -52,15 +54,17 @@ be downloaded directly from netgear. For details on how to do this, see
[here](#obtaining-firmware-images). Power on your device immediately [here](#obtaining-firmware-images). Power on your device immediately
after starting `nmrpflash`. after starting `nmrpflash`.
```` ```
$ nmrpflash -i eth0 -a 192.168.1.254 -f EX2700-V1.0.1.8.img $ nmrpflash -i eth0 -a 192.168.1.254 -f EX2700-V1.0.1.8.img
Advertising NMRP server on eth0 ... / Advertising NMRP server on eth0 ... /
Received configuration request from a4:2b:8c:00:00:01. Received configuration request from a4:2b:8c:00:00:01.
Sending configuration: ip 192.168.1.254, mask 255.255.255.0. Sending configuration: ip 192.168.1.254, mask 255.255.255.0.
Received upload request: filename 'firmware'.
Uploading EX2700-V1.0.1.8.img ... OK Uploading EX2700-V1.0.1.8.img ... OK
Waiting for remote to respond. Waiting for remote to respond.
Remote finished. Closing connection. Remote finished. Closing connection.
```` Reboot your device now.
```
### Common issues ### Common issues
###### "No suitable network interfaces found." ###### "No suitable network interfaces found."
@ -74,7 +78,26 @@ The router did not respond. Try rebooting the device and run `nmrpflash` again.
You could also try running `nmrpflash` with `-m` and specify your router's You could also try running `nmrpflash` with `-m` and specify your router's
MAC address. It's also possible that your device does not support the NMRP protocol. MAC address. It's also possible that your device does not support the NMRP protocol.
###### "Timeout while waiting for 0x04." ###### "Timeout while waiting for initial reply."
The device did not respond to `nmrpflash`'s TFTP upload request. This could indicate a bug
in the TFTP code; try using an external tftp client (busybox in this example), by specifying
the `-c` flag instead of the `-f` flag:
`$ nmrpflash -i eth0 -a 192.168.1.254 -c "busybox tftp -p -l EX2700-V1.0.1.8.img 192.168.1.254"`
If the upload still fails, and you're on Windows, try executing the following command before
running `nmrpflash`:
`C:\> netsh interface ip add neighbors <interface> <ip> <mac>`
where `<interface>` is the pretty interface name (e.g. "Local Area Connection"; as displayed by
`nmrpflash -L`), `<ip>` is the same IP address you'd use for `nmrpflash`'s `-a` flag, and `<mac>`
is the target device's mac address.
Cheers to [@ntadmin](https://github.com/ntadmin) for this info!
###### "Timeout while waiting for CLOSE_REQ."
After a successful file upload, `nmrpflash` waits for up to 120 seconds for an After a successful file upload, `nmrpflash` waits for up to 120 seconds for an
answer from your device. You can increase this by specifying a longer timeout answer from your device. You can increase this by specifying a longer timeout
@ -83,12 +106,35 @@ using `-T` switch (argument is in seconds).
It's entirely possible that the image was flashed successfully, but the It's entirely possible that the image was flashed successfully, but the
operation took longer than 120 seconds. operation took longer than 120 seconds.
###### "Address X/Y cannot be used on interface Z."
`nmrpflash` refuses to use an IP address / subnet mask combination that would
make the remote device unreachable from the device running `nmrpflash`. For
example, if the IP address of your computer is 192.168.0.1/255.255.255.0, assigning
192.168.2.1/255.255.255.0 to the router makes no sense, because the TFTP upload will
fail.
###### "IP address of X has changed. Please assign a static IP to the interface."
This can happen if the network interface in question automatically detects that
the network cable has been connected, and your computer tries to reconfigure that
interface (NetworkManager on Linux does this for example) - this can usually be
disabled.
An alternative would be to add `-c 'ifconfig <interface> <ip>'` to the command line,
for example:
`nmrpflash -i eth0 -a 192.168.1.1 -f firmware.bin -c 'ifconfig eth0 192.168.1.2'`
This will execute the command specified by `-c` prior to starting the TFTP upload (in
this case setting the IP address to 192.168.1.2).
### Building and installing ### Building and installing
###### Linux, Mac OS X, BSDs ###### Linux, Mac OS X, BSDs
```` ```
$ make && sudo make install $ make && sudo make install
```` ```
###### Windows ###### Windows

View file

@ -29,6 +29,7 @@ struct ethsock
int fd; int fd;
#else #else
HANDLE handle; HANDLE handle;
DWORD index;
#endif #endif
unsigned timeout; unsigned timeout;
uint8_t hwaddr[6]; uint8_t hwaddr[6];
@ -75,7 +76,7 @@ static inline bool sockaddr_get_hwaddr(struct sockaddr *sa, uint8_t *hwaddr)
return true; return true;
} }
static bool get_hwaddr_from_intf(const char *intf, uint8_t *hwaddr) static bool get_intf_info(const char *intf, uint8_t *hwaddr, void *dummy)
{ {
struct ifaddrs *ifas, *ifa; struct ifaddrs *ifas, *ifa;
bool found; bool found;
@ -119,7 +120,7 @@ void win_perror2(const char *msg, DWORD err)
} }
} }
static bool get_hwaddr_from_intf(const char *intf, uint8_t *hwaddr) static bool get_intf_info(const char *intf, uint8_t *hwaddr, DWORD *index)
{ {
PIP_ADAPTER_INFO adapters, adapter; PIP_ADAPTER_INFO adapters, adapter;
DWORD ret; DWORD ret;
@ -147,10 +148,10 @@ static bool get_hwaddr_from_intf(const char *intf, uint8_t *hwaddr)
* AdapterName from GetAdaptersInfo is just "{GUID}".*/ * AdapterName from GetAdaptersInfo is just "{GUID}".*/
if (strstr(intf, adapter->AdapterName)) { if (strstr(intf, adapter->AdapterName)) {
if (adapter->AddressLength == 6) { if (adapter->AddressLength == 6) {
for (i = 0; i != 6; ++i) { memcpy(hwaddr, adapter->Address, 6);
hwaddr[i] = adapter->Address[i]; if (index) {
*index = adapter->Index;
} }
found = true; found = true;
break; break;
} }
@ -289,8 +290,13 @@ struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
goto cleanup_pcap; goto cleanup_pcap;
} }
if (!get_hwaddr_from_intf(intf, sock->hwaddr)) { #ifndef NMRPFLASH_WINDOWS
fprintf(stderr, "Failed to get MAC address of interface.\n"); err = !get_intf_info(intf, sock->hwaddr, NULL);
#else
err = !get_intf_info(intf, sock->hwaddr, &sock->index);
#endif
if (err) {
fprintf(stderr, "Failed to get interface info.\n");
goto cleanup_malloc; goto cleanup_malloc;
} }
@ -436,6 +442,54 @@ inline int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
return 0; return 0;
} }
#ifndef NMRPFLASH_WINDOWS
int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr)
{
return 0;
}
int ethsock_arp_del(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr)
{
return 0;
}
#else
static int ethsock_arp(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr, int add)
{
DWORD ret;
MIB_IPNETROW arp = {
.dwIndex = sock->index,
.dwPhysAddrLen = 6,
.dwAddr = ipaddr->s_addr,
.dwType = MIB_IPNET_TYPE_STATIC
};
memcpy(arp.bPhysAddr, hwaddr, 6);
if (add) {
ret = CreateIpNetEntry(&arp);
if (ret != NO_ERROR) {
win_perror2("CreateIpNetEntry", ret);
return -1;
}
} else {
DeleteIpNetEntry(&arp);
}
return 0;
}
int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr)
{
ethsock_arp_del(sock, hwaddr, ipaddr);
return ethsock_arp(sock, hwaddr, ipaddr, 1);
}
int ethsock_arp_del(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr)
{
return ethsock_arp(sock, hwaddr, ipaddr, 0);
}
#endif
static bool get_hwaddr_from_pcap(const pcap_if_t *dev, uint8_t *hwaddr) static bool get_hwaddr_from_pcap(const pcap_if_t *dev, uint8_t *hwaddr)
{ {
#ifndef NMRPFLASH_WINDOWS #ifndef NMRPFLASH_WINDOWS
@ -458,7 +512,7 @@ static bool get_hwaddr_from_pcap(const pcap_if_t *dev, uint8_t *hwaddr)
} }
#endif #endif
return get_hwaddr_from_intf(dev->name, hwaddr); return get_intf_info(dev->name, hwaddr, NULL);
} }
int ethsock_list_all(void) int ethsock_list_all(void)

14
main.c
View file

@ -23,6 +23,8 @@
#include <stdio.h> #include <stdio.h>
#include "nmrpd.h" #include "nmrpd.h"
#define NMRPFLASH_SET_REGION
int verbosity = 0; int verbosity = 0;
void usage(FILE *fp) void usage(FILE *fp)
@ -42,7 +44,7 @@ void usage(FILE *fp)
" -T <timeout> Time (seconds) to wait after successfull TFTP upload\n" " -T <timeout> Time (seconds) to wait after successfull TFTP upload\n"
" -p <port> Port to use for TFTP upload\n" " -p <port> Port to use for TFTP upload\n"
#ifdef NMRPFLASH_SET_REGION #ifdef NMRPFLASH_SET_REGION
" -R <region> Set device region\n" " -R <region> Set device region (NA, WW, GR, PR, RU, BZ, IN, KO, JP)\n"
#endif #endif
#ifdef NMRPFLASH_TFTP_TEST #ifdef NMRPFLASH_TFTP_TEST
" -U Test TFTP upload\n" " -U Test TFTP upload\n"
@ -83,7 +85,7 @@ int main(int argc, char **argv)
.mac = "ff:ff:ff:ff:ff:ff", .mac = "ff:ff:ff:ff:ff:ff",
.op = NMRP_UPLOAD_FW, .op = NMRP_UPLOAD_FW,
.port = 69, .port = 69,
.force_root = 1 .region = NULL,
}; };
#ifdef NMRPFLASH_WINDOWS #ifdef NMRPFLASH_WINDOWS
WSADATA wsa; WSADATA wsa;
@ -121,16 +123,16 @@ int main(int argc, char **argv)
case 'M': case 'M':
args.ipmask = optarg; args.ipmask = optarg;
break; break;
case 'p':
#ifdef NMRPFLASH_SET_REGION #ifdef NMRPFLASH_SET_REGION
case 'R': case 'R':
args.region = optarg;
break;
#endif #endif
case 'p':
case 'T': case 'T':
case 't': case 't':
if (c == 'p') { if (c == 'p') {
max = 0xffff; max = 0xffff;
} else if (c == 'R') {
max = 0x0009;
} }
val = atoi(optarg); val = atoi(optarg);
@ -145,8 +147,6 @@ int main(int argc, char **argv)
args.rx_timeout = val; args.rx_timeout = val;
} else if (c == 'T') { } else if (c == 'T') {
args.ul_timeout = val * 1000; args.ul_timeout = val * 1000;
} else if (c == 'R') {
args.region = val;
} }
break; break;

219
nmrp.c
View file

@ -27,11 +27,13 @@
#include "nmrpd.h" #include "nmrpd.h"
#define NMRP_HDR_LEN 6 #define NMRP_HDR_LEN 6
#define NMRP_OPT_LEN 4 #define NMRP_OPT_HDR_LEN 4
#define NMRP_MIN_PKT_LEN (sizeof(struct eth_hdr) + NMRP_HDR_LEN) #define NMRP_MIN_PKT_LEN (sizeof(struct eth_hdr) + NMRP_HDR_LEN)
#define NMRP_MAX_OPT_SIZE 12 #define NMRP_MAX_OPT_SIZE 12
#define NMRP_MAX_OPT_NUM 2 #define NMRP_MAX_OPT_NUM 3
#define NMRP_OPT_NEXT(x) ((struct nmrp_opt*)(((char*)x) + x->len))
#define ETH_P_NMRP 0x0912 #define ETH_P_NMRP 0x0912
#define IP_LEN 4 #define IP_LEN 4
@ -80,7 +82,9 @@ struct nmrp_msg {
uint8_t code; uint8_t code;
uint8_t id; uint8_t id;
uint16_t len; uint16_t len;
struct nmrp_opt opts[2]; /* only opts[0] is valid! think of this as a char* */
struct nmrp_opt opts[NMRP_MAX_OPT_NUM];
/* this is NOT part of the transmitted packet */
uint32_t num_opts; uint32_t num_opts;
} PACKED; } PACKED;
@ -116,13 +120,20 @@ static const char *msg_code_str(uint16_t code)
#undef CASE_CODE #undef CASE_CODE
} }
static void msg_update_len(struct nmrp_msg *msg) static uint16_t to_region_code(const char *region)
{ {
uint32_t i = 0; #define REGION_CODE(r, c) if (!strcasecmp(region, r)) return c
msg->len = NMRP_HDR_LEN; REGION_CODE("NA", 0x0001);
for (; i != msg->num_opts; ++i) { REGION_CODE("WW", 0x0002);
msg->len += msg->opts[i].len; REGION_CODE("GR", 0x0003);
} REGION_CODE("PR", 0x0004);
REGION_CODE("RU", 0x0005);
REGION_CODE("BZ", 0x0006);
REGION_CODE("IN", 0x0007);
REGION_CODE("KO", 0x0008);
REGION_CODE("JP", 0x0009);
#undef REGION_CODE
return 0;
} }
static void msg_dump(struct nmrp_msg *msg, int dump_opts) static void msg_dump(struct nmrp_msg *msg, int dump_opts)
@ -142,16 +153,18 @@ static void msg_dump(struct nmrp_msg *msg, int dump_opts)
while (remain_len > 0) { while (remain_len > 0) {
len = opt->len; len = opt->len;
fprintf(stderr, " opt type=%u, len=%u", opt->type, len); fprintf(stderr, " opt type=%u, len=%u", opt->type, len);
for (i = 0; i != len - NMRP_OPT_LEN; ++i) { if (len) {
if (!(i % 16)) { for (i = 0; i != len - NMRP_OPT_HDR_LEN; ++i) {
fprintf(stderr, "\n "); if (!(i % 16)) {
} fprintf(stderr, "\n ");
}
fprintf(stderr, "%02x ", ((char*)&opt->val)[i] & 0xff); fprintf(stderr, "%02x ", ((char*)&opt->val)[i] & 0xff);
}
fprintf(stderr, "\n");
} }
fprintf(stderr, "\n");
remain_len -= len; remain_len -= len;
opt = (struct nmrp_opt*)(((char*)opt) + len); opt = NMRP_OPT_NEXT(opt);
} }
} }
} }
@ -159,13 +172,16 @@ static void msg_dump(struct nmrp_msg *msg, int dump_opts)
static void msg_hton(struct nmrp_msg *msg) static void msg_hton(struct nmrp_msg *msg)
{ {
uint32_t i = 0; uint32_t i = 0;
struct nmrp_opt *opt = msg->opts, *next;
msg->reserved = htons(msg->reserved); msg->reserved = htons(msg->reserved);
msg->len = htons(msg->len); msg->len = htons(msg->len);
for (; i != msg->num_opts; ++i) { for (; i != msg->num_opts; ++i) {
msg->opts[i].len = htons(msg->opts[i].len); next = NMRP_OPT_NEXT(opt);
msg->opts[i].type = htons(msg->opts[i].type); opt->len = htons(opt->len);
opt->type = htons(opt->type);
opt = next;
} }
} }
@ -186,7 +202,7 @@ static int msg_ntoh(struct nmrp_msg *msg)
// size is 12 // size is 12
if (remaining < NMRP_MAX_OPT_NUM * NMRP_MAX_OPT_SIZE) { if (remaining < NMRP_MAX_OPT_NUM * NMRP_MAX_OPT_SIZE) {
while (remaining > 0) { while (remaining > 0) {
if (remaining < NMRP_OPT_LEN) { if (remaining < NMRP_OPT_HDR_LEN) {
break; break;
} }
@ -198,7 +214,7 @@ static int msg_ntoh(struct nmrp_msg *msg)
} }
remaining -= opt->len; remaining -= opt->len;
++opt; opt = NMRP_OPT_NEXT(opt);
} }
if (!remaining) { if (!remaining) {
@ -211,7 +227,7 @@ static int msg_ntoh(struct nmrp_msg *msg)
return 1; return 1;
} }
static void *msg_opt_data(struct nmrp_msg *msg, int type, uint16_t *len) static void *msg_opt_data(struct nmrp_msg *msg, uint16_t type, uint16_t *len)
{ {
static char buf[128]; static char buf[128];
struct nmrp_opt *opt = msg->opts; struct nmrp_opt *opt = msg->opts;
@ -221,21 +237,54 @@ static void *msg_opt_data(struct nmrp_msg *msg, int type, uint16_t *len)
while (remaining > 0) { while (remaining > 0) {
if (opt->type == type) { if (opt->type == type) {
if (opt->len == NMRP_OPT_LEN) { if (opt->len == NMRP_OPT_HDR_LEN) {
return NULL; return NULL;
} }
*len = opt->len - NMRP_OPT_LEN; *len = opt->len - NMRP_OPT_HDR_LEN;
memcpy(buf, &opt->val, MIN(*len, sizeof(buf)-1)); memcpy(buf, &opt->val, MIN(*len, sizeof(buf)-1));
return buf; return buf;
} }
remaining -= opt->len; remaining -= opt->len;
opt = (struct nmrp_opt*)((char*)opt) + opt->len; opt = NMRP_OPT_NEXT(opt);
} }
return NULL; return NULL;
} }
static void msg_opt_add(struct nmrp_msg *msg, uint16_t type, void *data,
uint16_t len)
{
uint32_t i = 0;
struct nmrp_opt *opt = msg->opts;
if (len + NMRP_OPT_HDR_LEN > NMRP_MAX_OPT_SIZE
|| msg->num_opts == NMRP_MAX_OPT_NUM) {
fprintf(stderr, "Invalid option - this is a bug.\n");
}
for (; i <= msg->num_opts; ++i) {
opt = NMRP_OPT_NEXT(opt);
}
opt->len = NMRP_OPT_HDR_LEN + len;
opt->type = type;
if (len) {
memcpy(&opt->val, data, len);
}
msg->len += opt->len;
++msg->num_opts;
}
static inline void msg_init(struct nmrp_msg *msg, uint16_t code)
{
memset(msg, 0, sizeof(*msg));
msg->len = NMRP_HDR_LEN;
msg->code = code;
}
static int pkt_send(struct ethsock *sock, struct nmrp_pkt *pkt) static int pkt_send(struct ethsock *sock, struct nmrp_pkt *pkt)
{ {
size_t len = ntohs(pkt->msg.len) + sizeof(pkt->eh); size_t len = ntohs(pkt->msg.len) + sizeof(pkt->eh);
@ -328,12 +377,19 @@ static int is_valid_ip(struct ethsock *sock, struct in_addr *ipaddr,
} }
static struct ethsock *gsock = NULL; static struct ethsock *gsock = NULL;
static int garp = 0;
static struct in_addr arpip = { 0 };
static uint8_t arpmac[6] = { 0 };
static void sigh(int sig) static void sigh(int sig)
{ {
printf("\n"); printf("\n");
if (gsock) { if (gsock) {
if (garp) {
ethsock_arp_del(gsock, arpmac, &arpip);
}
ethsock_close(gsock); ethsock_close(gsock);
gsock = NULL;
} }
exit(1); exit(1);
@ -345,13 +401,16 @@ int nmrp_do(struct nmrpd_args *args)
{ {
struct nmrp_pkt tx, rx; struct nmrp_pkt tx, rx;
uint8_t *src, dest[6]; uint8_t *src, dest[6];
uint16_t len; uint16_t len, region;
char *filename; char *filename;
struct in_addr ipaddr, ipmask;
time_t beg; time_t beg;
int i, status, ulreqs, expect; int i, status, ulreqs, expect, upload_ok;
struct ethsock *sock; struct ethsock *sock;
void (*sigh_orig)(int); void (*sigh_orig)(int);
struct {
struct in_addr addr;
struct in_addr mask;
} PACKED ipconf;
if (args->op != NMRP_UPLOAD_FW) { if (args->op != NMRP_UPLOAD_FW) {
fprintf(stderr, "Operation not implemented.\n"); fprintf(stderr, "Operation not implemented.\n");
@ -363,17 +422,17 @@ int nmrp_do(struct nmrpd_args *args)
return 1; return 1;
} }
if ((ipaddr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) { if ((ipconf.addr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) {
fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr); fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr);
return 1; return 1;
} }
if ((ipmask.s_addr = inet_addr(args->ipmask)) == INADDR_NONE) { if ((ipconf.mask.s_addr = inet_addr(args->ipmask)) == INADDR_NONE) {
fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask); fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask);
return 1; return 1;
} }
if (strcmp(args->file_local, "-") && access(args->file_local, R_OK) == -1) { if (args->file_local && strcmp(args->file_local, "-") && access(args->file_local, R_OK) == -1) {
fprintf(stderr, "Error accessing file '%s'.\n", args->file_local); fprintf(stderr, "Error accessing file '%s'.\n", args->file_local);
return 1; return 1;
} }
@ -386,6 +445,16 @@ int nmrp_do(struct nmrpd_args *args)
} }
} }
if (args->region) {
region = htons(to_region_code(args->region));
if (!region) {
fprintf(stderr, "Invalid region code '%s'.\n", args->region);
return 1;
}
} else {
region = 0;
}
status = 1; status = 1;
sock = ethsock_create(args->intf, ETH_P_NMRP); sock = ethsock_create(args->intf, ETH_P_NMRP);
@ -393,8 +462,6 @@ int nmrp_do(struct nmrpd_args *args)
return 1; return 1;
} }
sigh_orig = signal(SIGINT, sigh);
status = is_valid_ip(sock, &ipaddr, &ipmask); status = is_valid_ip(sock, &ipaddr, &ipmask);
if (status <= 0) { if (status <= 0) {
if (!status) { if (!status) {
@ -405,6 +472,8 @@ int nmrp_do(struct nmrpd_args *args)
} }
gsock = sock; gsock = sock;
garp = 0;
sigh_orig = signal(SIGINT, sigh);
if (ethsock_set_timeout(sock, args->rx_timeout)) { if (ethsock_set_timeout(sock, args->rx_timeout)) {
goto out; goto out;
@ -419,21 +488,12 @@ int nmrp_do(struct nmrpd_args *args)
memcpy(tx.eh.ether_dhost, dest, 6); memcpy(tx.eh.ether_dhost, dest, 6);
tx.eh.ether_type = htons(ETH_P_NMRP); tx.eh.ether_type = htons(ETH_P_NMRP);
tx.msg.reserved = 0; msg_init(&tx.msg, NMRP_C_ADVERTISE);
tx.msg.code = NMRP_C_ADVERTISE; msg_opt_add(&tx.msg, NMRP_O_MAGIC_NO, "NTGR", 4);
tx.msg.id = 0;
tx.msg.num_opts = 1;
tx.msg.opts[0].type = NMRP_O_MAGIC_NO;
tx.msg.opts[0].len = NMRP_OPT_LEN + 4;
tx.msg.opts[0].val.magic[0] = 'N';
tx.msg.opts[0].val.magic[1] = 'T';
tx.msg.opts[0].val.magic[2] = 'G';
tx.msg.opts[0].val.magic[3] = 'R';
msg_update_len(&tx.msg);
msg_hton(&tx.msg); msg_hton(&tx.msg);
i = 0; i = 0;
upload_ok = 0;
beg = time(NULL); beg = time(NULL);
while (1) { while (1) {
@ -472,11 +532,7 @@ int nmrp_do(struct nmrpd_args *args)
msg_code_str(rx.msg.code), msg_code_str(expect)); msg_code_str(rx.msg.code), msg_code_str(expect));
} }
tx.msg.code = NMRP_C_NONE; msg_init(&tx.msg, NMRP_C_NONE);
tx.msg.reserved = 0;
tx.msg.id = 0;
tx.msg.num_opts = 0;
tx.msg.len = 0;
status = 1; status = 1;
@ -488,23 +544,14 @@ int nmrp_do(struct nmrpd_args *args)
goto out; goto out;
case NMRP_C_CONF_REQ: case NMRP_C_CONF_REQ:
tx.msg.code = NMRP_C_CONF_ACK; tx.msg.code = NMRP_C_CONF_ACK;
tx.msg.num_opts = 2;
tx.msg.opts[0].type = NMRP_O_DEV_IP; msg_opt_add(&tx.msg, NMRP_O_DEV_IP, &ipconf, 8);
tx.msg.opts[0].len = NMRP_OPT_LEN + 2 * 4; msg_opt_add(&tx.msg, NMRP_O_FW_UP, NULL, 0);
memcpy(tx.msg.opts[0].val.ip.addr, &ipaddr, 4);
memcpy(tx.msg.opts[0].val.ip.mask, &ipmask, 4);
tx.msg.opts[1].type = NMRP_O_FW_UP;
tx.msg.opts[1].len = NMRP_OPT_LEN;
#ifdef NMRPFLASH_SET_REGION #ifdef NMRPFLASH_SET_REGION
tx.msg.num_opts = 3; if (region) {
msg_opt_add(&tx.msg, NMRP_O_DEV_REGION, &region, 2);
tx.msg.opts[2].type = NMRP_O_DEV_REGION; }
tx.msg.opts[2].len = NMRP_OPT_LEN + 2;
tx.msg.opts[2].val.region = args->region;
#endif #endif
expect = NMRP_C_TFTP_UL_REQ; expect = NMRP_C_TFTP_UL_REQ;
@ -517,12 +564,30 @@ int nmrp_do(struct nmrpd_args *args)
printf("Sending configuration: ip %s, mask %s.\n", printf("Sending configuration: ip %s, mask %s.\n",
args->ipaddr, args->ipmask); args->ipaddr, args->ipmask);
memcpy(arpmac, rx.eh.ether_shost, 6);
memcpy(&arpip, &ipconf.addr, sizeof(ipconf.addr));
if (ethsock_arp_add(sock, arpmac, &arpip) != 0) {
goto out;
}
garp = 1;
break; break;
case NMRP_C_TFTP_UL_REQ: case NMRP_C_TFTP_UL_REQ:
if (++ulreqs > 5) { if (!upload_ok) {
fprintf(stderr, "Device re-requested file upload %d " if (++ulreqs > 5) {
"times; aborting.\n", ulreqs); printf("Bailing out after %d upload requests.\n",
tx.msg.code = NMRP_C_CLOSE_REQ; ulreqs);
tx.msg.code = NMRP_C_CLOSE_REQ;
break;
}
} else {
if (verbosity) {
printf("Ignoring extra upload request.\n");
}
ethsock_set_timeout(sock, args->ul_timeout);
tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
break; break;
} }
@ -535,12 +600,8 @@ int nmrp_do(struct nmrpd_args *args)
printf("Received upload request: filename '%.*s'.\n", printf("Received upload request: filename '%.*s'.\n",
len, filename); len, filename);
} else if (!args->file_remote) { } else if (!args->file_remote) {
if (tftp_is_valid_filename(args->file_local)) { args->file_remote = args->file_local;
args->file_remote = args->file_local; printf("Received upload request with empty filename.\n");
} else {
args->file_remote = "firmware";
}
printf("Received upload request with empty filename.");
} }
status = 0; status = 0;
@ -557,7 +618,7 @@ int nmrp_do(struct nmrpd_args *args)
} }
if (!status && args->file_local) { if (!status && args->file_local) {
status = is_valid_ip(sock, &ipaddr, &ipmask); status = is_valid_ip(sock, &ipconf.addr, &ipconf.mask);
if (status < 0) { if (status < 0) {
goto out; goto out;
} else if (!status) { } else if (!status) {
@ -575,7 +636,7 @@ int nmrp_do(struct nmrpd_args *args)
if (!strcmp(args->file_local, "-")) { if (!strcmp(args->file_local, "-")) {
printf("Uploading from stdin ... "); printf("Uploading from stdin ... ");
} else { } else {
printf("Uploading %s ... ", args->file_local); printf("Uploading %s ... ", leafname(args->file_local));
} }
fflush(stdout); fflush(stdout);
status = tftp_put(args); status = tftp_put(args);
@ -583,7 +644,9 @@ int nmrp_do(struct nmrpd_args *args)
if (!status) { if (!status) {
printf("OK\nWaiting for remote to respond.\n"); printf("OK\nWaiting for remote to respond.\n");
upload_ok = 1;
ethsock_set_timeout(sock, args->ul_timeout); ethsock_set_timeout(sock, args->ul_timeout);
tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
expect = NMRP_C_NONE; expect = NMRP_C_NONE;
} else if (status == -2) { } else if (status == -2) {
expect = NMRP_C_TFTP_UL_REQ; expect = NMRP_C_TFTP_UL_REQ;
@ -610,13 +673,16 @@ int nmrp_do(struct nmrpd_args *args)
} }
if (tx.msg.code != NMRP_C_NONE) { if (tx.msg.code != NMRP_C_NONE) {
msg_update_len(&tx.msg);
msg_hton(&tx.msg); msg_hton(&tx.msg);
if (pkt_send(sock, &tx) < 0) { if (pkt_send(sock, &tx) < 0) {
perror("sendto"); perror("sendto");
goto out; goto out;
} }
if (tx.msg.code == NMRP_C_CLOSE_REQ) {
goto out;
}
} }
if (rx.msg.code == NMRP_C_CLOSE_REQ) { if (rx.msg.code == NMRP_C_CLOSE_REQ) {
@ -648,6 +714,7 @@ int nmrp_do(struct nmrpd_args *args)
out: out:
signal(SIGINT, sigh_orig); signal(SIGINT, sigh_orig);
gsock = NULL; gsock = NULL;
ethsock_arp_del(sock, arpmac, &arpip);
ethsock_close(sock); ethsock_close(sock);
return status; return status;
} }

View file

@ -71,10 +71,10 @@ struct nmrpd_args {
const char *mac; const char *mac;
enum nmrp_op op; enum nmrp_op op;
uint16_t port; uint16_t port;
uint16_t region; const char *region;
int force_root;
}; };
const char *leafname(const char *path);
int tftp_put(struct nmrpd_args *args); int tftp_put(struct nmrpd_args *args);
bool tftp_is_valid_filename(const char *filename); bool tftp_is_valid_filename(const char *filename);
@ -100,6 +100,8 @@ int ethsock_send(struct ethsock *sock, void *buf, size_t len);
ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len); ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len);
int ethsock_set_timeout(struct ethsock *sock, unsigned msec); int ethsock_set_timeout(struct ethsock *sock, unsigned msec);
uint8_t *ethsock_get_hwaddr(struct ethsock *sock); uint8_t *ethsock_get_hwaddr(struct ethsock *sock);
int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr);
int ethsock_arp_del(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr);
int ethsock_list_all(void); int ethsock_list_all(void);
struct ethsock_ip_callback_args struct ethsock_ip_callback_args

View file

@ -9,7 +9,7 @@ Libs=Wpdpack\Lib
PrivateResource= PrivateResource=
ResourceIncludes= ResourceIncludes=
MakeIncludes= MakeIncludes=
Compiler=-DNMRPFLASH_VERSION=\"0.9.2\"_@@_ Compiler=-DNMRPFLASH_VERSION=\"0.9.3\"_@@_
CppCompiler= CppCompiler=
Linker=-liphlpapi_@@_-lws2_32_@@_-ladvapi32_@@_-lwpcap_@@_-lpacket_@@__@@_ Linker=-liphlpapi_@@_-lws2_32_@@_-ladvapi32_@@_-lwpcap_@@_-lpacket_@@__@@_
IsCpp=0 IsCpp=0

43
tftp.c
View file

@ -40,24 +40,6 @@ enum tftp_opcode {
ERR = 5 ERR = 5
}; };
static const char *leafname(const char *path)
{
const char *slash, *bslash;
slash = strrchr(path, '/');
bslash = strrchr(path, '\\');
if (slash && bslash) {
path = 1 + (slash > bslash ? slash : bslash);
} else if (slash) {
path = 1 + slash;
} else if (bslash) {
path = 1 + bslash;
}
return path;
}
static bool is_netascii(const char *str) static bool is_netascii(const char *str)
{ {
uint8_t *p = (uint8_t*)str; uint8_t *p = (uint8_t*)str;
@ -211,16 +193,29 @@ static ssize_t tftp_sendto(int sock, char *pkt, size_t len,
return sent; return sent;
} }
const char *leafname(const char *path)
{
const char *slash, *bslash;
slash = strrchr(path, '/');
bslash = strrchr(path, '\\');
if (slash && bslash) {
path = 1 + (slash > bslash ? slash : bslash);
} else if (slash) {
path = 1 + slash;
} else if (bslash) {
path = 1 + bslash;
}
return path;
}
#ifdef NMRPFLASH_WINDOWS #ifdef NMRPFLASH_WINDOWS
void sock_perror(const char *msg) void sock_perror(const char *msg)
{ {
win_perror2(msg, WSAGetLastError()); win_perror2(msg, WSAGetLastError());
} }
#else
inline void sock_perror(const char *msg)
{
perror(msg);
}
#endif #endif
inline bool tftp_is_valid_filename(const char *filename) inline bool tftp_is_valid_filename(const char *filename)
@ -322,7 +317,7 @@ int tftp_put(struct nmrpd_args *args)
if (ret < 0) { if (ret < 0) {
goto cleanup; goto cleanup;
} else if (!ret) { } else if (!ret) {
if (++timeout < 5) { if (++timeout < 5 || (!block && timeout < 10)) {
continue; continue;
} else if (block) { } else if (block) {
fprintf(stderr, "Timeout while waiting for ACK(%d).\n", block); fprintf(stderr, "Timeout while waiting for ACK(%d).\n", block);