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