#define WPCAP
#include <pcap.h>
#else
-#include <pcap.h>
+#include <sys/ioctl.h>
#include <ifaddrs.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <pcap.h>
#if defined(NMRPFLASH_LINUX)
#define NMRPFLASH_AF_PACKET AF_PACKET
#include <linux/if_packet.h>
#endif
#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];
}
#ifndef NMRPFLASH_LINUX
- if (((struct sockaddr_dl*)addr)->sdl_type != IFT_ETHER) {
+ if (((struct sockaddr_dl*)sa)->sdl_type != IFT_ETHER) {
return false;
}
src = LLADDR((struct sockaddr_dl*)sa);
return true;
}
-static bool get_hwaddr_from_intf(const char *intf, uint8_t *hwaddr)
+static bool get_intf_info(const char *intf, uint8_t *hwaddr, void *dummy)
{
struct ifaddrs *ifas, *ifa;
bool found;
}
}
-static bool get_hwaddr_from_intf(const char *intf, uint8_t *hwaddr)
+static bool get_intf_info(const char *intf, uint8_t *hwaddr, DWORD *index)
{
PIP_ADAPTER_INFO adapters, adapter;
DWORD ret;
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;
}
* AdapterName from GetAdaptersInfo is just "{GUID}".*/
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;
}
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;
goto cleanup_pcap;
}
- if (!get_hwaddr_from_intf(intf, sock->hwaddr)) {
- 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;
}
return 0;
}
+#ifndef NMRPFLASH_WINDOWS
+int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, struct in_addr *ipaddr)
+{
+ 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
}
#endif
- return get_hwaddr_from_intf(dev->name, hwaddr);
+ return get_intf_info(dev->name, hwaddr, NULL);
}
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;
+}