+
+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
+}
+
+#if !defined(NMRPFLASH_WINDOWS) && !defined(NMRPFLASH_LINUX)
+static bool intf_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
+
+static int ethsock_ip_add_del(struct ethsock *sock, uint32_t ipaddr, uint32_t ipmask, struct ethsock_ip_undo **undo, bool add)
+{
+ int ret, fd;
+
+ if (add && undo) {
+ if (!(*undo = malloc(sizeof(struct ethsock_ip_undo)))) {
+ xperror("malloc");
+ return -1;
+ }
+
+ memset(*undo, 0, sizeof(**undo));
+ }
+
+ ret = -1;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ sock_perror("socket");
+ goto out;
+ }
+
+#ifndef NMRPFLASH_WINDOWS
+#ifdef NMRPFLASH_LINUX
+ if (add) {
+ (*undo)->ip[0] = ipaddr;
+ (*undo)->ip[1] = ipmask;
+ }
+
+ if (!intf_add_del_ip(sock->intf, (*undo)->ip[0], (*undo)->ip[1], 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;
+ intf_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_add(struct ethsock *sock, uint32_t ipaddr, uint32_t ipmask, struct ethsock_ip_undo **undo)
+{
+ return ethsock_ip_add_del(sock, ipaddr, ipmask, undo, true);
+}
+
+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_del(sock, (*undo)->ip[0], (*undo)->ip[1], undo, false);
+ } else {
+ ret = 0;
+ }
+#else
+ ret = DeleteIPAddress((*undo)->context) ? 0 : -1;
+#endif
+
+ free(*undo);
+ *undo = NULL;
+ return ret;
+}