+
+int ethsock_for_each_ip(struct ethsock *sock, ethsock_ip_callback_t callback,
+ void *arg)
+{
+ struct ethsock_ip_callback_args args;
+ pcap_if_t *devs, *dev;
+ pcap_addr_t *addr;
+ int status = 0;
+
+ if (x_pcap_findalldevs(&devs) != 0) {
+ return -1;
+ }
+
+ args.arg = arg;
+
+ for (dev = devs; dev; dev = dev->next) {
+ if (strcmp(sock->intf, dev->name)) {
+ continue;
+ }
+
+ for (addr = dev->addresses; addr; addr = addr->next) {
+ if (addr->addr->sa_family == AF_INET) {
+ args.ipaddr = &((struct sockaddr_in*)addr->addr)->sin_addr;
+ args.ipmask = &((struct sockaddr_in*)addr->netmask)->sin_addr;
+
+ status = callback(&args);
+ if (status <= 0) {
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+
+ pcap_freealldevs(devs);
+
+ return status <= 0 ? status : 0;
+}
+
+static inline void set_addr(void *p, uint32_t addr)
+{
+ struct sockaddr_in* sin = p;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = addr;
+#ifdef NMRPFLASH_BSD
+ ((struct sockaddr*)p)->sa_len = sizeof(struct sockaddr_in);
+#endif
+}
+
+#ifndef NMRPFLASH_WINDOWS
+static bool set_interface_up(int fd, const char *intf, bool up)
+{
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, intf, IFNAMSIZ);
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+ if (up) {
+ xperror("ioctl(SIOCGIFFLAGS)");
+ }
+ return false;
+ }
+
+ if (!up) {
+ ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
+ } else {
+ ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+ }
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+ if (up) {
+ xperror("ioctl(SIOCSIFFLAGS)");
+ }
+ return false;
+ }
+
+ return true;
+}
+
+#endif
+
+int ethsock_ip_add(struct ethsock *sock, uint32_t ipaddr, uint32_t ipmask, struct ethsock_ip_undo **undo)
+{
+ if (undo && !(*undo = malloc(sizeof(struct ethsock_ip_undo)))) {
+ xperror("malloc");
+ return -1;
+ }
+
+ int ret = -1;
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (!fd) {
+ sock_perror("socket");
+ goto out;
+ }
+
+#ifndef NMRPFLASH_WINDOWS
+ // XXX: undo is non-zero only if we're adding an IP
+ bool add = undo;
+#ifdef NMRPFLASH_LINUX
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, sock->intf, IFNAMSIZ);
+ // FIXME: automatically determine the next free alias
+ strcat(ifr.ifr_name, ":42");
+
+ if (add) {
+ set_addr(&ifr.ifr_addr, ipaddr);
+ if (ioctl(fd, SIOCSIFADDR, &ifr) != 0) {
+ xperror("ioctl(SIOSIFADDR)");
+ goto out;
+ }
+
+ set_addr(&ifr.ifr_netmask, ipmask);
+ if (ioctl(fd, SIOCSIFNETMASK, &ifr) != 0) {
+ xperror("ioctl(SIOCSIFNETMASK)");
+ goto out;
+ }
+
+ (*undo)->ip[0] = ipaddr;
+ (*undo)->ip[1] = ipmask;
+ }
+
+ if (!set_interface_up(fd, ifr.ifr_name, add)) {
+ goto out;
+ }
+#else // NMRPFLASH_OSX (or any other BSD)
+ struct ifaliasreq ifra;
+ memset(&ifra, 0, sizeof(ifra));
+ strncpy(ifra.ifra_name, sock->intf, IFNAMSIZ);
+
+ set_addr(&ifra.ifra_addr, ipaddr);
+ set_addr(&ifra.ifra_mask, ipmask);
+ //set_addr(&ifra.ifra_broadaddr, (ipaddr & ipmask) | ~ipmask);
+
+ if (ioctl(fd, add ? SIOCAIFADDR : SIOCDIFADDR, &ifra) != 0) {
+ if (add) {
+ xperror("ioctl(SIOCAIFADDR");
+ }
+ goto out;
+ }
+
+ if (add) {
+ (*undo)->ip[0] = ipaddr;
+ (*undo)->ip[1] = ipmask;
+ set_interface_up(fd, ifra.ifra_name, true);
+ }
+
+#endif
+#else // NMRPFLASH_WINDOWS
+ struct sockaddr_in sin;
+ ULONG instance;
+
+ (*undo)->context = 0;
+
+ DWORD err = AddIPAddress(ipaddr, ipmask, sock->index, &(*undo)->context, &instance);
+ if (err != NO_ERROR && err != ERROR_DUP_DOMAINNAME && err != ERROR_OBJECT_ALREADY_EXISTS) {
+ win_perror2("AddIPAddress", err);
+ goto out;
+ }
+
+ set_addr(&sin, ipaddr);
+ time_t beg = time_monotonic();
+
+ /* Wait until the new IP has actually been added */
+
+ while (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
+ if ((time_monotonic() - beg) >= 5) {
+ fprintf(stderr, "Failed to bind after 5 seconds: ");
+ sock_perror("bind");
+ DeleteIPAddress((*undo)->context);
+ goto out;
+ }
+ }
+#endif
+ ret = 0;
+
+out:
+#ifndef NMRPFLASH_WINDOWS
+ close(fd);
+#else
+ closesocket(fd);
+#endif
+ if (ret != 0 && undo) {
+ free(*undo);
+ *undo = NULL;
+ }
+
+ return ret;
+}
+
+int ethsock_ip_del(struct ethsock *sock, struct ethsock_ip_undo **undo)
+{
+ if (!*undo) {
+ return 0;
+ }
+
+ int ret;
+
+#ifndef NMRPFLASH_WINDOWS
+ if ((*undo)->ip[0] != INADDR_NONE) {
+ ret = ethsock_ip_add(sock, (*undo)->ip[0], (*undo)->ip[1], NULL);
+ } else {
+ ret = 0;
+ }
+#else
+ DeleteIPAddress((*undo)->context);
+ ret = 0;
+#endif
+
+ free(*undo);
+ *undo = NULL;
+ return ret;
+}