arp.c revision 31061
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 *
2031061Sbrian * $Id: arp.c,v 1.16 1997/10/26 01:02:03 brian Exp $
218857Srgrimes *
226735Samurai */
236735Samurai
246735Samurai/*
256735Samurai * TODO:
266735Samurai */
276735Samurai
286735Samurai#include <sys/types.h>
2930715Sbrian#include <sys/time.h>
306735Samurai#include <sys/socket.h>
316735Samurai#include <net/if.h>
3221264Swollman#include <net/if_var.h>
336735Samurai#include <net/route.h>
346735Samurai#include <net/if_dl.h>
356735Samurai#include <netinet/in.h>
366735Samurai#include <net/if_types.h>
376735Samurai#include <netinet/in_var.h>
386735Samurai#include <netinet/if_ether.h>
3930715Sbrian
4030715Sbrian#include <fcntl.h>
4130715Sbrian#include <stdio.h>
4230715Sbrian#include <string.h>
4330715Sbrian#include <sys/errno.h>
4430715Sbrian#include <sys/ioctl.h>
4530715Sbrian#include <sys/uio.h>
4630715Sbrian#include <unistd.h>
4730715Sbrian
4830715Sbrian#include "mbuf.h"
4929265Sbrian#include "log.h"
5031061Sbrian#include "id.h"
5130715Sbrian#include "arp.h"
526735Samurai
536735Samuraistatic int rtm_seq;
546735Samurai
5526516Sbrianstatic int get_ether_addr(int, u_long, struct sockaddr_dl *);
5613389Sphk
576735Samurai/*
586735Samurai * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
596735Samurai * if it exists.
606735Samurai */
616735Samurai#define SET_SA_FAMILY(addr, family)		\
6230715Sbrian    memset((char *) &(addr), '\0', sizeof(addr));	\
636735Samurai    addr.sa_family = (family); 			\
646735Samurai    addr.sa_len = sizeof(addr);
656735Samurai
666735Samurai
676735Samurai#if RTM_VERSION >= 3
686735Samurai
696735Samurai/*
706735Samurai * sifproxyarp - Make a proxy ARP entry for the peer.
716735Samurai */
726735Samuraistatic struct {
7328679Sbrian  struct rt_msghdr hdr;
7428679Sbrian  struct sockaddr_inarp dst;
7528679Sbrian  struct sockaddr_dl hwa;
7628679Sbrian  char extra[128];
7730715Sbrian} arpmsg;
786735Samurai
796735Samuraistatic int arpmsg_valid;
806735Samurai
816735Samuraiint
8228679Sbriansifproxyarp(int unit, u_long hisaddr)
836735Samurai{
8428679Sbrian  int routes;
856735Samurai
8628679Sbrian  /*
8728679Sbrian   * Get the hardware address of an interface on the same subnet as our local
8828679Sbrian   * address.
8928679Sbrian   */
9028679Sbrian  memset(&arpmsg, 0, sizeof(arpmsg));
9128679Sbrian  if (!get_ether_addr(unit, hisaddr, &arpmsg.hwa)) {
9228974Sbrian    LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n");
9328679Sbrian    return 0;
9428679Sbrian  }
9531061Sbrian  routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
9631061Sbrian  if (routes < 0) {
9728679Sbrian    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
9828679Sbrian	      strerror(errno));
9928679Sbrian    return 0;
10028679Sbrian  }
10128679Sbrian  arpmsg.hdr.rtm_type = RTM_ADD;
10228679Sbrian  arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
10328679Sbrian  arpmsg.hdr.rtm_version = RTM_VERSION;
10428679Sbrian  arpmsg.hdr.rtm_seq = ++rtm_seq;
10528679Sbrian  arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
10628679Sbrian  arpmsg.hdr.rtm_inits = RTV_EXPIRE;
10728679Sbrian  arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
10828679Sbrian  arpmsg.dst.sin_family = AF_INET;
10928679Sbrian  arpmsg.dst.sin_addr.s_addr = hisaddr;
11028679Sbrian  arpmsg.dst.sin_other = SIN_PROXY;
1116735Samurai
11228679Sbrian  arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
11328679Sbrian    + arpmsg.hwa.sdl_len;
11428679Sbrian  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
11528679Sbrian    LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno));
1166735Samurai    close(routes);
11728679Sbrian    return 0;
11828679Sbrian  }
11928679Sbrian  close(routes);
12028679Sbrian  arpmsg_valid = 1;
12128679Sbrian  return 1;
1226735Samurai}
1236735Samurai
1246735Samurai/*
1256735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer.
1266735Samurai */
1276735Samuraiint
12828679Sbriancifproxyarp(int unit, u_long hisaddr)
1296735Samurai{
13028679Sbrian  int routes;
1316735Samurai
13228679Sbrian  if (!arpmsg_valid)
13328679Sbrian    return 0;
13428679Sbrian  arpmsg_valid = 0;
1356735Samurai
13628679Sbrian  arpmsg.hdr.rtm_type = RTM_DELETE;
13728679Sbrian  arpmsg.hdr.rtm_seq = ++rtm_seq;
1386735Samurai
13931061Sbrian  routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
14031061Sbrian  if (routes < 0) {
14128679Sbrian    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
14228679Sbrian	      strerror(errno));
14328679Sbrian    return 0;
14428679Sbrian  }
14528679Sbrian  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
14628679Sbrian    LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno));
1476735Samurai    close(routes);
14828679Sbrian    return 0;
14928679Sbrian  }
15028679Sbrian  close(routes);
15128679Sbrian  return 1;
1526735Samurai}
1536735Samurai
15428679Sbrian#else				/* RTM_VERSION */
1556735Samurai
1566735Samurai/*
1576735Samurai * sifproxyarp - Make a proxy ARP entry for the peer.
1586735Samurai */
1596735Samuraiint
16028679Sbriansifproxyarp(int unit, u_long hisaddr)
1616735Samurai{
16228679Sbrian  struct arpreq arpreq;
16328679Sbrian  struct {
16428679Sbrian    struct sockaddr_dl sdl;
16528679Sbrian    char space[128];
16628679Sbrian  }      dls;
1676735Samurai
16830715Sbrian  memset(&arpreq, '\0', sizeof(arpreq));
1696735Samurai
17028679Sbrian  /*
17128679Sbrian   * Get the hardware address of an interface on the same subnet as our local
17228679Sbrian   * address.
17328679Sbrian   */
17428679Sbrian  if (!get_ether_addr(unit, hisaddr, &dls.sdl)) {
17528679Sbrian    LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n");
17628679Sbrian    return 0;
17728679Sbrian  }
17828679Sbrian  arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
17928679Sbrian  arpreq.arp_ha.sa_family = AF_UNSPEC;
18030715Sbrian  memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen);
18128679Sbrian  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
18228679Sbrian  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
18328679Sbrian  arpreq.arp_flags = ATF_PERM | ATF_PUBL;
18431061Sbrian  if (ID0ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) {
18528974Sbrian    LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno));
18628679Sbrian    return 0;
18728679Sbrian  }
18828679Sbrian  return 1;
1896735Samurai}
1906735Samurai
1916735Samurai/*
1926735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer.
1936735Samurai */
1946735Samuraiint
19528679Sbriancifproxyarp(int unit, u_long hisaddr)
1966735Samurai{
19728679Sbrian  struct arpreq arpreq;
1986735Samurai
19930715Sbrian  memset(&arpreq, '\0', sizeof(arpreq));
20028679Sbrian  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
20128679Sbrian  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
20231061Sbrian  if (ID0ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) {
20328974Sbrian    LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno));
20428679Sbrian    return 0;
20528679Sbrian  }
20628679Sbrian  return 1;
2076735Samurai}
2086735Samurai
20928679Sbrian#endif				/* RTM_VERSION */
2106735Samurai
21128679Sbrian
2126735Samurai/*
2136735Samurai * get_ether_addr - get the hardware address of an interface on the
2146735Samurai * the same subnet as ipaddr.
2156735Samurai */
2166735Samurai#define MAX_IFS		32
2176735Samurai
21830715Sbrianstatic int
21930715Sbrianget_ether_addr(int s, u_long ipaddr, struct sockaddr_dl *hwaddr)
2206735Samurai{
22128679Sbrian  struct ifreq *ifr, *ifend, *ifp;
22228679Sbrian  u_long ina, mask;
22328679Sbrian  struct sockaddr_dl *dla;
22428679Sbrian  struct ifreq ifreq;
22528679Sbrian  struct ifconf ifc;
22628679Sbrian  struct ifreq ifs[MAX_IFS];
2276735Samurai
22828679Sbrian  ifc.ifc_len = sizeof(ifs);
22928679Sbrian  ifc.ifc_req = ifs;
23028679Sbrian  if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
23128974Sbrian    LogPrintf(LogERROR, "get_ether_addr: ioctl(SIOCGIFCONF): %s\n",
23228974Sbrian	      strerror(errno));
23328679Sbrian    return 0;
23428679Sbrian  }
2356735Samurai
23628679Sbrian  /*
23728679Sbrian   * Scan through looking for an interface with an Internet address on the
23828679Sbrian   * same subnet as `ipaddr'.
23928679Sbrian   */
24028679Sbrian  ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
24128679Sbrian  for (ifr = ifc.ifc_req; ifr < ifend;) {
24228679Sbrian    if (ifr->ifr_addr.sa_family == AF_INET) {
24328679Sbrian      ina = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr.s_addr;
24428679Sbrian      strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
24528679Sbrian      ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';
2466735Samurai
24728679Sbrian      /*
24828679Sbrian       * Check that the interface is up, and not point-to-point or loopback.
24928679Sbrian       */
25028679Sbrian      if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
25128679Sbrian	continue;
25228679Sbrian      if ((ifreq.ifr_flags &
25328679Sbrian      (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP))
25428679Sbrian	  != (IFF_UP | IFF_BROADCAST))
25528679Sbrian	goto nextif;
25628679Sbrian
25728679Sbrian      /*
25828679Sbrian       * Get its netmask and check that it's on the right subnet.
25928679Sbrian       */
26028679Sbrian      if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
26128679Sbrian	continue;
26228679Sbrian      mask = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr.s_addr;
26328679Sbrian      if ((ipaddr & mask) != (ina & mask))
26428679Sbrian	goto nextif;
26528679Sbrian
26628679Sbrian      break;
26728679Sbrian    }
2686735Samurainextif:
26928679Sbrian    ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len);
27028679Sbrian  }
2716735Samurai
27228679Sbrian  if (ifr >= ifend)
27328679Sbrian    return 0;
27428679Sbrian  LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", ifr->ifr_name);
2756735Samurai
27628679Sbrian  /*
27728679Sbrian   * Now scan through again looking for a link-level address for this
27828679Sbrian   * interface.
27928679Sbrian   */
28028679Sbrian  ifp = ifr;
28128679Sbrian  for (ifr = ifc.ifc_req; ifr < ifend;) {
28228679Sbrian    if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
28328679Sbrian	&& ifr->ifr_addr.sa_family == AF_LINK) {
28428679Sbrian
28528679Sbrian      /*
28628679Sbrian       * Found the link-level address - copy it out
28728679Sbrian       */
28828679Sbrian      dla = (struct sockaddr_dl *) & ifr->ifr_addr;
28930715Sbrian      memcpy(hwaddr, dla, dla->sdl_len);
29028679Sbrian      return 1;
2916735Samurai    }
29228679Sbrian    ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len);
29328679Sbrian  }
2946735Samurai
29528679Sbrian  return 0;
2966735Samurai}
2976735Samurai
2986735Samurai
29930715Sbrian#ifdef DEBUG
3006735Samuraiint
3016735Samuraimain()
3026735Samurai{
30328679Sbrian  u_long ipaddr;
30428679Sbrian  int s;
3056735Samurai
30628679Sbrian  s = socket(AF_INET, SOCK_DGRAM, 0);
30728679Sbrian  ipaddr = inet_addr("192.168.1.32");
30828679Sbrian  sifproxyarp(s, ipaddr);
30928679Sbrian  close(s);
3106735Samurai}
3116735Samurai#endif
312