Re-implement the proxy arp sockaddr_dl search routine (again).

It now works :-) and searches all interface aliases.
This commit is contained in:
Brian Somers 1998-01-23 21:37:27 +00:00
parent e97fdb2506
commit 813bfe4d88
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=32721

View File

@ -17,7 +17,7 @@
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id: arp.c,v 1.23 1998/01/19 22:34:20 brian Exp $
* $Id: arp.c,v 1.24 1998/01/21 12:52:14 brian Exp $
*
*/
@ -34,6 +34,7 @@
#include <netinet/in.h>
#include <net/if_types.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
@ -65,7 +66,7 @@
* and type ``make arp-test''.
*
*/
#define LogIsKept(x) 0
#define LogIsKept(x) 1
#define LogPrintf fprintf
#undef LogDEBUG
#define LogDEBUG stderr
@ -240,90 +241,104 @@ cifproxyarp(int unit, struct in_addr hisaddr)
* get_ether_addr - get the hardware address of an interface on the
* the same subnet as ipaddr.
*/
#define MAX_IFS 32
static int
get_ether_addr(int s, struct in_addr ipaddr, struct sockaddr_dl *hwaddr)
{
int idx;
const char *got;
char *sp, *ep, *cp, *wp;
struct ifreq ifrq;
struct in_addr addr, mask;
struct rt_msghdr *rtm;
struct sockaddr *sa_dst, *sa_gw;
struct sockaddr_dl *dl;
int mib[6], sa_len, skip, b;
size_t needed;
int mib[6];
idx = 1;
while (strcmp(got = Index2Nam(idx), "???")) {
strncpy(ifrq.ifr_name, got, sizeof ifrq.ifr_name - 1);
ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
if (ID0ioctl(s, SIOCGIFADDR, &ifrq) == 0 &&
ifrq.ifr_addr.sa_family == AF_INET) {
addr = ((struct sockaddr_in *)&ifrq.ifr_addr)->sin_addr;
if (ID0ioctl(s, SIOCGIFNETMASK, &ifrq) == 0) {
mask = ((struct sockaddr_in *)&ifrq.ifr_broadaddr)->sin_addr;
if ((ipaddr.s_addr & mask.s_addr) == (addr.s_addr & mask.s_addr))
break;
}
}
idx++;
}
if (!strcmp(got, "???"))
return 0;
LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", got);
char *buf, *ptr, *end;
struct if_msghdr *ifm;
struct ifa_msghdr *ifam;
struct sockaddr *sa;
struct sockaddr_dl *dl;
struct sockaddr_in *ifa, *mask;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = 0;
mib[4] = NET_RT_DUMP;
mib[4] = NET_RT_IFLIST;
mib[5] = 0;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
LogPrintf(LogERROR, "get_ether_addr: sysctl: estimate: %s\n",
strerror(errno));
LogPrintf(LogERROR, "Index2Nam: sysctl: estimate: %s\n", strerror(errno));
return 0;
}
if (needed < 0)
if ((buf = malloc(needed)) == NULL)
return 0;
if ((sp = malloc(needed)) == NULL)
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
free(buf);
return 0;
if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
LogPrintf(LogERROR, "ShowRoute: sysctl: getroute: %s\n", strerror(errno));
free(sp);
return (1);
}
ep = sp + needed;
end = buf + needed;
for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *) cp;
if (rtm->rtm_index == idx) {
wp = (char *)(rtm+1);
if (rtm->rtm_addrs & RTA_DST) {
sa_dst = (struct sockaddr *)wp;
wp += sa_dst->sa_len;
} else
sa_dst = NULL;
if (rtm->rtm_addrs & RTA_GATEWAY) {
sa_gw = (struct sockaddr *)wp;
if (sa_gw->sa_family == AF_LINK) {
dl = (struct sockaddr_dl *)wp;
if (!dl->sdl_nlen && !dl->sdl_alen && !dl->sdl_slen) {
memcpy(hwaddr, dl, dl->sdl_len);
free(sp);
return 1;
}
ptr = buf;
while (ptr < end) {
ifm = (struct if_msghdr *)ptr; /* On if_msghdr */
if (ifm->ifm_type != RTM_IFINFO)
break;
dl = (struct sockaddr_dl *)(ifm + 1); /* Single _dl at end */
skip = (ifm->ifm_flags & (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT |
IFF_NOARP | IFF_LOOPBACK)) != (IFF_UP | IFF_BROADCAST);
ptr += ifm->ifm_msglen; /* First ifa_msghdr */
while (ptr < end) {
ifam = (struct ifa_msghdr *)ptr; /* Next ifa_msghdr (alias) */
if (ifam->ifam_type != RTM_NEWADDR) /* finished ? */
break;
sa = (struct sockaddr *)(ifam+1); /* pile of sa's at end */
ptr += ifam->ifam_msglen;
if (skip || (ifam->ifam_addrs & (RTA_NETMASK|RTA_IFA)) !=
(RTA_NETMASK|RTA_IFA))
continue;
/* Found a candidate. Do the addresses match ? */
if (LogIsKept(LogDEBUG) &&
ptr == (char *)ifm + ifm->ifm_msglen + ifam->ifam_msglen)
LogPrintf(LogDEBUG, "%.*s interface is a candidate for proxy\n",
dl->sdl_nlen, dl->sdl_data);
b = 1;
while (b < (RTA_NETMASK|RTA_IFA) && sa < (struct sockaddr *)ptr) {
switch (b) {
case RTA_IFA:
ifa = (struct sockaddr_in *)sa;
break;
case RTA_NETMASK:
/*
* Careful here ! this sockaddr doesn't have sa_family set to
* AF_INET, and is only 8 bytes big ! I have no idea why !
*/
mask = (struct sockaddr_in *)sa;
break;
}
if (ifam->ifam_addrs & b) {
#define ALN sizeof(ifa->sin_addr.s_addr)
sa_len = sa->sa_len > 0 ? ((sa->sa_len-1)|(ALN-1))+1 : ALN;
sa = (struct sockaddr *)((char *)sa + sa_len);
}
b <<= 1;
}
if (LogIsKept(LogDEBUG)) {
char a[16];
strncpy(a, inet_ntoa(mask->sin_addr), sizeof a - 1);
a[sizeof a - 1] = '\0';
LogPrintf(LogDEBUG, "Check addr %s, mask %s\n",
inet_ntoa(ifa->sin_addr), a);
}
if (ifa->sin_family == AF_INET &&
(ifa->sin_addr.s_addr & mask->sin_addr.s_addr) ==
(ipaddr.s_addr & mask->sin_addr.s_addr)) {
LogPrintf(LogPHASE, "Found interface %.*s for proxy arp\n",
dl->sdl_alen, dl->sdl_data);
memcpy(hwaddr, dl, dl->sdl_len);
free(buf);
return 1;
}
}
}
free(sp);
free(buf);
return 0;
}