arp.c revision 32616
150276Speter/*
2174993Srafan * sys-bsd.c - System-dependent procedures for setting up
350276Speter * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
450276Speter *
550276Speter * Copyright (c) 1989 Carnegie Mellon University.
650276Speter * All rights reserved.
750276Speter *
850276Speter * Redistribution and use in source and binary forms are permitted
950276Speter * provided that the above copyright notice and this paragraph are
1050276Speter * duplicated in all such forms and that any documentation,
1150276Speter * advertising materials, and other materials related to such
1250276Speter * distribution and use acknowledge that the software was developed
1350276Speter * by Carnegie Mellon University.  The name of the
1450276Speter * University may not be used to endorse or promote products derived
1550276Speter * from this software without specific prior written permission.
1650276Speter * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1750276Speter * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1850276Speter * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1950276Speter *
2050276Speter * $Id: arp.c,v 1.21 1998/01/11 04:02:57 brian Exp $
2150276Speter *
2250276Speter */
2350276Speter
2450276Speter/*
2550276Speter * TODO:
2650276Speter */
2750276Speter
2850276Speter#include <sys/types.h>
2950276Speter#include <sys/time.h>
3050276Speter#include <sys/socket.h>
3150276Speter#include <net/if.h>
3250276Speter#include <net/route.h>
3350276Speter#include <net/if_dl.h>
3450276Speter#include <netinet/in.h>
3550276Speter#include <net/if_types.h>
3650276Speter#include <netinet/if_ether.h>
3750276Speter
3850276Speter#include <fcntl.h>
3950276Speter#include <stdio.h>
4050276Speter#include <stdlib.h>
41174993Srafan#include <string.h>
4250276Speter#include <sys/errno.h>
4366963Speter#include <sys/ioctl.h>
4466963Speter#include <sys/sysctl.h>
4550276Speter#include <sys/uio.h>
4650276Speter#include <unistd.h>
4766963Speter
4850276Speter#include "command.h"
49174993Srafan#include "mbuf.h"
5050276Speter#include "log.h"
5150276Speter#include "id.h"
5250276Speter#include "route.h"
5350276Speter#include "arp.h"
5450276Speter
5566963Speter#ifdef DEBUG
5666963Speter/*
5766963Speter * To test the proxy arp stuff, put the following in your Makefile:
5866963Speter *
5966963Speter * arp-test: arp.c
6066963Speter * 	cp ${.CURDIR}/arp.c arp-test.c
6150276Speter * 	echo 'const char *' >>arp-test.c
6266963Speter * 	awk '/^Index2Nam/,/^}/' ${.CURDIR}/route.c >>arp-test.c
6366963Speter * 	cc -I${.CURDIR} -DDEBUG arp-test.c -o arp-test
6450276Speter *
6562449Speter * and type ``make arp-test''.
6662449Speter *
6750276Speter */
6862449Speter#define LogIsKept(x) 0
6950276Speter#define LogPrintf fprintf
7062449Speter#undef LogDEBUG
71166124Srafan#define LogDEBUG stderr
7262449Speter#undef LogERROR
7362449Speter#define LogERROR stderr
7462449Speter#undef LogPHASE
75166124Srafan#define LogPHASE stdout
7650276Speter#define ID0socket socket
7750276Speter#define ID0ioctl ioctl
7862449Speter#endif
7962449Speter
8050276Speterstatic int rtm_seq;
8166963Speter
82166124Srafanstatic int get_ether_addr(int, struct in_addr, struct sockaddr_dl *);
8350276Speter
8450276Speter/*
8566963Speter * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
8666963Speter * if it exists.
8766963Speter */
8866963Speter#define SET_SA_FAMILY(addr, family)		\
8966963Speter    memset((char *) &(addr), '\0', sizeof(addr));	\
9066963Speter    addr.sa_family = (family); 			\
9166963Speter    addr.sa_len = sizeof(addr);
9266963Speter
9366963Speter
9466963Speter#if RTM_VERSION >= 3
9566963Speter
9666963Speter/*
9766963Speter * sifproxyarp - Make a proxy ARP entry for the peer.
9866963Speter */
9966963Speterstatic struct {
10066963Speter  struct rt_msghdr hdr;
10176726Speter  struct sockaddr_inarp dst;
10266963Speter  struct sockaddr_dl hwa;
10366963Speter  char extra[128];
10466963Speter} arpmsg;
10566963Speter
10666963Speterstatic int arpmsg_valid;
10766963Speter
10866963Speterint
10966963Spetersifproxyarp(int unit, struct in_addr hisaddr)
11066963Speter{
11166963Speter  int routes;
11266963Speter
11366963Speter  /*
11466963Speter   * Get the hardware address of an interface on the same subnet as our local
11566963Speter   * address.
11666963Speter   */
11766963Speter  memset(&arpmsg, 0, sizeof arpmsg);
11866963Speter  if (!get_ether_addr(unit, hisaddr, &arpmsg.hwa)) {
11966963Speter    LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n");
12066963Speter    return 0;
12166963Speter  }
12266963Speter  routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
12366963Speter  if (routes < 0) {
12466963Speter    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
12566963Speter	      strerror(errno));
12666963Speter    return 0;
12766963Speter  }
12862449Speter  arpmsg.hdr.rtm_type = RTM_ADD;
129166124Srafan  arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
130166124Srafan  arpmsg.hdr.rtm_version = RTM_VERSION;
131166124Srafan  arpmsg.hdr.rtm_seq = ++rtm_seq;
132166124Srafan  arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
133166124Srafan  arpmsg.hdr.rtm_inits = RTV_EXPIRE;
134166124Srafan  arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
135166124Srafan  arpmsg.dst.sin_family = AF_INET;
136166124Srafan  arpmsg.dst.sin_addr.s_addr = hisaddr.s_addr;
137166124Srafan  arpmsg.dst.sin_other = SIN_PROXY;
138166124Srafan
139166124Srafan  arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
140166124Srafan    + arpmsg.hwa.sdl_len;
141166124Srafan  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
142166124Srafan    LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno));
143166124Srafan    close(routes);
144166124Srafan    return 0;
145166124Srafan  }
146166124Srafan  close(routes);
147166124Srafan  arpmsg_valid = 1;
14862449Speter  return 1;
14950276Speter}
15062449Speter
15162449Speter/*
15262449Speter * cifproxyarp - Delete the proxy ARP entry for the peer.
15366963Speter */
15462449Speterint
15550276Spetercifproxyarp(int unit, struct in_addr hisaddr)
15697049Speter{
15797049Speter  int routes;
15897049Speter
15966963Speter  if (!arpmsg_valid)
16062449Speter    return 0;
16162449Speter  arpmsg_valid = 0;
16250276Speter
16362449Speter  arpmsg.hdr.rtm_type = RTM_DELETE;
16450276Speter  arpmsg.hdr.rtm_seq = ++rtm_seq;
16566963Speter
16662449Speter  routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
16762449Speter  if (routes < 0) {
16862449Speter    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
16962449Speter	      strerror(errno));
17062449Speter    return 0;
17150276Speter  }
17266963Speter  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
17362449Speter    LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno));
17462449Speter    close(routes);
17562449Speter    return 0;
17662449Speter  }
17762449Speter  close(routes);
17850276Speter  return 1;
17997049Speter}
18062449Speter
181166124Srafan#else				/* RTM_VERSION */
18297049Speter
18397049Speter/*
18497049Speter * sifproxyarp - Make a proxy ARP entry for the peer.
18597049Speter */
18697049Speterint
187166124Srafansifproxyarp(int unit, struct in_addr hisaddr)
188166124Srafan{
18997049Speter  struct arpreq arpreq;
19097049Speter  struct {
19197049Speter    struct sockaddr_dl sdl;
19297049Speter    char space[128];
19397049Speter  }      dls;
19462449Speter
19562449Speter  memset(&arpreq, '\0', sizeof arpreq);
19662449Speter
19762449Speter  /*
19862449Speter   * Get the hardware address of an interface on the same subnet as our local
19962449Speter   * address.
20062449Speter   */
20162449Speter  if (!get_ether_addr(unit, hisaddr, &dls.sdl)) {
202166124Srafan    LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n");
20362449Speter    return 0;
20462449Speter  }
20562449Speter  arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
20650276Speter  arpreq.arp_ha.sa_family = AF_UNSPEC;
20762449Speter  memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen);
20862449Speter  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
20962449Speter  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr.s_addr;
21062449Speter  arpreq.arp_flags = ATF_PERM | ATF_PUBL;
21162449Speter  if (ID0ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) {
21262449Speter    LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno));
21362449Speter    return 0;
21462449Speter  }
21562449Speter  return 1;
21650276Speter}
21762449Speter
21862449Speter/*
21962449Speter * cifproxyarp - Delete the proxy ARP entry for the peer.
22062449Speter */
221166124Srafanint
22262449Spetercifproxyarp(int unit, struct in_addr hisaddr)
22362449Speter{
22462449Speter  struct arpreq arpreq;
22562449Speter
22662449Speter  memset(&arpreq, '\0', sizeof arpreq);
22750276Speter  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
22850276Speter  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr.s_addr;
22962449Speter  if (ID0ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) {
23062449Speter    LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno));
23150276Speter    return 0;
23266963Speter  }
23362449Speter  return 1;
23462449Speter}
235166124Srafan
23662449Speter#endif				/* RTM_VERSION */
23762449Speter
23862449Speter
23962449Speter/*
24062449Speter * get_ether_addr - get the hardware address of an interface on the
24162449Speter * the same subnet as ipaddr.
24262449Speter */
24362449Speter#define MAX_IFS		32
244166124Srafan
24562449Speterstatic int
24662449Speterget_ether_addr(int s, struct in_addr ipaddr, struct sockaddr_dl *hwaddr)
24762449Speter{
24862449Speter  int idx;
24962449Speter  const char *got;
25050276Speter  char *sp, *ep, *cp, *wp;
25162449Speter  struct ifreq ifrq;
25250276Speter  struct in_addr addr, mask;
25366963Speter  struct rt_msghdr *rtm;
25462449Speter  struct sockaddr *sa_dst, *sa_gw;
255166124Srafan  struct sockaddr_dl *dl;
256166124Srafan  size_t needed;
25750276Speter  int mib[6];
25862449Speter
25962449Speter  idx = 1;
26062449Speter  while (strcmp(got = Index2Nam(idx), "???")) {
26150276Speter    strncpy(ifrq.ifr_name, got, sizeof ifrq.ifr_name - 1);
26262449Speter    ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
26362449Speter    if (ID0ioctl(s, SIOCGIFADDR, &ifrq) == 0 &&
26462449Speter        ifrq.ifr_addr.sa_family == AF_INET) {
26562449Speter      addr = ((struct sockaddr_in *)&ifrq.ifr_addr)->sin_addr;
26666963Speter      if (ID0ioctl(s, SIOCGIFNETMASK, &ifrq) == 0) {
26762449Speter        mask = ((struct sockaddr_in *)&ifrq.ifr_broadaddr)->sin_addr;
26862449Speter        if ((ipaddr.s_addr & mask.s_addr) == (addr.s_addr & mask.s_addr))
26950276Speter          break;
27062449Speter      }
27162449Speter    }
27262449Speter    idx++;
27362449Speter  }
27462449Speter
27562449Speter  if (!strcmp(got, "???"))
27650276Speter    return 0;
27762449Speter
27862449Speter  LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", got);
27962449Speter
28062449Speter  mib[0] = CTL_NET;
28150276Speter  mib[1] = PF_ROUTE;
28262449Speter  mib[2] = 0;
28362449Speter  mib[3] = 0;
28462449Speter  mib[4] = NET_RT_DUMP;
28562449Speter  mib[5] = 0;
28662449Speter  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
28762449Speter    LogPrintf(LogERROR, "get_ether_addr: sysctl: estimate: %s\n",
28850276Speter              strerror(errno));
28950276Speter    return 0;
29062449Speter  }
291166124Srafan  if (needed < 0)
29262449Speter    return 0;
29362449Speter  if ((sp = malloc(needed)) == NULL)
294166124Srafan    return 0;
29562449Speter  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
296166124Srafan    LogPrintf(LogERROR, "ShowRoute: sysctl: getroute: %s\n", strerror(errno));
29766963Speter    free(sp);
29862449Speter    return (1);
29962449Speter  }
300166124Srafan  ep = sp + needed;
301166124Srafan
302166124Srafan  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
303166124Srafan    rtm = (struct rt_msghdr *) cp;
30450276Speter    if (rtm->rtm_index == idx) {
30562449Speter      wp = (char *)(rtm+1);
30662449Speter
30762449Speter      if (rtm->rtm_addrs & RTA_DST) {
30862449Speter        sa_dst = (struct sockaddr *)wp;
30950276Speter        wp += sa_dst->sa_len;
31062449Speter      } else
31166963Speter        sa_dst = NULL;
31266963Speter
31366963Speter      if (rtm->rtm_addrs & RTA_GATEWAY) {
31466963Speter        sa_gw = (struct sockaddr *)wp;
31566963Speter        if (sa_gw->sa_family == AF_LINK) {
31662449Speter          dl = (struct sockaddr_dl *)wp;
317166124Srafan          if (dl->sdl_alen && dl->sdl_type == IFT_ETHER) {
31866963Speter            memcpy(hwaddr, dl, dl->sdl_len);
31966963Speter            free(sp);
32066963Speter            return 1;
32150276Speter          }
32266963Speter        }
32366963Speter      }
324166124Srafan    }
32566963Speter  }
32666963Speter  free(sp);
327166124Srafan  return 0;
32866963Speter}
329174993Srafan
33066963Speter#ifdef DEBUG
331166124Srafanint
332166124Srafanmain(int argc, char **argv)
333166124Srafan{
334166124Srafan  struct in_addr ipaddr;
335166124Srafan  int s, f;
336166124Srafan
337166124Srafan  s = socket(AF_INET, SOCK_DGRAM, 0);
338166124Srafan  for (f = 1; f < argc; f++) {
339166124Srafan    if (inet_aton(argv[f], &ipaddr))
340166124Srafan      sifproxyarp(s, ipaddr);
341166124Srafan  }
342166124Srafan  close(s);
34366963Speter}
34466963Speter#endif
34550276Speter