Listen on all interfaces
Rather than requiring an interface to listen on on the command-line,
just listen on all interface, and use the sockaddr returned from
recvfrom() to determine which interface the inarp request came from.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/inarp.c b/inarp.c
index 31f7b51..212b10f 100644
--- a/inarp.c
+++ b/inarp.c
@@ -184,62 +184,154 @@
return 0;
}
-static void usage(const char *progname)
+static int init_interfaces(struct inarp_ctx *inarp)
{
- fprintf(stderr, "Usage: %s <interface>\n", progname);
+ int alloc_len, rc, retry = 3;
+ struct ifconf ifconf;
+ unsigned int i;
+
+ /* find the names of all interfaces */
+ for (;;) {
+ ifconf.ifc_len = 0;
+ ifconf.ifc_buf = NULL;
+ rc = ioctl(inarp->socket, SIOCGIFCONF, &ifconf);
+ if (rc < 0) {
+ warn("Error querying interface configuration size");
+ return -1;
+ }
+
+ /* the only way we can detect truncated ifconf data is by
+ * allocating for one extra ifreq, then seeing if it gets
+ * filled; if so, subsequent ones may have been dropped.
+ */
+ alloc_len = ifconf.ifc_len + sizeof(struct ifreq);
+
+ ifconf.ifc_len = alloc_len;
+ ifconf.ifc_buf = malloc(alloc_len);
+
+ rc = ioctl(inarp->socket, SIOCGIFCONF, &ifconf);
+ if (rc < 0) {
+ warn("Error quering interface configuration");
+ free(ifconf.ifc_buf);
+ return -1;
+ }
+
+ if (ifconf.ifc_len < alloc_len)
+ break;
+
+ if (retry--) {
+ /* we may have lost ifconf data, as our buffer was
+ * full; retry */
+ free(ifconf.ifc_buf);
+ continue;
+ }
+
+ warnx("Can't allocate data for interface configuration");
+ return -1;
+ }
+
+ /* populate interface data */
+ for (i = 0; i < ifconf.ifc_len / sizeof(struct ifreq); i++) {
+ struct ifreq *ifreq = &ifconf.ifc_req[i];
+ struct interface iface;
+
+ strncpy(iface.ifname, ifreq->ifr_name, IFNAMSIZ);
+
+ rc = get_ifindex(inarp->socket, iface.ifname, &iface.ifindex);
+ if (rc)
+ continue;
+
+ rc = get_local_hwaddr(inarp->socket, iface.ifname,
+ &iface.eth_addr);
+ if (rc)
+ continue;
+
+ inarp->n_interfaces++;
+ inarp->interfaces = realloc(inarp->interfaces,
+ sizeof(*inarp->interfaces) *
+ inarp->n_interfaces);
+ memcpy(&inarp->interfaces[inarp->n_interfaces - 1],
+ &iface, sizeof(iface));
+ }
+
+ free(ifconf.ifc_buf);
+
+ if (!inarp->n_interfaces) {
+ warnx("No interfaces defined");
+ return -1;
+ }
+
+ printf("%d interfaces found:\n", inarp->n_interfaces);
+ for (i = 0; i < inarp->n_interfaces; i++) {
+ struct interface *iface = &inarp->interfaces[i];
+ printf(" %d: %-*s [%s]\n", iface->ifindex,
+ IFNAMSIZ,
+ iface->ifname,
+ eth_mac_to_str(&iface->eth_addr));
+ }
+
+ return 0;
}
-int main(int argc, char **argv)
+static struct interface *find_interface_by_ifindex(struct inarp_ctx *inarp,
+ int ifindex)
+{
+ unsigned int i;
+
+ for (i = 0; i < inarp->n_interfaces; i++) {
+ struct interface *iface = &inarp->interfaces[i];
+ if (iface->ifindex == ifindex)
+ return iface;
+ }
+
+ return NULL;
+}
+
+int main(void)
{
struct arp_packet inarp_req;
struct in_addr local_ip;
- struct inarp_ctx inarp;
struct interface *iface;
+ struct sockaddr_ll addr;
+ struct inarp_ctx inarp;
+ socklen_t addrlen;
ssize_t len;
int ret;
- if (argc < 2) {
- usage(argv[0]);
- return EXIT_FAILURE;
- }
-
- if (strlen(argv[1]) > IFNAMSIZ)
- errx(EXIT_FAILURE, "Interface name '%s' is invalid",
- argv[1]);
-
memset(&inarp, 0, sizeof(inarp));
- /* prepare for a single interface */
- inarp.interfaces = calloc(1, sizeof(inarp.interfaces[0]));
- inarp.n_interfaces = 1;
- iface = &inarp.interfaces[0];
-
- strncpy(iface->ifname, argv[1], sizeof(iface->ifname));
-
inarp.socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (inarp.socket < 0)
err(EXIT_FAILURE, "Error opening ARP socket");
- ret = get_ifindex(inarp.socket, iface->ifname, &iface->ifindex);
+ ret = init_interfaces(&inarp);
if (ret)
exit(EXIT_FAILURE);
- ret = get_local_hwaddr(inarp.socket, iface->ifname, &iface->eth_addr);
- if (ret)
- exit(EXIT_FAILURE);
-
- printf("%s MAC address: %s\n", iface->ifname,
- eth_mac_to_str(&iface->eth_addr));
-
while (1) {
+ addrlen = sizeof(addr);
len = recvfrom(inarp.socket, &inarp_req, sizeof(inarp_req), 0,
- NULL, NULL);
+ (struct sockaddr *)&addr, &addrlen);
if (len <= 0) {
if (errno == EINTR)
continue;
err(EXIT_FAILURE, "Error recieving ARP packet");
}
+ /*
+ * struct sockaddr_ll allows for 8 bytes of hardware address;
+ * we only need ETH_ALEN for a full ethernet address.
+ */
+ if (addrlen < sizeof(addr) - (8 - ETH_ALEN))
+ continue;
+
+ if (addr.sll_family != AF_PACKET)
+ continue;
+
+ iface = find_interface_by_ifindex(&inarp, addr.sll_ifindex);
+ if (!iface)
+ continue;
+
/* Is this packet large enough for an inarp? */
if ((size_t)len < sizeof(inarp_req))
continue;
@@ -261,6 +353,7 @@
if (ret)
continue;
+ printf("local mac: %s\n", eth_mac_to_str(&iface->eth_addr));
printf("local ip: %s\n", inet_ntoa(local_ip));
send_arp_packet(inarp.socket, iface->ifindex,