#include #include #include #include "nmrpd.h" #ifndef PACKED #define PACKED __attribute__((packed)) #endif #ifndef ETH_P_ARP #define ETH_P_ARP 0x0806 #endif #define REQUEST_COUNT 256 struct arp { uint16_t htype; uint16_t ptype; uint8_t hlen; uint8_t plen; uint16_t oper; uint8_t sha[6]; uint32_t spa; uint8_t tha[6]; uint32_t tpa; } PACKED; struct arppkt { struct eth_hdr eth; struct arp arp; uint8_t padding[18]; } PACKED; static bool is_arp(void *pktbuf, size_t len) { if (len < 28) { return false; } len -= 14; pktbuf += 14; struct arp *pkt = pktbuf; return ntohs(pkt->htype) == 1 && ntohs(pkt->ptype) == 0x0800 && pkt->hlen == 6 && pkt->plen == 4; } static bool is_reply(void *pktbuf, size_t len, struct ethsock *sock) { struct arppkt *pkt = pktbuf; return is_arp(pktbuf, len) && htons(pkt->arp.oper) == 2 && !memcmp(ethsock_get_hwaddr(sock), pkt->arp.tha, 6); } static const char *u32toa(uint32_t u32) { struct in_addr addr = { .s_addr = u32 }; return inet_ntoa(addr); } static int ip_callback(struct ethsock_ip_callback_args *args) { uint32_t *ip = args->arg; ip[0] = args->ipaddr->s_addr; ip[1] = args->ipmask->s_addr; return 0; } static void init_request(struct arppkt *pkt, struct ethsock *sock, uint32_t spa, uint32_t tpa) { memcpy(pkt->eth.ether_shost, ethsock_get_hwaddr(sock), 6); memset(pkt->eth.ether_dhost, 0xff, 6); pkt->eth.ether_type = htons(0x0806); memset(pkt->padding, 0, sizeof(pkt->padding)); pkt->arp.htype = htons(1); pkt->arp.ptype = htons(0x0800); pkt->arp.hlen = 6; pkt->arp.plen = 4; pkt->arp.oper = htons(1); memcpy(pkt->arp.sha, ethsock_get_hwaddr(sock), 6); pkt->arp.spa = htonl(spa); memset(pkt->arp.tha, 0xff, 6); pkt->arp.tpa = htonl(tpa); } int arp_find_free_ip(const char *intf, uint32_t *addr) { struct arppkt pkt; uint32_t srcip[2]; struct ethsock *arpsock = NULL; uint32_t min, max, ip; int i, timeouts; bool replies[REQUEST_COUNT] = { 0 }; int ret = -1; arpsock = ethsock_create(intf, ETH_P_ARP); if (!arpsock) { return -1; } if (ethsock_set_timeout(arpsock, 1000) != 0) { goto out; } if (ethsock_for_each_ip(arpsock, &ip_callback, srcip) != 0) { goto out; } printf("IP is %s/", u32toa(srcip[0])); printf("%s", u32toa(srcip[1])); srcip[0] = ntohl(srcip[0]); srcip[1] = ntohl(srcip[1]); printf(" aka 0x%08x/0x%08x\n", srcip[0], srcip[1]); if (~srcip[1]) { min = srcip[0] & srcip[1]; // highest possible address, minus 1 (e.g. for 192.168.0.1/24, // set value to 192.168.0.254) max = min | (~srcip[1] - 1); ip = max; if (verbosity) { printf("ARPinging range %s-", u32toa(htonl(min))); printf("%s\n", u32toa(htonl(max))); } for (i = 0; i < REQUEST_COUNT && ip > min; --ip, ++i) { if (ip == srcip[0] || replies[i]) { continue; } init_request(&pkt, arpsock, srcip[0], ip); if (ethsock_send(arpsock, &pkt, sizeof(pkt)) != 0) { goto out; } } min = ip; timeouts = 0; while (1) { ssize_t bytes = ethsock_recv(arpsock, &pkt, sizeof(pkt)); if (bytes < 0) { goto out; } else if (!bytes) { if (++timeouts >= 5) { break; } continue; } timeouts = 0; if (!is_reply(&pkt, sizeof(pkt), arpsock)) { continue; } uint32_t spa = ntohl(pkt.arp.spa); if (spa > min && spa <= max) { replies[spa - min] = true; if (verbosity > 1) { printf("Got ARP reply for %s from %s.\n", u32toa(pkt.arp.spa), mac_to_str(pkt.arp.sha)); } } else if (verbosity > 1) { printf("Got unexpected ARP reply for %s (min=", u32toa(pkt.arp.spa)); printf("%s, max=", u32toa(htonl(min))); printf("%s)\n", u32toa(htonl(max))); } } for (; i; --i) { if (!replies[i - 1]) { *addr = htonl(min + i); printf("Found free address %s.\n", u32toa(*addr)); ret = 0; break; } } } out: ethsock_close(arpsock); if (ret != 0) { fprintf(stderr, "Failed to find free ip address on %s\n", intf); } return ret; }