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