/* arping/src/findif_sysctl.c
*
* Copyright (C) 2000-2011 Thomas Habets <thomas@habets.se>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* This file should never be used. Systems that are chosen for sysctl()
* should always have getifaddrs() which is preferred to this.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include "arping.h"
#ifndef SALIGN
#define SALIGN (sizeof(int32_t) - 1)
#endif
#ifndef SA_SIZE
#define SA_SIZE(sa) ((sa)->sa_len \
? (((sa)->sa_len + SALIGN) & ~SALIGN)\
: (SALIGN + 1))
#endif
/**
*
*/
const char *
arping_lookupdev(uint32_t srcip,
uint32_t dstip,
char *ebuf)
{
int mib[6] = {
CTL_NET,
PF_ROUTE,
0, /* Protocol */
AF_INET, /* Address family */
NET_RT_IFLIST,
0
};
int c;
/* buffer */
char *buf, *lim;
size_t bufsize;
/* Matching interfaces */
int match_count = 0;
/* best match */
in_addr_t best_mask = 0;
struct in_addr best_addr;
/* Results */
static char ifName[IFNAMSIZ];
/* Allocate buffer and retrieve data. */
for (c = 0;;) {
if (sysctl(mib, 6, NULL, &bufsize, NULL, 0) < 0) {
strcpy(ebuf, "sysctl: get buffer size error");
goto failed;
}
if ((buf = malloc(bufsize)) == NULL) {
strcpy(ebuf, "malloc: error");
goto failed;
}
if (sysctl(mib, 6, buf, &bufsize, NULL, 0) == 0) {
break;
}
if (errno != ENOMEM || ++c >= 10 ) {
strcpy(ebuf, "sysctl: get ifaces error");
goto failed;
}
fprintf(stderr, "sysctl: buffer size changed");
free(buf);
}
lim = buf + bufsize;
/* Loop through all interfaces */
while (buf < lim) {
struct sockaddr_dl *sdl;
char tmpIfName[IFNAMSIZ];
int i;
struct if_msghdr *ifh = (struct if_msghdr *)buf;
if (ifh->ifm_type != RTM_IFINFO) {
strcpy(ebuf, "Wrong data in NET_RT_IFLIST");
return NULL;
}
sdl = (struct sockaddr_dl *)(buf +
sizeof(struct if_msghdr) -
sizeof(struct if_data) +
sizeof(struct if_data));
i = sdl->sdl_nlen < sizeof(ifName)
? sdl->sdl_nlen
: (sizeof(tmpIfName)-1);
memcpy(tmpIfName, sdl->sdl_data, i);
tmpIfName[i] = 0;
buf += ifh->ifm_msglen;
/* Loop through all addresses of interface. */
while (buf < lim) {
struct ifa_msghdr *ifht = (struct ifa_msghdr *)buf;
char* addrptr;
struct sockaddr_in *if_addr = NULL;
struct sockaddr_in *if_nmsk = NULL;
struct sockaddr_in *if_bcst = NULL;
in_addr_t mask;
if (ifht->ifam_type != RTM_NEWADDR) {
break;
}
addrptr = buf + sizeof(struct ifa_msghdr);
buf += ifht->ifam_msglen;
if (ifh->ifm_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) {
continue;
}
/* Loop through all the address attributes. */
for (c=1; c < (1<<RTAX_MAX); c<<=1) {
size_t len;
struct sockaddr_in *sa;
sa = addrptr;
switch (c & ifht->ifam_addrs) {
case 0:
continue;
case RTA_NETMASK:
if_nmsk = sa;
break;
case RTA_IFA:
if_addr = sa;
break;
case RTA_BRD:
if_bcst = sa;
break;
}
addrptr += SA_SIZE((struct sockaddr*)sa);
}
if (!if_addr || !if_nmsk || !if_bcst) {
continue;
}
if (if_addr->sin_family != AF_INET) {
continue;
}
if ((dstip & if_nmsk->sin_addr.s_addr)
!= (if_addr->sin_addr.s_addr
& if_nmsk->sin_addr.s_addr)) {
continue;
}
match_count++;
if (verbose) {
printf("Specified addr matches "
"interface '%s':\n", tmpIfName);
printf(" IP addr %s, ",
inet_ntoa(if_addr->sin_addr));
printf("mask %s, ",
inet_ntoa(if_nmsk->sin_addr));
printf("bcast %s\n",
inet_ntoa(if_bcst->sin_addr));
}
mask = ntohl(if_nmsk->sin_addr.s_addr);
if (mask > best_mask) {
memcpy(ifName,
tmpIfName,
sizeof(ifName));
best_mask = mask;
best_addr = if_addr->sin_addr;
}
}
}
if (match_count == 0 ) {
if (verbose) {
strcpy(ebuf,
"No interface found that matches specified IP");
}
goto failed;
}
if (verbose && match_count > 1) {
printf("Using interface '%s' with src IP %s due to longer "
"mask.\n", ifName, inet_ntoa(best_addr));
}
#if 0
if (ifce_ip != 0) {
*ifce_ip = best_addr.s_addr;
}
#endif
return ifName;
failed:
return NULL;
}
/* ---- Emacs Variables ----
* Local Variables:
* c-basic-offset: 8
* indent-tabs-mode: nil
* End:
*/
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>