6c4defda08e9e6652f708dd1d3c0934a52734110
[oweals/nmrpflash.git] / ethsock.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 <sys/types.h>
21 #include <stdbool.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include "nmrpd.h"
28
29 #if defined(NMRPFLASH_WINDOWS)
30 #define NMRPFLASH_NETALIAS_PREFIX "net"
31 #define WPCAP
32 #include <pcap.h>
33 #else
34 #include <sys/ioctl.h>
35 #include <ifaddrs.h>
36 #include <unistd.h>
37 #include <net/if.h>
38 #include <pcap.h>
39 #if defined(NMRPFLASH_LINUX)
40 #define NMRPFLASH_AF_PACKET AF_PACKET
41 #include <linux/if_packet.h>
42 #include <netlink/route/addr.h>
43 #include <netlink/route/neighbour.h>
44 #else
45 #define NMRPFLASH_AF_PACKET AF_LINK
46 #include <net/if_types.h>
47 #include <net/if_media.h>
48 #endif
49 #endif
50
51 struct ethsock
52 {
53         const char *intf;
54         pcap_t *pcap;
55 #ifndef NMRPFLASH_WINDOWS
56         int fd;
57 #ifdef NMRPFLASH_LINUX
58         bool stp;
59 #endif
60 #else
61         HANDLE handle;
62         DWORD index;
63 #endif
64         unsigned timeout;
65         uint8_t hwaddr[6];
66 };
67
68 struct ethsock_arp_undo
69 {
70         uint32_t ipaddr;
71         uint8_t hwaddr[6];
72 };
73
74 struct ethsock_ip_undo
75 {
76 #ifndef NMRPFLASH_WINDOWS
77         uint32_t ip[2];
78 #else
79         ULONG context;
80 #endif
81 };
82
83 const char *mac_to_str(uint8_t *mac)
84 {
85         static char buf[18];
86         snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
87                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
88         return buf;
89 }
90
91 static int x_pcap_findalldevs(pcap_if_t **devs)
92 {
93         char errbuf[PCAP_ERRBUF_SIZE];
94         if (pcap_findalldevs(devs, errbuf) != 0) {
95                 fprintf(stderr, "%s.\n", errbuf);
96                 return -1;
97         }
98
99         return 0;
100 }
101
102 static int systemf(const char *fmt, ...)
103 {
104         char cmd[1024];
105         int ret;
106         va_list va;
107         va_start(va, fmt);
108
109         ret = vsnprintf(cmd, sizeof(cmd) - 1, fmt, va);
110         if (ret >= sizeof(cmd) - 1) {
111                 return -1;
112         }
113
114         ret = system(cmd);
115         va_end(va);
116
117         return ret;
118 }
119
120 #ifndef NMRPFLASH_WINDOWS
121 static inline bool sockaddr_get_hwaddr(struct sockaddr *sa, uint8_t *hwaddr)
122 {
123         void *src;
124
125         if (sa->sa_family != NMRPFLASH_AF_PACKET) {
126                 return false;
127         }
128
129 #ifndef NMRPFLASH_LINUX
130         if (((struct sockaddr_dl*)sa)->sdl_type != IFT_ETHER) {
131                 return false;
132         }
133         src = LLADDR((struct sockaddr_dl*)sa);
134 #else
135         src = ((struct sockaddr_ll*)sa)->sll_addr;
136 #endif
137
138         memcpy(hwaddr, src, 6);
139         return true;
140 }
141
142 #ifdef NMRPFLASH_LINUX
143 static int bridge_stp_state(const char *intf)
144 {
145         char name[256];
146         snprintf(name, sizeof(name), "/sys/class/net/%s/bridge/stp_state", intf);
147         return open(name, O_RDWR, 0644);
148 }
149
150 static bool bridge_stp_enabled(const char *intf)
151 {
152         char c;
153         int fd = bridge_stp_state(intf);
154         if (fd == -1) {
155                 return false;
156         }
157
158         if (read(fd, &c, 1) != 1) {
159                 c = '0';
160         }
161
162         close(fd);
163         return c == '1';
164 }
165
166 static bool bridge_stp(const char *intf, bool enabled)
167 {
168         bool ret;
169         const char *s = enabled ? "1\n" : "0\n";
170         int fd = bridge_stp_state(intf);
171         if (fd == -1) {
172                 return false;
173         }
174
175         ret = (write(fd, s, 2) == 2);
176         close(fd);
177
178         return ret;
179 }
180
181 static bool xrtnl_addr_set(struct rtnl_addr *ra, uint32_t addr, int (*cb)(struct rtnl_addr*, struct nl_addr*))
182 {
183         int err;
184         struct nl_addr *na = nl_addr_build(AF_INET, &addr, 4);
185
186         if (!na) {
187                 xperror("nl_addr_build");
188                 return false;
189         }
190
191         if ((err = cb(ra, na)) != 0 && verbosity) {
192                 nl_perror(err, __func__);
193         }
194         nl_addr_put(na);
195
196         return true;
197 }
198
199 static struct nl_sock *xnl_socket_route()
200 {
201         int err;
202         struct nl_sock *sk = nl_socket_alloc();
203         if (sk) {
204                 if (!(err = nl_connect(sk, NETLINK_ROUTE))) {
205                         return sk;
206                 }
207                 nl_socket_free(sk);
208                 nl_perror(err, "nl_connect");
209         } else {
210                 xperror("nl_socket_alloc");
211         }
212
213         return NULL;
214 }
215
216 static bool intf_add_del_ip(const char *intf, uint32_t ipaddr, uint32_t ipmask, bool add)
217 {
218         struct rtnl_addr *ra = NULL;
219         struct nl_sock *sk = NULL;
220         int err = 1;
221
222         if (!(sk = xnl_socket_route())) {
223                 return false;
224         }
225
226         if (!(ra = rtnl_addr_alloc())) {
227                 xperror("rtnl_addr_alloc");
228                 goto out;
229         }
230
231         rtnl_addr_set_ifindex(ra, if_nametoindex(intf));
232         rtnl_addr_set_prefixlen(ra, bitcount(ipmask));
233
234         if (!xrtnl_addr_set(ra, (ipaddr & ipmask) | ~ipmask, &rtnl_addr_set_broadcast)
235                         || !xrtnl_addr_set(ra, ipaddr, &rtnl_addr_set_local)) {
236                 goto out;
237         }
238
239         if ((err = add ? rtnl_addr_add(sk, ra, 0) : rtnl_addr_delete(sk, ra, 0)) < 0) {
240                 if (add && err == -NLE_EXIST) {
241                         err = 0;
242                 } else if (add || verbosity > 1) {
243                         nl_perror(err, add ? "rtnl_addr_add" : "rtnl_addr_delete");
244                 }
245         }
246
247 out:
248         rtnl_addr_put(ra);
249         nl_socket_free(sk);
250
251         return !err;
252 }
253
254 static bool intf_add_del_arp(const char *intf, uint32_t ipaddr, uint8_t *hwaddr, bool add)
255 {
256         struct nl_sock *sk;
257         struct rtnl_neigh *neigh;
258         struct nl_addr *mac, *ip;
259         int err = 1;
260
261         sk = NULL;
262         neigh = NULL;
263         mac = ip = NULL;
264
265         if (!(sk = xnl_socket_route())) {
266                 goto out;
267         }
268
269         if (!(neigh = rtnl_neigh_alloc())) {
270                 xperror("rtnl_neigh_alloc");
271                 goto out;
272         }
273
274         if (!(mac = nl_addr_build(AF_PACKET, hwaddr, 6))) {
275                 xperror("nl_addr_build");
276                 goto out;
277         }
278
279         if (!(ip = nl_addr_build(AF_INET, &ipaddr, 4))) {
280                 xperror("nl_addr_build");
281                 goto out;
282         }
283
284         rtnl_neigh_set_ifindex(neigh, if_nametoindex(intf));
285         rtnl_neigh_set_dst(neigh, ip);
286
287         err = rtnl_neigh_delete(sk, neigh, 0);
288
289         if (add) {
290                 rtnl_neigh_set_lladdr(neigh, mac);
291                 rtnl_neigh_set_state(neigh, NUD_PERMANENT);
292                 system("arp -an");
293                 err = rtnl_neigh_add(sk, neigh, NLM_F_CREATE);
294                 system("arp -an");
295         }
296
297         if (err && (add || verbosity > 1)) {
298                 nl_perror(err, add ? "rtnl_neigh_add" : "rtnl_neigh_delete");
299         }
300
301 out:
302         nl_addr_put(ip);
303         nl_addr_put(mac);
304         rtnl_neigh_put(neigh);
305         nl_socket_free(sk);
306
307         return !err;
308 }
309
310 #endif
311
312 static bool intf_get_info(const char *intf, uint8_t *hwaddr, bool *bridge)
313 {
314         struct ifaddrs *ifas, *ifa;
315         bool found;
316
317         if (getifaddrs(&ifas) != 0) {
318                 xperror("getifaddrs");
319                 return false;
320         }
321
322         found = false;
323
324         if (bridge) {
325                 *bridge = false;
326         }
327
328         for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
329                 if (!strcmp(ifa->ifa_name, intf)) {
330                         if (sockaddr_get_hwaddr(ifa->ifa_addr, hwaddr)) {
331 #ifdef NMRPFLASH_BSD
332                                 if (bridge) {
333                                         *bridge = ((struct if_data*) ifa->ifa_data)->ifi_type == IFT_BRIDGE;
334                                 }
335 #endif
336                                 found = true;
337                                 break;
338                         }
339                 }
340         }
341
342         freeifaddrs(ifas);
343         return found;
344 }
345
346 #else
347
348 void win_perror2(const char *msg, DWORD err)
349 {
350         char *buf = NULL;
351         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
352                         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
353                         NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
354                         (LPTSTR)&buf, 0, NULL);
355
356         if (buf) {
357                 /* FormatMessageA terminates buf with CRLF! */
358                 fprintf(stderr, "%s: %s", msg, buf);
359                 LocalFree(buf);
360         } else {
361                 fprintf(stderr, "%s: error %d\n", msg, (int)err);
362         }
363 }
364
365 static bool intf_get_info(const char *intf, uint8_t *hwaddr, DWORD *index)
366 {
367         PIP_ADAPTER_INFO adapters, adapter;
368         DWORD ret;
369         ULONG i, bufLen = 0;
370         bool found = false;
371
372         if ((ret = GetAdaptersInfo(NULL, &bufLen)) != ERROR_BUFFER_OVERFLOW) {
373                 win_perror2("GetAdaptersInfo", ret);
374                 return false;
375         }
376
377         adapters = malloc(bufLen);
378         if (!adapters) {
379                 xperror("malloc");
380                 return false;
381         }
382
383         if ((ret = GetAdaptersInfo(adapters, &bufLen) == NO_ERROR)) {
384                 for (adapter = adapters; adapter; adapter = adapter->Next) {
385                         if (adapter->Type != MIB_IF_TYPE_ETHERNET && adapter->Type != IF_TYPE_IEEE80211) {
386                                 continue;
387                         }
388
389                         /* Interface names from WinPcap are "\Device\NPF_{GUID}", while
390                          * AdapterName from GetAdaptersInfo is just "{GUID}".*/
391                         if (strstr(intf, adapter->AdapterName)) {
392                                 if (adapter->AddressLength == 6) {
393                                         memcpy(hwaddr, adapter->Address, 6);
394                                         if (index) {
395                                                 *index = adapter->Index;
396                                         }
397                                         found = true;
398                                         break;
399                                 }
400                         }
401                 }
402         } else {
403                 win_perror2("GetAdaptersInfo", ret);
404         }
405
406         free(adapters);
407         return found;
408 }
409
410 static const char *intf_alias_to_wpcap(const char *intf)
411 {
412         static char buf[128];
413         pcap_if_t *devs, *dev;
414         unsigned i = 0, dev_num = 0;
415
416         if (intf[0] == '\\') {
417                 return intf;
418         } else if (sscanf(intf, NMRPFLASH_NETALIAS_PREFIX "%u", &dev_num) != 1) {
419                 fprintf(stderr, "Invalid interface alias.\n");
420                 return NULL;
421         }
422
423         if (x_pcap_findalldevs(&devs) != 0) {
424                 return NULL;
425         }
426
427         for (dev = devs; dev; dev = dev->next, ++i) {
428                 if (i == dev_num) {
429                         if (verbosity) {
430                                 printf("%s%u: %s\n", NMRPFLASH_NETALIAS_PREFIX, i, dev->name);
431                         }
432                         strncpy(buf, dev->name, sizeof(buf) - 1);
433                         buf[sizeof(buf) - 1] = '\0';
434                         break;
435                 }
436         }
437
438         pcap_freealldevs(devs);
439
440         if (!dev) {
441                 fprintf(stderr, "Interface alias not found.\n");
442                 return NULL;
443         }
444
445         return buf;
446 }
447
448 static const char *intf_get_pretty_name(const char *intf)
449 {
450         static char buf[512];
451         char *guid;
452         HKEY hkey;
453         LONG err;
454         DWORD len;
455
456         guid = strstr(intf, "NPF_{");
457         if (!guid) {
458                 return NULL;
459         }
460
461         guid += 4;
462
463         snprintf(buf, sizeof(buf),
464                         "System\\CurrentControlSet\\Control\\Network\\"
465                         "{4D36E972-E325-11CE-BFC1-08002BE10318}\\"
466                         "%s\\Connection", guid);
467         err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &hkey);
468         if (err != ERROR_SUCCESS) {
469                 if (verbosity > 1) {
470                         win_perror2("RegOpenKeyExA", err);
471                 }
472                 return NULL;
473         }
474
475         len = sizeof(buf);
476         err = RegQueryValueExA(hkey, "Name", NULL, NULL, (LPBYTE)buf, &len);
477         if (err == ERROR_SUCCESS) {
478                 intf = buf;
479         } else {
480                 if (verbosity > 1) {
481                         win_perror2("RegQueryValueExA", err);
482                 }
483                 intf = NULL;
484         }
485
486         RegCloseKey(hkey);
487         return intf;
488 }
489 #endif
490
491 inline uint8_t *ethsock_get_hwaddr(struct ethsock *sock)
492 {
493         return sock->hwaddr;
494 }
495
496 struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
497 {
498         char buf[PCAP_ERRBUF_SIZE];
499         struct bpf_program bpf;
500         struct ethsock *sock;
501         bool is_bridge;
502         int err;
503
504 #ifdef NMRPFLASH_WINDOWS
505         intf = intf_alias_to_wpcap(intf);
506         if (!intf) {
507                 return NULL;
508         }
509 #endif
510
511         sock = malloc(sizeof(struct ethsock));
512         if (!sock) {
513                 xperror("malloc");
514                 return NULL;
515         }
516
517         buf[0] = '\0';
518
519         sock->intf = intf;
520         sock->pcap = pcap_open_live(sock->intf, BUFSIZ, 1, 1, buf);
521         if (!sock->pcap) {
522                 fprintf(stderr, "%s.\n", buf);
523                 goto cleanup;
524         }
525
526         if (*buf) {
527                 fprintf(stderr, "Warning: %s.\n", buf);
528         }
529
530         if (pcap_datalink(sock->pcap) != DLT_EN10MB) {
531                 fprintf(stderr, "%s is not an ethernet interface.\n",
532                                 intf);
533                 goto cleanup;
534         }
535
536 #ifndef NMRPFLASH_WINDOWS
537         err = !intf_get_info(intf, sock->hwaddr, &is_bridge);
538 #else
539         err = !intf_get_info(intf, sock->hwaddr, &sock->index);
540 #endif
541         if (err) {
542                 fprintf(stderr, "Failed to get interface info.\n");
543                 goto cleanup;
544         }
545
546 #ifndef NMRPFLASH_WINDOWS
547         sock->fd = pcap_get_selectable_fd(sock->pcap);
548         if (sock->fd == -1) {
549                 pcap_perror(sock->pcap, "pcap_get_selectable_fd");
550                 goto cleanup;
551         }
552 #else
553         sock->handle = pcap_getevent(sock->pcap);
554         if (!sock->handle) {
555                 pcap_perror(sock->pcap, "pcap_getevent");
556                 goto cleanup;
557         }
558
559         err = pcap_setmintocopy(sock->pcap, 1);
560         if (err) {
561                 pcap_perror(sock->pcap, "pcap_setmintocopy");
562                 goto cleanup;
563         }
564 #endif
565
566         err = pcap_setdirection(sock->pcap, PCAP_D_IN);
567         if (err) {
568                 pcap_perror(sock->pcap, "pcap_setdirection");
569                 goto cleanup;
570         }
571
572         snprintf(buf, sizeof(buf), "ether proto 0x%04x", protocol);
573
574         err = pcap_compile(sock->pcap, &bpf, buf, 0, 0);
575         if (err) {
576                 pcap_perror(sock->pcap, "pcap_compile");
577                 goto cleanup;
578         }
579
580         err = pcap_setfilter(sock->pcap, &bpf);
581         pcap_freecode(&bpf);
582
583         if (err) {
584                 pcap_perror(sock->pcap, "pcap_setfilter");
585                 goto cleanup;
586         }
587
588 #ifdef NMRPFLASH_LINUX
589         // nmrpflash does not work on bridge interfaces with STP enabled
590         if ((sock->stp = bridge_stp_enabled(intf))) {
591                 if (!bridge_stp(intf, false)) {
592                         fprintf(stderr, "Warning: failed to disable STP on %s.\n", intf);
593                 }
594         }
595 #else
596         if (is_bridge) {
597                 fprintf(stderr, "Warning: bridge interfaces are not fully "
598                                 "supported on this platform.\n");
599         }
600 #endif
601
602         return sock;
603
604 cleanup:
605         ethsock_close(sock);
606         return NULL;
607 }
608
609 int select_fd(int fd, unsigned timeout)
610 {
611         struct timeval tv;
612         int status;
613         fd_set fds;
614
615         FD_ZERO(&fds);
616         FD_SET(fd, &fds);
617
618         tv.tv_sec = timeout / 1000;
619         tv.tv_usec = 1000 * (timeout % 1000);
620
621         status = select(fd + 1, &fds, NULL, NULL, &tv);
622         if (status < 0) {
623                 sock_perror("select");
624         }
625
626         return status;
627 }
628
629 ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len)
630 {
631         struct pcap_pkthdr* hdr;
632         const u_char *capbuf;
633         int status;
634 #ifdef NMRPFLASH_WINDOWS
635         DWORD ret;
636
637         if (sock->timeout) {
638                 ret = WaitForSingleObject(sock->handle, sock->timeout);
639                 if (ret == WAIT_TIMEOUT) {
640                         return 0;
641                 } else if (ret != WAIT_OBJECT_0) {
642                         win_perror2("WaitForSingleObject", ret);
643                         return -1;
644                 }
645         }
646 #else
647         if (sock->timeout) {
648                 status = select_fd(sock->fd, sock->timeout);
649                 if (status < 0) {
650                         return -1;
651                 } else if (status == 0) {
652                         return 0;
653                 }
654         }
655 #endif
656
657         status = pcap_next_ex(sock->pcap, &hdr, &capbuf);
658         switch (status) {
659                 case 1:
660                         memcpy(buf, capbuf, MIN(len, hdr->caplen));
661                         return hdr->caplen;
662                 case 0:
663                         return 0;
664                 case -1:
665                         pcap_perror(sock->pcap, "pcap_next_ex");
666                         return -1;
667                 default:
668                         fprintf(stderr, "pcap_next_ex: returned %d.\n", status);
669                         return -1;
670         }
671 }
672
673 int ethsock_send(struct ethsock *sock, void *buf, size_t len)
674 {
675 #ifdef NMRPFLASH_WINDOWS
676         if (pcap_sendpacket(sock->pcap, buf, len) == 0) {
677                 return 0;
678         } else {
679                 pcap_perror(sock->pcap, "pcap_sendpacket");
680                 return -1;
681         }
682 #else
683         if (pcap_inject(sock->pcap, buf, len) == len) {
684                 return 0;
685         } else {
686                 pcap_perror(sock->pcap, "pcap_inject");
687                 return -1;
688         }
689 #endif
690 }
691
692 int ethsock_close(struct ethsock *sock)
693 {
694         if (!sock) {
695                 return 0;
696         }
697
698 #ifdef NMRPFLASH_LINUX
699         if (sock->stp) {
700                 bridge_stp(sock->intf, true);
701         }
702 #endif
703         if (sock->pcap) {
704                 pcap_close(sock->pcap);
705         }
706
707         free(sock);
708         return 0;
709 }
710
711 inline int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
712 {
713         sock->timeout = msec;
714         return 0;
715 }
716
717 static int ethsock_arp(struct ethsock *sock, uint8_t *hwaddr, uint32_t ipaddr, struct ethsock_arp_undo **undo)
718 {
719 #if defined(NMRPFLASH_UNIX) && !defined(NMRPFLASH_LINUX)
720         struct in_addr addr = { .s_addr = ipaddr };
721 #elif defined(NMRPFLASH_WINDOWS)
722         DWORD err;
723         MIB_IPNETROW arp = {
724                 .dwIndex = sock->index,
725                 .dwPhysAddrLen = 6,
726                 .dwAddr = ipaddr,
727                 .dwType = MIB_IPNET_TYPE_STATIC
728         };
729
730         memcpy(arp.bPhysAddr, hwaddr, 6);
731 #endif
732
733         if (undo) {
734 #if defined(NMRPFLASH_LINUX)
735                 if (!intf_add_del_arp(sock->intf, ipaddr, hwaddr, true)) {
736                         return -1;
737                 }
738 #elif defined(NMRPFLASH_WINDOWS)
739                 err = CreateIpNetEntry(&arp);
740                 if (err != NO_ERROR) {
741                         win_perror2("CreateIpNetEntry", err);
742                         return -1;
743                 }
744 #else
745                 if (systemf("arp -s %s %s", inet_ntoa(addr), mac_to_str(hwaddr)) != 0) {
746                         return -1;
747                 }
748 #endif
749
750                 *undo = malloc(sizeof(struct ethsock_arp_undo));
751                 if (!*undo) {
752                         xperror("malloc");
753                         return -1;
754                 }
755
756                 (*undo)->ipaddr = ipaddr;
757                 memcpy((*undo)->hwaddr, hwaddr, 6);
758         } else {
759 #if defined(NMRPFLASH_LINUX)
760                 if (!intf_add_del_arp(sock->intf, ipaddr, hwaddr, false)) {
761                         return -1;
762                 }
763 #elif defined(NMRPFLASH_WINDOWS)
764                 return DeleteIpNetEntry(&arp) ? 0 : -1;
765 #else
766                 return systemf("arp -d %s", inet_ntoa(addr);
767 #endif
768         }
769
770         return 0;
771 }
772
773 int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, uint32_t ipaddr, struct ethsock_arp_undo **undo)
774 {
775         ethsock_arp(sock, hwaddr, ipaddr, NULL);
776         return undo ? ethsock_arp(sock, hwaddr, ipaddr, undo) : -1;
777 }
778
779 int ethsock_arp_del(struct ethsock *sock, struct ethsock_arp_undo **undo)
780 {
781         if (!*undo) {
782                 return 0;
783         }
784
785         int ret = ethsock_arp(sock, (*undo)->hwaddr, (*undo)->ipaddr, NULL);
786         free(*undo);
787         *undo = NULL;
788         return ret;
789 }
790
791 static bool get_hwaddr_from_pcap(const pcap_if_t *dev, uint8_t *hwaddr)
792 {
793 #ifndef NMRPFLASH_WINDOWS
794         pcap_addr_t *addr;
795         int i;
796
797         for (addr = dev->addresses; addr; addr = addr->next) {
798                 if (verbosity > 1) {
799                         printf("%s: sa_family=%d, sa_data={ ", dev->name,
800                                         addr->addr->sa_family);
801                         for (i = 0; i != sizeof(addr->addr->sa_data); ++i) {
802                                 printf("%02x ", addr->addr->sa_data[i] & 0xff);
803                         }
804                         printf("}\n");
805                 }
806
807                 if (sockaddr_get_hwaddr(addr->addr, hwaddr)) {
808                         return true;
809                 }
810         }
811 #endif
812
813         return intf_get_info(dev->name, hwaddr, NULL);
814 }
815
816 int ethsock_list_all(void)
817 {
818         pcap_if_t *devs, *dev;
819         pcap_addr_t *addr;
820         uint8_t hwaddr[6];
821         unsigned dev_num = 0, dev_ok = 0;
822 #ifdef NMRPFLASH_WINDOWS
823         const char *pretty;
824 #endif
825
826         if (x_pcap_findalldevs(&devs) != 0) {
827                 return -1;
828         }
829
830         memset(hwaddr, 0, 6);
831
832         for (dev = devs; dev; dev = dev->next, ++dev_num) {
833                 if (dev->flags & PCAP_IF_LOOPBACK) {
834                         if (verbosity) {
835                                 printf("%-15s  (loopback device)\n", dev->name);
836                         }
837                         continue;
838                 }
839
840                 if (!get_hwaddr_from_pcap(dev, hwaddr)) {
841                         if (verbosity) {
842                                 printf("%-15s  (not an ethernet device)\n",
843                                                 dev->name);
844                         }
845                         continue;
846                 }
847
848 #ifndef NMRPFLASH_WINDOWS
849                 printf("%-15s", dev->name);
850 #else
851                 /* Call this here so *_perror() calls don't happen within a line */
852                 pretty = intf_get_pretty_name(dev->name);
853
854                 if (!verbosity) {
855                         printf("%s%-2u", NMRPFLASH_NETALIAS_PREFIX, dev_num);
856                 } else {
857                         printf("%s", dev->name);
858                 }
859 #endif
860
861                 for (addr = dev->addresses; addr; addr = addr->next) {
862                         if (addr->addr->sa_family == AF_INET) {
863                                 printf("  %-15s",
864                                                 inet_ntoa(((struct sockaddr_in*)addr->addr)->sin_addr));
865                                 break;
866                         }
867                 }
868
869                 if (!addr) {
870                         printf("  %-15s", "0.0.0.0");
871                 }
872
873                 printf("  %s", mac_to_str(hwaddr));
874
875 #ifdef NMRPFLASH_WINDOWS
876                 if (pretty) {
877                         printf("  (%s)", pretty);
878                 } else if (dev->description) {
879                         printf("  (%s)", dev->description);
880                 }
881
882 #endif
883                 printf("\n");
884                 ++dev_ok;
885         }
886
887         if (!dev_ok) {
888                 printf("No suitable network interfaces found.\n");
889         }
890
891         return 0;
892 }
893
894 int ethsock_for_each_ip(struct ethsock *sock, ethsock_ip_callback_t callback,
895                 void *arg)
896 {
897         struct ethsock_ip_callback_args args;
898         pcap_if_t *devs, *dev;
899         pcap_addr_t *addr;
900         int status = 0;
901
902         if (x_pcap_findalldevs(&devs) != 0) {
903                 return -1;
904         }
905
906         args.arg = arg;
907
908         for (dev = devs; dev; dev = dev->next) {
909                 if (strcmp(sock->intf, dev->name)) {
910                         continue;
911                 }
912
913                 for (addr = dev->addresses; addr; addr = addr->next) {
914                         if (addr->addr->sa_family == AF_INET) {
915                                 args.ipaddr = &((struct sockaddr_in*)addr->addr)->sin_addr;
916                                 args.ipmask = &((struct sockaddr_in*)addr->netmask)->sin_addr;
917
918                                 status = callback(&args);
919                                 if (status <= 0) {
920                                         break;
921                                 }
922                         }
923                 }
924
925                 break;
926         }
927
928         pcap_freealldevs(devs);
929
930         return status <= 0 ? status : 0;
931 }
932
933 static inline void set_addr(void *p, uint32_t addr)
934 {
935         struct sockaddr_in* sin = p;
936         sin->sin_family = AF_INET;
937         sin->sin_addr.s_addr = addr;
938 #ifdef NMRPFLASH_BSD
939         ((struct sockaddr*)p)->sa_len = sizeof(struct sockaddr_in);
940 #endif
941 }
942
943 #if !defined(NMRPFLASH_WINDOWS) && !defined(NMRPFLASH_LINUX)
944 static bool intf_up(int fd, const char *intf, bool up)
945 {
946         struct ifreq ifr;
947         strncpy(ifr.ifr_name, intf, IFNAMSIZ);
948
949         if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
950                 if (up) {
951                         xperror("ioctl(SIOCGIFFLAGS)");
952                 }
953                 return false;
954         }
955
956         if (!up) {
957                 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
958         } else {
959                 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
960         }
961
962         if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
963                 if (up) {
964                         xperror("ioctl(SIOCSIFFLAGS)");
965                 }
966                 return false;
967         }
968
969         return true;
970 }
971 #endif
972
973 static int ethsock_ip_add_del(struct ethsock *sock, uint32_t ipaddr, uint32_t ipmask, struct ethsock_ip_undo **undo, bool add)
974 {
975         int ret, fd;
976
977         if (add && undo) {
978                 if (!(*undo = malloc(sizeof(struct ethsock_ip_undo)))) {
979                         xperror("malloc");
980                         return -1;
981                 }
982
983                 memset(*undo, 0, sizeof(**undo));
984         }
985
986         ret = -1;
987         fd = socket(AF_INET, SOCK_DGRAM, 0);
988         if (fd < 0) {
989                 sock_perror("socket");
990                 goto out;
991         }
992
993 #ifndef NMRPFLASH_WINDOWS
994 #ifdef NMRPFLASH_LINUX
995         if (add) {
996                 (*undo)->ip[0] = ipaddr;
997                 (*undo)->ip[1] = ipmask;
998         }
999
1000         if (!intf_add_del_ip(sock->intf, (*undo)->ip[0], (*undo)->ip[1], add)) {
1001                 goto out;
1002         }
1003 #else // NMRPFLASH_OSX (or any other BSD)
1004         struct ifaliasreq ifra;
1005         memset(&ifra, 0, sizeof(ifra));
1006         strncpy(ifra.ifra_name, sock->intf, IFNAMSIZ);
1007
1008         set_addr(&ifra.ifra_addr, ipaddr);
1009         set_addr(&ifra.ifra_mask, ipmask);
1010         //set_addr(&ifra.ifra_broadaddr, (ipaddr & ipmask) | ~ipmask);
1011
1012         if (ioctl(fd, add ? SIOCAIFADDR : SIOCDIFADDR, &ifra) != 0) {
1013                 if (add) {
1014                         xperror("ioctl(SIOCAIFADDR");
1015                 }
1016                 goto out;
1017         }
1018
1019         if (add) {
1020                 (*undo)->ip[0] = ipaddr;
1021                 (*undo)->ip[1] = ipmask;
1022                 intf_up(fd, ifra.ifra_name, true);
1023         }
1024
1025 #endif
1026 #else // NMRPFLASH_WINDOWS
1027         struct sockaddr_in sin;
1028         ULONG instance;
1029
1030         (*undo)->context = 0;
1031
1032         DWORD err = AddIPAddress(ipaddr, ipmask, sock->index, &(*undo)->context, &instance);
1033         if (err != NO_ERROR && err != ERROR_DUP_DOMAINNAME && err != ERROR_OBJECT_ALREADY_EXISTS) {
1034                 win_perror2("AddIPAddress", err);
1035                 goto out;
1036         }
1037
1038         set_addr(&sin, ipaddr);
1039         time_t beg = time_monotonic();
1040
1041         /* Wait until the new IP has actually been added */
1042
1043         while (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
1044                 if ((time_monotonic() - beg) >= 5) {
1045                         fprintf(stderr, "Failed to bind after 5 seconds: ");
1046                         sock_perror("bind");
1047                         DeleteIPAddress((*undo)->context);
1048                         goto out;
1049                 }
1050         }
1051 #endif
1052         ret = 0;
1053
1054 out:
1055 #ifndef NMRPFLASH_WINDOWS
1056         close(fd);
1057 #else
1058         closesocket(fd);
1059 #endif
1060         if (ret != 0 && undo) {
1061                 free(*undo);
1062                 *undo = NULL;
1063         }
1064
1065         return ret;
1066 }
1067
1068 int ethsock_ip_add(struct ethsock *sock, uint32_t ipaddr, uint32_t ipmask, struct ethsock_ip_undo **undo)
1069 {
1070         return ethsock_ip_add_del(sock, ipaddr, ipmask, undo, true);
1071 }
1072
1073 int ethsock_ip_del(struct ethsock *sock, struct ethsock_ip_undo **undo)
1074 {
1075         if (!*undo) {
1076                 return 0;
1077         }
1078
1079         int ret;
1080
1081 #ifndef NMRPFLASH_WINDOWS
1082         if ((*undo)->ip[0] != INADDR_NONE) {
1083                 ret = ethsock_ip_add_del(sock, (*undo)->ip[0], (*undo)->ip[1], undo, false);
1084         } else {
1085                 ret = 0;
1086         }
1087 #else
1088         ret = DeleteIPAddress((*undo)->context) ? 0 : -1;
1089 #endif
1090
1091         free(*undo);
1092         *undo = NULL;
1093         return ret;
1094 }