X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=ethsock.c;h=75e166867047e8d9026fb06a688b05a655f45221;hb=482b03db2360198287a2a96d4bf04a9aa3320117;hp=52cde69a61831629a1b128417eeee3c4487b81e3;hpb=8b0251e7e1c51ddf71b2a84f0a9a8be2f51396ae;p=oweals%2Fnmrpflash.git diff --git a/ethsock.c b/ethsock.c index 52cde69..75e1668 100644 --- a/ethsock.c +++ b/ethsock.c @@ -10,34 +10,43 @@ #define WPCAP #include #else -#include +#include #include +#include +#include +#include #if defined(NMRPFLASH_LINUX) -#define NMRPFLASH_AF_ETHERNET AF_PACKET +#define NMRPFLASH_AF_PACKET AF_PACKET #include -#elif defined (NMRPFLASH_OSX) -#define NMRPFLASH_AF_ETHERNET AF_LINK -#include -#endif +#else +#define NMRPFLASH_AF_PACKET AF_LINK +#include #endif - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif - struct ethsock { + const char *intf; pcap_t *pcap; #ifndef NMRPFLASH_WINDOWS int fd; #else HANDLE handle; + DWORD index; #endif unsigned timeout; uint8_t hwaddr[6]; }; +struct ethsock_ip_undo +{ +#ifndef NMRPFLASH_WINDOWS + uint32_t ip[2]; +#else + ULONG context; +#endif +}; + const char *mac_to_str(uint8_t *mac) { static char buf[18]; @@ -58,10 +67,30 @@ static int x_pcap_findalldevs(pcap_if_t **devs) } #ifndef NMRPFLASH_WINDOWS -static bool get_hwaddr(uint8_t *hwaddr, const char *intf) +static inline bool sockaddr_get_hwaddr(struct sockaddr *sa, uint8_t *hwaddr) { - struct ifaddrs *ifas, *ifa; void *src; + + if (sa->sa_family != NMRPFLASH_AF_PACKET) { + return false; + } + +#ifndef NMRPFLASH_LINUX + if (((struct sockaddr_dl*)sa)->sdl_type != IFT_ETHER) { + return false; + } + src = LLADDR((struct sockaddr_dl*)sa); +#else + src = ((struct sockaddr_ll*)sa)->sll_addr; +#endif + + memcpy(hwaddr, src, 6); + return true; +} + +static bool get_intf_info(const char *intf, uint8_t *hwaddr, void *dummy) +{ + struct ifaddrs *ifas, *ifa; bool found; if (getifaddrs(&ifas) != 0) { @@ -73,23 +102,17 @@ static bool get_hwaddr(uint8_t *hwaddr, const char *intf) for (ifa = ifas; ifa; ifa = ifa->ifa_next) { if (!strcmp(ifa->ifa_name, intf)) { - if (ifa->ifa_addr->sa_family != NMRPFLASH_AF_ETHERNET) { - continue; + if (sockaddr_get_hwaddr(ifa->ifa_addr, hwaddr)) { + found = true; + break; } -#ifdef NMRPFLASH_LINUX - src = ((struct sockaddr_ll*)ifa->ifa_addr)->sll_addr; -#else - src = LLADDR((struct sockaddr_dl*)ifa->ifa_addr); -#endif - memcpy(hwaddr, src, 6); - found = true; - break; } } freeifaddrs(ifas); return found; } + #else void win_perror2(const char *msg, DWORD err) @@ -109,7 +132,7 @@ void win_perror2(const char *msg, DWORD err) } } -static bool get_hwaddr(uint8_t *hwaddr, const char *intf) +static bool get_intf_info(const char *intf, uint8_t *hwaddr, DWORD *index) { PIP_ADAPTER_INFO adapters, adapter; DWORD ret; @@ -129,23 +152,18 @@ static bool get_hwaddr(uint8_t *hwaddr, const char *intf) if ((ret = GetAdaptersInfo(adapters, &bufLen) == NO_ERROR)) { for (adapter = adapters; adapter; adapter = adapter->Next) { - if (adapter->Type != MIB_IF_TYPE_ETHERNET) { + if (adapter->Type != MIB_IF_TYPE_ETHERNET && adapter->Type != IF_TYPE_IEEE80211) { continue; } -#ifndef NMRPFLASH_WINDOWS - if (!strcmp(intf, adapter->AdapterName)) -#else /* Interface names from WinPcap are "\Device\NPF_{GUID}", while * AdapterName from GetAdaptersInfo is just "{GUID}".*/ - if (strstr(intf, adapter->AdapterName)) -#endif - { + if (strstr(intf, adapter->AdapterName)) { if (adapter->AddressLength == 6) { - for (i = 0; i != 6; ++i) { - hwaddr[i] = adapter->Address[i]; + memcpy(hwaddr, adapter->Address, 6); + if (index) { + *index = adapter->Index; } - found = true; break; } @@ -240,7 +258,6 @@ static const char *intf_get_pretty_name(const char *intf) } #endif - inline uint8_t *ethsock_get_hwaddr(struct ethsock *sock) { return sock->hwaddr; @@ -268,7 +285,8 @@ struct ethsock *ethsock_create(const char *intf, uint16_t protocol) buf[0] = '\0'; - sock->pcap = pcap_open_live(intf, BUFSIZ, 1, 1, buf); + sock->intf = intf; + sock->pcap = pcap_open_live(sock->intf, BUFSIZ, 1, 1, buf); if (!sock->pcap) { fprintf(stderr, "%s.\n", buf); goto cleanup_malloc; @@ -284,21 +302,26 @@ struct ethsock *ethsock_create(const char *intf, uint16_t protocol) goto cleanup_pcap; } - if (!get_hwaddr(sock->hwaddr, intf)) { - fprintf(stderr, "Failed to get MAC address of interface.\n"); +#ifndef NMRPFLASH_WINDOWS + err = !get_intf_info(intf, sock->hwaddr, NULL); +#else + err = !get_intf_info(intf, sock->hwaddr, &sock->index); +#endif + if (err) { + fprintf(stderr, "Failed to get interface info.\n"); goto cleanup_malloc; } #ifndef NMRPFLASH_WINDOWS sock->fd = pcap_get_selectable_fd(sock->pcap); if (sock->fd == -1) { - fprintf(stderr, "No selectable file descriptor available.\n"); + pcap_perror(sock->pcap, "pcap_get_selectable_fd"); goto cleanup_pcap; } #else sock->handle = pcap_getevent(sock->pcap); if (!sock->handle) { - fprintf(stderr, "No event handle available.\n"); + pcap_perror(sock->pcap, "pcap_getevent"); goto cleanup_pcap; } @@ -431,13 +454,59 @@ inline int ethsock_set_timeout(struct ethsock *sock, unsigned msec) return 0; } -static bool is_ethernet(const pcap_if_t *dev) +#ifndef NMRPFLASH_WINDOWS +int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr) { - pcap_t *pcap; + return 0; +} + +int ethsock_arp_del(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr) +{ + return 0; +} +#else +static int ethsock_arp(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr, int add) +{ + DWORD ret; + MIB_IPNETROW arp = { + .dwIndex = sock->index, + .dwPhysAddrLen = 6, + .dwAddr = ipaddr->s_addr, + .dwType = MIB_IPNET_TYPE_STATIC + }; + + memcpy(arp.bPhysAddr, hwaddr, 6); + + if (add) { + ret = CreateIpNetEntry(&arp); + if (ret != NO_ERROR) { + win_perror2("CreateIpNetEntry", ret); + return -1; + } + } else { + DeleteIpNetEntry(&arp); + } + + return 0; +} + +int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr) +{ + ethsock_arp_del(sock, hwaddr, ipaddr); + return ethsock_arp(sock, hwaddr, ipaddr, 1); +} + +int ethsock_arp_del(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr) +{ + return ethsock_arp(sock, hwaddr, ipaddr, 0); +} +#endif + +static bool get_hwaddr_from_pcap(const pcap_if_t *dev, uint8_t *hwaddr) +{ +#ifndef NMRPFLASH_WINDOWS pcap_addr_t *addr; int i; - char errbuf[PCAP_ERRBUF_SIZE]; - bool ret = false; for (addr = dev->addresses; addr; addr = addr->next) { if (verbosity > 1) { @@ -449,19 +518,13 @@ static bool is_ethernet(const pcap_if_t *dev) printf("}\n"); } - if (addr->addr->sa_family == NMRPFLASH_AF_ETHERNET) { + if (sockaddr_get_hwaddr(addr->addr, hwaddr)) { return true; } } +#endif - if ((pcap = pcap_create(dev->name, errbuf))) { - if (pcap_activate(pcap) == 0) { - ret = (pcap_datalink(pcap) == DLT_EN10MB); - } - pcap_close(pcap); - } - - return ret; + return get_intf_info(dev->name, hwaddr, NULL); } int ethsock_list_all(void) @@ -488,7 +551,7 @@ int ethsock_list_all(void) continue; } - if (!is_ethernet(dev)) { + if (!get_hwaddr_from_pcap(dev, hwaddr)) { if (verbosity) { printf("%-15s (not an ethernet device)\n", dev->name); @@ -496,14 +559,6 @@ int ethsock_list_all(void) continue; } - if (!get_hwaddr(hwaddr, dev->name)) { - if (verbosity) { - printf("%-15s (failed to get hardware address)\n", - dev->name); - } - continue; - } - #ifndef NMRPFLASH_WINDOWS printf("%-15s", dev->name); #else @@ -549,3 +604,185 @@ int ethsock_list_all(void) return 0; } + +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; +} + +#ifndef NMRPFLASH_WINDOWS +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; +} + +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) { + perror("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) { + perror("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)))) { + perror("malloc"); + return -1; + } + +#ifndef NMRPFLASH_WINDOWS + int ret = -1; + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (!fd) { + perror("socket"); + return -1; + } + +#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"); + + // XXX: undo is non-zero only if we're actually adding an ip + if (undo) { + set_addr(&ifr.ifr_addr, ipaddr); + if (ioctl(fd, SIOCSIFADDR, &ifr) != 0) { + perror("ioctl(SIOSIFADDR)"); + goto out; + } + + set_addr(&ifr.ifr_netmask, ipmask); + if (ioctl(fd, SIOCSIFNETMASK, &ifr) != 0) { + perror("ioctl(SIOCSIFNETMASK)"); + goto out; + } + + (*undo)->ip[0] = ipaddr; + (*undo)->ip[1] = ipmask; + } + + if (!set_interface_up(fd, ifr.ifr_name, undo ? true : false)) { + goto out; + } +#else // NMRPFLASH_OSX (or any other BSD) + struct ifaliasreq 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); + memset(&ifra.ifra_broadaddr, 0, sizeof(ifra.ifra_broadaddr)); + + // XXX: undo is non-zero only if we're actually adding an ip + + if (ioctl(fd, undo ? SIOCAIFADDR : SIOCDIFADDR, &ifra) != 0) { + perror("ioctl(SIOCAIFADDR)"); + goto out; + } + + if (undo) { + (*undo)->ip[0] = ipaddr; + (*undo)->ip[1] = ipmask; + set_interface_up(fd, ifra.ifra_name, true); + } + +#endif + ret = 0; + +out: + close(fd); + return ret; +#else // NMRPFLASH_WINDOWS + ULONG instance; + + (*undo)->context = 0; + + DWORD ret = AddIPAddress(ipaddr, ipmask, sock->index, &(*undo)->context, &instance); + if (ret != NO_ERROR && ret != ERROR_DUP_DOMAINNAME && ret != ERROR_OBJECT_ALREADY_EXISTS) { + win_perror2("AddIPAddress", ret); + return -1; + } + + return 0; +#endif +} + +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; +}