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