arp.c revision 28679
16735Samurai/*
26735Samurai * sys-bsd.c - System-dependent procedures for setting up
36735Samurai * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
46735Samurai *
56735Samurai * Copyright (c) 1989 Carnegie Mellon University.
66735Samurai * All rights reserved.
76735Samurai *
86735Samurai * Redistribution and use in source and binary forms are permitted
96735Samurai * provided that the above copyright notice and this paragraph are
106735Samurai * duplicated in all such forms and that any documentation,
116735Samurai * advertising materials, and other materials related to such
126735Samurai * distribution and use acknowledge that the software was developed
136735Samurai * by Carnegie Mellon University.  The name of the
146735Samurai * University may not be used to endorse or promote products derived
156735Samurai * from this software without specific prior written permission.
166735Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
176735Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
186735Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
198857Srgrimes *
2028679Sbrian * $Id: arp.c,v 1.12 1997/06/09 03:27:11 brian Exp $
218857Srgrimes *
226735Samurai */
236735Samurai
246735Samurai/*
256735Samurai * TODO:
266735Samurai */
276735Samurai
286735Samurai#include <sys/ioctl.h>
296735Samurai#include <sys/types.h>
3013379Sphk#include <sys/uio.h>
316735Samurai#include <sys/socket.h>
326735Samurai#include <sys/time.h>
336735Samurai#include <sys/errno.h>
3413379Sphk#include <unistd.h>
3513379Sphk#include <string.h>
366735Samurai
376735Samurai#include <net/if.h>
3821264Swollman#include <net/if_var.h>
396735Samurai#include <net/route.h>
406735Samurai#include <net/if_dl.h>
416735Samurai#include <netinet/in.h>
426735Samurai#include <stdio.h>
436735Samurai#include <fcntl.h>
446735Samurai#ifdef __bsdi__
456735Samurai#include <kvm.h>
466735Samurai#endif
476735Samurai#include <net/if_types.h>
486735Samurai#include <netinet/in_var.h>
496735Samurai#include <netinet/if_ether.h>
506735Samurai#include "log.h"
516735Samurai
526735Samurai#if RTM_VERSION >= 3
536735Samurai#include <netinet/if_ether.h>
546735Samurai#endif
556735Samurai
566735Samuraistatic int rtm_seq;
576735Samurai
5826516Sbrianstatic int get_ether_addr(int, u_long, struct sockaddr_dl *);
5913389Sphk
606735Samurai#define BCOPY(s, d, l)		memcpy(d, s, l)
616735Samurai#define BZERO(s, n)		memset(s, 0, n)
626735Samurai/*
636735Samurai * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
646735Samurai * if it exists.
656735Samurai */
666735Samurai#define SET_SA_FAMILY(addr, family)		\
676735Samurai    BZERO((char *) &(addr), sizeof(addr));	\
686735Samurai    addr.sa_family = (family); 			\
696735Samurai    addr.sa_len = sizeof(addr);
706735Samurai
716735Samurai
726735Samurai#if RTM_VERSION >= 3
736735Samurai
746735Samurai/*
756735Samurai * sifproxyarp - Make a proxy ARP entry for the peer.
766735Samurai */
776735Samuraistatic struct {
7828679Sbrian  struct rt_msghdr hdr;
7928679Sbrian  struct sockaddr_inarp dst;
8028679Sbrian  struct sockaddr_dl hwa;
8128679Sbrian  char extra[128];
8228679Sbrian}      arpmsg;
836735Samurai
846735Samuraistatic int arpmsg_valid;
856735Samurai
866735Samuraiint
8728679Sbriansifproxyarp(int unit, u_long hisaddr)
886735Samurai{
8928679Sbrian  int routes;
906735Samurai
9128679Sbrian  /*
9228679Sbrian   * Get the hardware address of an interface on the same subnet as our local
9328679Sbrian   * address.
9428679Sbrian   */
9528679Sbrian  memset(&arpmsg, 0, sizeof(arpmsg));
9628679Sbrian  if (!get_ether_addr(unit, hisaddr, &arpmsg.hwa)) {
9728679Sbrian    LogPrintf(LogERROR, "Cannot determine ethernet address"
9828679Sbrian	      " for proxy ARP\n");
9928679Sbrian    return 0;
10028679Sbrian  }
10128679Sbrian  if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
10228679Sbrian    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
10328679Sbrian	      strerror(errno));
10428679Sbrian    return 0;
10528679Sbrian  }
10628679Sbrian  arpmsg.hdr.rtm_type = RTM_ADD;
10728679Sbrian  arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
10828679Sbrian  arpmsg.hdr.rtm_version = RTM_VERSION;
10928679Sbrian  arpmsg.hdr.rtm_seq = ++rtm_seq;
11028679Sbrian  arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
11128679Sbrian  arpmsg.hdr.rtm_inits = RTV_EXPIRE;
11228679Sbrian  arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
11328679Sbrian  arpmsg.dst.sin_family = AF_INET;
11428679Sbrian  arpmsg.dst.sin_addr.s_addr = hisaddr;
11528679Sbrian  arpmsg.dst.sin_other = SIN_PROXY;
1166735Samurai
11728679Sbrian  arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
11828679Sbrian    + arpmsg.hwa.sdl_len;
11928679Sbrian  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
12028679Sbrian    LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno));
1216735Samurai    close(routes);
12228679Sbrian    return 0;
12328679Sbrian  }
12428679Sbrian  close(routes);
12528679Sbrian  arpmsg_valid = 1;
12628679Sbrian  return 1;
1276735Samurai}
1286735Samurai
1296735Samurai/*
1306735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer.
1316735Samurai */
1326735Samuraiint
13328679Sbriancifproxyarp(int unit, u_long hisaddr)
1346735Samurai{
13528679Sbrian  int routes;
1366735Samurai
13728679Sbrian  if (!arpmsg_valid)
13828679Sbrian    return 0;
13928679Sbrian  arpmsg_valid = 0;
1406735Samurai
14128679Sbrian  arpmsg.hdr.rtm_type = RTM_DELETE;
14228679Sbrian  arpmsg.hdr.rtm_seq = ++rtm_seq;
1436735Samurai
14428679Sbrian  if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
14528679Sbrian    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
14628679Sbrian	      strerror(errno));
14728679Sbrian    return 0;
14828679Sbrian  }
14928679Sbrian  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
15028679Sbrian    LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno));
1516735Samurai    close(routes);
15228679Sbrian    return 0;
15328679Sbrian  }
15428679Sbrian  close(routes);
15528679Sbrian  return 1;
1566735Samurai}
1576735Samurai
15828679Sbrian#else				/* RTM_VERSION */
1596735Samurai
1606735Samurai/*
1616735Samurai * sifproxyarp - Make a proxy ARP entry for the peer.
1626735Samurai */
1636735Samuraiint
16428679Sbriansifproxyarp(int unit, u_long hisaddr)
1656735Samurai{
16628679Sbrian  struct arpreq arpreq;
16728679Sbrian  struct {
16828679Sbrian    struct sockaddr_dl sdl;
16928679Sbrian    char space[128];
17028679Sbrian  }      dls;
1716735Samurai
17228679Sbrian  BZERO(&arpreq, sizeof(arpreq));
1736735Samurai
17428679Sbrian  /*
17528679Sbrian   * Get the hardware address of an interface on the same subnet as our local
17628679Sbrian   * address.
17728679Sbrian   */
17828679Sbrian  if (!get_ether_addr(unit, hisaddr, &dls.sdl)) {
17928679Sbrian    LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n");
18028679Sbrian    return 0;
18128679Sbrian  }
18228679Sbrian  arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
18328679Sbrian  arpreq.arp_ha.sa_family = AF_UNSPEC;
18428679Sbrian  BCOPY(LLADDR(&dls.sdl), arpreq.arp_ha.sa_data, dls.sdl.sdl_alen);
18528679Sbrian  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
18628679Sbrian  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
18728679Sbrian  arpreq.arp_flags = ATF_PERM | ATF_PUBL;
18828679Sbrian  if (ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) {
18928679Sbrian    LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): \n");
19028679Sbrian    return 0;
19128679Sbrian  }
19228679Sbrian  return 1;
1936735Samurai}
1946735Samurai
1956735Samurai/*
1966735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer.
1976735Samurai */
1986735Samuraiint
19928679Sbriancifproxyarp(int unit, u_long hisaddr)
2006735Samurai{
20128679Sbrian  struct arpreq arpreq;
2026735Samurai
20328679Sbrian  BZERO(&arpreq, sizeof(arpreq));
20428679Sbrian  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
20528679Sbrian  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
20628679Sbrian  if (ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) {
20728679Sbrian    LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): \n");
20828679Sbrian    return 0;
20928679Sbrian  }
21028679Sbrian  return 1;
2116735Samurai}
2126735Samurai
21328679Sbrian#endif				/* RTM_VERSION */
2146735Samurai
21528679Sbrian
2166735Samurai/*
2176735Samurai * get_ether_addr - get the hardware address of an interface on the
2186735Samurai * the same subnet as ipaddr.
2196735Samurai */
2206735Samurai#define MAX_IFS		32
2216735Samurai
2226735Samuraiint
22328679Sbrianget_ether_addr(int s, u_long ipaddr, struct sockaddr_dl * hwaddr)
2246735Samurai{
22528679Sbrian  struct ifreq *ifr, *ifend, *ifp;
22628679Sbrian  u_long ina, mask;
22728679Sbrian  struct sockaddr_dl *dla;
22828679Sbrian  struct ifreq ifreq;
22928679Sbrian  struct ifconf ifc;
23028679Sbrian  struct ifreq ifs[MAX_IFS];
2316735Samurai
23228679Sbrian  ifc.ifc_len = sizeof(ifs);
23328679Sbrian  ifc.ifc_req = ifs;
23428679Sbrian  if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
23528679Sbrian    LogPrintf(LogERROR, "get_ether_addr: ioctl(SIOCGIFCONF): \n");
23628679Sbrian    return 0;
23728679Sbrian  }
2386735Samurai
23928679Sbrian  /*
24028679Sbrian   * Scan through looking for an interface with an Internet address on the
24128679Sbrian   * same subnet as `ipaddr'.
24228679Sbrian   */
24328679Sbrian  ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
24428679Sbrian  for (ifr = ifc.ifc_req; ifr < ifend;) {
24528679Sbrian    if (ifr->ifr_addr.sa_family == AF_INET) {
24628679Sbrian      ina = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr.s_addr;
24728679Sbrian      strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
24828679Sbrian      ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';
2496735Samurai
25028679Sbrian      /*
25128679Sbrian       * Check that the interface is up, and not point-to-point or loopback.
25228679Sbrian       */
25328679Sbrian      if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
25428679Sbrian	continue;
25528679Sbrian      if ((ifreq.ifr_flags &
25628679Sbrian      (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP))
25728679Sbrian	  != (IFF_UP | IFF_BROADCAST))
25828679Sbrian	goto nextif;
25928679Sbrian
26028679Sbrian      /*
26128679Sbrian       * Get its netmask and check that it's on the right subnet.
26228679Sbrian       */
26328679Sbrian      if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
26428679Sbrian	continue;
26528679Sbrian      mask = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr.s_addr;
26628679Sbrian      if ((ipaddr & mask) != (ina & mask))
26728679Sbrian	goto nextif;
26828679Sbrian
26928679Sbrian      break;
27028679Sbrian    }
2716735Samurainextif:
27228679Sbrian    ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len);
27328679Sbrian  }
2746735Samurai
27528679Sbrian  if (ifr >= ifend)
27628679Sbrian    return 0;
27728679Sbrian  LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", ifr->ifr_name);
2786735Samurai
27928679Sbrian  /*
28028679Sbrian   * Now scan through again looking for a link-level address for this
28128679Sbrian   * interface.
28228679Sbrian   */
28328679Sbrian  ifp = ifr;
28428679Sbrian  for (ifr = ifc.ifc_req; ifr < ifend;) {
28528679Sbrian    if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
28628679Sbrian	&& ifr->ifr_addr.sa_family == AF_LINK) {
28728679Sbrian
28828679Sbrian      /*
28928679Sbrian       * Found the link-level address - copy it out
29028679Sbrian       */
29128679Sbrian      dla = (struct sockaddr_dl *) & ifr->ifr_addr;
2926735Samurai#ifdef __bsdi__
29328679Sbrian      if (dla->sdl_alen == 0)
29428679Sbrian	kmemgetether(ifr->ifr_name, dla);
2956735Samurai#endif
29628679Sbrian      BCOPY(dla, hwaddr, dla->sdl_len);
29728679Sbrian      return 1;
2986735Samurai    }
29928679Sbrian    ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len);
30028679Sbrian  }
3016735Samurai
30228679Sbrian  return 0;
3036735Samurai}
3046735Samurai
3056735Samurai#ifdef __bsdi__
3066735Samurai#include <nlist.h>
3076735Samurai
3086735Samuraistruct nlist nl[] = {
3096735Samurai#define N_IFNET		0
31028679Sbrian  {"_ifnet"},
31128679Sbrian  "",
3126735Samurai};
3136735Samurai
3146735Samurai
3156735Samuraikvm_t *kvmd;
3166735Samurai
3178857Srgrimes/*
3186735Samurai * Read kernel memory, return 0 on success.
3198857Srgrimes */
3206735Samuraiint
32128679Sbriankread(u_long addr, char *buf, int size)
3228857Srgrimes{
32326516Sbrian  if (kvm_read(kvmd, addr, buf, size) != size) {
32426516Sbrian    /* XXX this duplicates kvm_read's error printout */
32526516Sbrian    LogPrintf(LogERROR, "kvm_read %s\n", kvm_geterr(kvmd));
32626516Sbrian    return -1;
32726516Sbrian  }
32826516Sbrian  return 0;
3296735Samurai}
3306735Samurai
33128679Sbrianvoid
33228679Sbriankmemgetether(char *ifname, struct sockaddr_dl * dlo)
3336735Samurai{
3346735Samurai  struct ifnet ifnet;
3356735Samurai  int n;
3366735Samurai  u_long addr, ifaddraddr, ifnetfound, ifaddrfound;
33728679Sbrian  char name[16 + 32];
3386735Samurai  struct sockaddr *sa;
3396735Samurai  char *cp;
3406735Samurai  struct sockaddr_dl *sdl;
3418857Srgrimes  union {
34228679Sbrian    struct ifaddr ifa;
34328679Sbrian    struct in_ifaddr in;
34428679Sbrian  }     ifaddr;
3456735Samurai  struct arpcom ac;
3466735Samurai
3476735Samurai  kvmd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
3486735Samurai  if (kvmd) {
3496735Samurai    n = kvm_nlist(kvmd, nl);
3506735Samurai    if (n >= 0) {
3516735Samurai      addr = nl[N_IFNET].n_value;
35228679Sbrian      kread(addr, (char *) &addr, sizeof(addr));
3536735Samurai      ifaddraddr = ifnetfound = 0;
3546735Samurai      while (addr || ifaddraddr) {
3556735Samurai	ifnetfound = addr;
3566735Samurai	if (ifaddraddr == 0) {
35728679Sbrian	  if (kread(addr, (char *) &ifnet, sizeof(ifnet)) ||
35828679Sbrian	      kread((u_long) ifnet.if_name, name, 16))
35928679Sbrian	    return;
3606735Samurai	  name[15] = 0;
3616735Samurai	  addr = (u_long) ifnet.if_next;
36228679Sbrian	  cp = (char *) index(name, '\0');
3636735Samurai	  cp += sprintf(cp, "%d", ifnet.if_unit);
3646735Samurai	  *cp = '\0';
36528679Sbrian	  ifaddraddr = (u_long) ifnet.if_addrlist;
3666735Samurai	}
3676735Samurai	ifaddrfound = ifaddraddr;
3686735Samurai	if (ifaddraddr) {
36928679Sbrian	  if (kread(ifaddraddr, (char *) &ifaddr, sizeof ifaddr)) {
3706735Samurai	    ifaddraddr = 0;
3716735Samurai	    continue;
3726735Samurai	  }
3736735Samurai#define CP(x) ((char *)(x))
3746735Samurai	  cp = (CP(ifaddr.ifa.ifa_addr) - CP(ifaddraddr)) + CP(&ifaddr);
37528679Sbrian	  sa = (struct sockaddr *) cp;
3766735Samurai	  if (sa->sa_family == AF_LINK && strcmp(ifname, name) == 0) {
37728679Sbrian	    sdl = (struct sockaddr_dl *) sa;
37828679Sbrian	    cp = (char *) LLADDR(sdl);
3796735Samurai	    n = sdl->sdl_alen;
3806735Samurai	    if (ifnet.if_type == IFT_ETHER) {
38128679Sbrian	      if (n == 0) {
38228679Sbrian		kread(ifnetfound, (char *) &ac, sizeof(ac));
38328679Sbrian		cp = (char *) LLADDR(sdl);
38428679Sbrian		bcopy((char *) ac.ac_enaddr, cp, 6);
38528679Sbrian		sdl->sdl_alen = 6;
38628679Sbrian	      }
38728679Sbrian	      bcopy(sdl, dlo, sizeof(*sdl));
38828679Sbrian	      return;
3896735Samurai	    }
3906735Samurai	  }
39128679Sbrian	  ifaddraddr = (u_long) ifaddr.ifa.ifa_next;
3926735Samurai	}
3936735Samurai      }
3946735Samurai    }
3956735Samurai  }
3966735Samurai}
39728679Sbrian
3986735Samurai#endif
3996735Samurai
4006735Samurai#ifdef DEBUG
4016735Samuraimain()
4026735Samurai{
40328679Sbrian  u_long ipaddr;
40428679Sbrian  int s;
4056735Samurai
40628679Sbrian  s = socket(AF_INET, SOCK_DGRAM, 0);
40728679Sbrian  ipaddr = inet_addr("192.168.1.32");
40828679Sbrian  sifproxyarp(s, ipaddr);
40928679Sbrian  close(s);
4106735Samurai}
41128679Sbrian
4126735Samurai#endif
413