X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=nmrp.c;h=bdbb6feb83a687cfae2b7ee4108f93ec6b13d640;hb=9a10f024df90fa4ba98cd949a6f40e9f9921df98;hp=61b592db5f6d743a56a4a68201df9c9c5eea9575;hpb=6e5156e1375495cb05d23995be8605d88abed1f5;p=oweals%2Fnmrpflash.git diff --git a/nmrp.c b/nmrp.c index 61b592d..bdbb6fe 100644 --- a/nmrp.c +++ b/nmrp.c @@ -1,21 +1,45 @@ +/** + * nmrp-flash - Netgear Unbrick Utility + * Copyright (C) 2016 Joseph Lehner + * + * nmrp-flash is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nmrp-flash is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with nmrp-flash. If not, see . + * + */ + #define _BSD_SOURCE -#include -#include -#include -#include -#include -#include +#include #include #include #include #include #include #include +#include "ethsock.h" #include "nmrpd.h" +#ifndef NMRPFLASH_WINDOWS +#include +#else +#include +#endif + #define NMRP_HDR_LEN 6 #define NMRP_OPT_LEN 4 -#define NMRP_MIN_PKT_LEN (sizeof(struct ether_header) + 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_NUM 2 #define ETH_P_NMRP 0x0912 #define IP_LEN 4 @@ -49,8 +73,8 @@ struct nmrp_opt { union { uint8_t magic[4]; struct { - uint8_t addr[IP_LEN]; - uint8_t mask[IP_LEN]; + uint8_t addr[4]; + uint8_t mask[4]; } ip; } val; } PACKED; @@ -64,8 +88,14 @@ struct nmrp_msg { uint32_t num_opts; } PACKED; +struct eth_hdr { + uint8_t ether_dhost[6]; + uint8_t ether_shost[6]; + uint16_t ether_type; +} PACKED; + struct nmrp_pkt { - struct ether_header eh; + struct eth_hdr eh; struct nmrp_msg msg; } PACKED; @@ -132,116 +162,69 @@ static int msg_ntoh(struct nmrp_msg *msg) { struct nmrp_opt *opt = msg->opts; int remaining; - + msg_hdr_ntoh(msg); remaining = msg->len - NMRP_HDR_LEN; - while (remaining > 0) { - if (remaining < NMRP_OPT_LEN) { - fprintf(stderr, "Malformed message.\n"); - msg_dump(msg, 0); - return 1; - } - - opt->type = ntohs(opt->type); - opt->len = ntohs(opt->len); - - remaining -= opt->len; - ++opt; - } - - if (remaining) { - fprintf(stderr, "Trailing data in message.\n"); - msg_dump(msg, 0); - return 1; - } - - return 0; -} + // FIXME maximum of two options supported, maximum option + // size is 12 + if (remaining < NMRP_MAX_OPT_NUM * NMRP_MAX_OPT_SIZE) { + while (remaining > 0) { + if (remaining < NMRP_OPT_LEN) { + break; + } -static int intf_get_info(int sock, const char *name, int *index, - uint8_t *hwaddr) -{ - struct ifreq ifr; + opt->type = ntohs(opt->type); + opt->len = ntohs(opt->len); - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); + if (opt->len > NMRP_MAX_OPT_SIZE) { + break; + } - if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) { - perror("ioctl(SIOCGIFINDEX)"); - return -1; - } - *index = ifr.ifr_ifindex; + remaining -= opt->len; + ++opt; + } - if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) { - perror("ioctl(SIOCGIFHWADDR)"); - return -1; + if (!remaining) { + return 0; + } } - memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - return 0; + fprintf(stderr, "Unexpected message format.\n"); + msg_dump(msg, 0); + return 1; } -static int pkt_send(int sock, struct sockaddr_ll *addr, 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); - return sendto(sock, pkt, len, 0, (struct sockaddr*)addr, sizeof(*addr)); + return ethsock_send(sock, pkt, len); } -static int pkt_recv(int sock, struct nmrp_pkt *pkt) +static int pkt_recv(struct ethsock *sock, struct nmrp_pkt *pkt) { - struct sockaddr_ll from; - socklen_t addrlen; ssize_t bytes, len; memset(pkt, 0, sizeof(*pkt)); - bytes = recvfrom(sock, pkt, NMRP_MIN_PKT_LEN, MSG_PEEK, - (struct sockaddr*)&from, &addrlen); - + bytes = ethsock_recv(sock, pkt, sizeof(*pkt)); if (bytes < 0) { - if (errno == EAGAIN) { - return 2; - } - perror("recvfrom(pkt)"); return 1; - } else if (ntohs(pkt->eh.ether_type) != ETH_P_NMRP) { - return 3; + } else if (!bytes) { + return 2; } else if (bytes < NMRP_MIN_PKT_LEN) { - fprintf(stderr, "Short packet (%zi bytes)\n", bytes); + fprintf(stderr, "Short packet (%d bytes)\n", (int)bytes); return 1; } msg_hdr_ntoh(&pkt->msg); len = pkt->msg.len + sizeof(pkt->eh); - bytes = recvfrom(sock, pkt, len, MSG_DONTWAIT, NULL, NULL); - if (bytes < 0) { - perror("recvfrom(msg)"); - return 1; - } else if (bytes != len) { - fprintf(stderr, "Unexpected message length (%zi bytes).\n", len); - return 1; - } else { - if (msg_ntoh(&pkt->msg) != 0) { - return 1; - } - return 0; - } - - return 1; -} - -static int sock_bind_to_intf(int sock, const char *name) -{ - struct ifreq ifr; - - strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); - if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) { - perror("setsockopt(SO_BINDTODEVICE)"); + if (bytes != len) { + fprintf(stderr, "Unexpected message length (%d bytes).\n", (int)len); return 1; } - return 0; + return msg_ntoh(&pkt->msg); } static int mac_parse(const char *str, uint8_t *hwaddr) @@ -268,16 +251,29 @@ static int mac_parse(const char *str, uint8_t *hwaddr) return 0; } +static struct ethsock *gsock = NULL; + +static void sigh(int sig) +{ + printf("\n"); + if (gsock) { + ethsock_close(gsock); + } + + exit(1); +} + static const char *spinner = "\\|/-"; int nmrp_do(struct nmrpd_args *args) { struct nmrp_pkt tx, rx; - struct sockaddr_ll addr; - uint8_t src[ETH_ALEN], dest[ETH_ALEN]; + uint8_t *src, dest[6]; struct in_addr ipaddr, ipmask; time_t beg; - int i, sock, err, ulreqs, expect; + int i, err, ulreqs, expect; + struct ethsock *sock; + void (*sigh_orig)(int); if (args->op != NMRP_UPLOAD_FW) { fprintf(stderr, "Operation not implemented.\n"); @@ -285,52 +281,46 @@ int nmrp_do(struct nmrpd_args *args) } if (!mac_parse(args->mac, dest)) { - fprintf(stderr, "Invalid MAC address %s.\n", args->mac); + fprintf(stderr, "Invalid MAC address '%s'.\n", args->mac); return 1; } - if (!inet_aton(args->ipaddr, &ipaddr)) { - fprintf(stderr, "Invalid IP address %s.\n", args->ipaddr); + if ((ipaddr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) { + fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr); return 1; } - if (!inet_aton(args->ipmask, &ipmask)) { - fprintf(stderr, "Invalid subnet mask %s.\n", args->ipmask); + if ((ipmask.s_addr = inet_addr(args->ipmask)) == INADDR_NONE) { + fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask); return 1; } if (access(args->filename, R_OK) == -1) { - fprintf(stderr, "Error accessing file %s.\n", args->filename); + fprintf(stderr, "Error accessing file '%s'.\n", args->filename); return 1; } err = 1; - sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_NMRP)); - if (sock == -1) { - perror("socket"); + sock = ethsock_create(args->intf, ETH_P_NMRP); + if (!sock) { return 1; } - if (intf_get_info(sock, args->intf, &addr.sll_ifindex, src)) { - return 1; - } + gsock = sock; + sigh_orig = signal(SIGINT, sigh); - if (sock_bind_to_intf(sock, args->intf)) { - return 1; + if (ethsock_set_timeout(sock, args->rx_timeout)) { + goto out; } - if (sock_set_rx_timeout(sock, args->rx_timeout)) { - return 1; + src = ethsock_get_hwaddr(sock); + if (!src) { + goto out; } - addr.sll_family = AF_PACKET; - addr.sll_protocol = htons(ETH_P_NMRP); - addr.sll_halen = ETH_ALEN; - memcpy(addr.sll_addr, dest, ETH_ALEN); - - memcpy(tx.eh.ether_shost, src, ETH_ALEN); - memcpy(tx.eh.ether_dhost, dest, ETH_ALEN); + memcpy(tx.eh.ether_shost, src, 6); + memcpy(tx.eh.ether_dhost, dest, 6); tx.eh.ether_type = htons(ETH_P_NMRP); tx.msg.reserved = 0; @@ -351,18 +341,18 @@ int nmrp_do(struct nmrpd_args *args) beg = time(NULL); while (1) { - printf("\rAdvertising NMRP server on %s ... %c", args->intf, + printf("\rAdvertising NMRP server on interface ... %c", spinner[i]); fflush(stdout); i = (i + 1) & 3; - if (pkt_send(sock, &addr, &tx) < 0) { + if (pkt_send(sock, &tx) < 0) { perror("sendto"); goto out; } err = pkt_recv(sock, &rx); - if (err == 0 && memcmp(rx.eh.ether_dhost, src, ETH_ALEN) == 0) { + if (err == 0 && memcmp(rx.eh.ether_dhost, src, 6) == 0) { break; } else if (err == 1) { printf("ERR\n"); @@ -408,24 +398,23 @@ int nmrp_do(struct nmrpd_args *args) tx.msg.num_opts = 2; tx.msg.opts[0].type = NMRP_O_DEV_IP; - tx.msg.opts[0].len = NMRP_OPT_LEN + 2 * IP_LEN; + tx.msg.opts[0].len = NMRP_OPT_LEN + 2 * 4; - memcpy(tx.msg.opts[0].val.ip.addr, &ipaddr, IP_LEN); - memcpy(tx.msg.opts[0].val.ip.mask, &ipmask, IP_LEN); + 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; expect = NMRP_C_TFTP_UL_REQ; - printf("Configuration request received from " + printf("Received configuration request from " "%02x:%02x:%02x:%02x:%02x:%02x.\n", rx.eh.ether_shost[0], rx.eh.ether_shost[1], rx.eh.ether_shost[2], rx.eh.ether_shost[3], rx.eh.ether_shost[4], rx.eh.ether_shost[5]); - memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, ETH_ALEN); - memcpy(addr.sll_addr, rx.eh.ether_shost, ETH_ALEN); + memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, 6); printf("Sending configuration: ip %s, mask %s.\n", args->ipaddr, args->ipmask); @@ -451,7 +440,7 @@ int nmrp_do(struct nmrpd_args *args) if (!err) { printf("OK\nWaiting for remote to respond.\n"); - sock_set_rx_timeout(sock, args->ul_timeout); + ethsock_set_timeout(sock, args->ul_timeout); expect = NMRP_C_CLOSE_REQ; } else { printf("\n"); @@ -478,7 +467,7 @@ int nmrp_do(struct nmrpd_args *args) msg_update_len(&tx.msg); msg_hton(&tx.msg); - if (pkt_send(sock, &addr, &tx) < 0) { + if (pkt_send(sock, &tx) < 0) { perror("sendto"); goto out; } @@ -497,14 +486,15 @@ int nmrp_do(struct nmrpd_args *args) goto out; } - sock_set_rx_timeout(sock, args->rx_timeout); + ethsock_set_timeout(sock, args->rx_timeout); } while (1); err = 0; out: - close(sock); - + signal(SIGINT, sigh_orig); + gsock = NULL; + ethsock_close(sock); return err; }