arp.c revision 32616
1168404Spjd/*
2168404Spjd * sys-bsd.c - System-dependent procedures for setting up
3168404Spjd * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
4168404Spjd *
5168404Spjd * Copyright (c) 1989 Carnegie Mellon University.
6168404Spjd * All rights reserved.
7168404Spjd *
8168404Spjd * Redistribution and use in source and binary forms are permitted
9168404Spjd * provided that the above copyright notice and this paragraph are
10168404Spjd * duplicated in all such forms and that any documentation,
11168404Spjd * advertising materials, and other materials related to such
12168404Spjd * distribution and use acknowledge that the software was developed
13168404Spjd * by Carnegie Mellon University.  The name of the
14168404Spjd * University may not be used to endorse or promote products derived
15168404Spjd * from this software without specific prior written permission.
16168404Spjd * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17168404Spjd * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18168404Spjd * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19168404Spjd *
20168404Spjd * $Id: arp.c,v 1.21 1998/01/11 04:02:57 brian Exp $
21168404Spjd *
22168404Spjd */
23168404Spjd
24168404Spjd/*
25168404Spjd * TODO:
26168404Spjd */
27168404Spjd
28168404Spjd#include <sys/types.h>
29168404Spjd#include <sys/time.h>
30168404Spjd#include <sys/socket.h>
31168404Spjd#include <net/if.h>
32168404Spjd#include <net/route.h>
33185029Spjd#include <net/if_dl.h>
34168404Spjd#include <netinet/in.h>
35168404Spjd#include <net/if_types.h>
36185029Spjd#include <netinet/if_ether.h>
37168404Spjd
38185029Spjd#include <fcntl.h>
39168404Spjd#include <stdio.h>
40168404Spjd#include <stdlib.h>
41219089Spjd#include <string.h>
42185029Spjd#include <sys/errno.h>
43185029Spjd#include <sys/ioctl.h>
44219089Spjd#include <sys/sysctl.h>
45185029Spjd#include <sys/uio.h>
46185029Spjd#include <unistd.h>
47185029Spjd
48219089Spjd#include "command.h"
49168404Spjd#include "mbuf.h"
50168404Spjd#include "log.h"
51219089Spjd#include "id.h"
52168404Spjd#include "route.h"
53168404Spjd#include "arp.h"
54168404Spjd
55219089Spjd#ifdef DEBUG
56168404Spjd/*
57168404Spjd * To test the proxy arp stuff, put the following in your Makefile:
58219089Spjd *
59168404Spjd * arp-test: arp.c
60168404Spjd * 	cp ${.CURDIR}/arp.c arp-test.c
61168404Spjd * 	echo 'const char *' >>arp-test.c
62219089Spjd * 	awk '/^Index2Nam/,/^}/' ${.CURDIR}/route.c >>arp-test.c
63168404Spjd * 	cc -I${.CURDIR} -DDEBUG arp-test.c -o arp-test
64168404Spjd *
65219089Spjd * and type ``make arp-test''.
66168404Spjd *
67168404Spjd */
68168404Spjd#define LogIsKept(x) 0
69219089Spjd#define LogPrintf fprintf
70168404Spjd#undef LogDEBUG
71168404Spjd#define LogDEBUG stderr
72219089Spjd#undef LogERROR
73168404Spjd#define LogERROR stderr
74168404Spjd#undef LogPHASE
75185029Spjd#define LogPHASE stdout
76219089Spjd#define ID0socket socket
77185029Spjd#define ID0ioctl ioctl
78185029Spjd#endif
79185029Spjd
80219089Spjdstatic int rtm_seq;
81219089Spjd
82185029Spjdstatic int get_ether_addr(int, struct in_addr, struct sockaddr_dl *);
83185029Spjd
84185029Spjd/*
85197860Spjd * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
86185029Spjd * if it exists.
87185029Spjd */
88168404Spjd#define SET_SA_FAMILY(addr, family)		\
89168404Spjd    memset((char *) &(addr), '\0', sizeof(addr));	\
90168404Spjd    addr.sa_family = (family); 			\
91168404Spjd    addr.sa_len = sizeof(addr);
92168404Spjd
93219089Spjd
94168404Spjd#if RTM_VERSION >= 3
95168404Spjd
96168404Spjd/*
97168404Spjd * sifproxyarp - Make a proxy ARP entry for the peer.
98219089Spjd */
99185029Spjdstatic struct {
100219089Spjd  struct rt_msghdr hdr;
101168404Spjd  struct sockaddr_inarp dst;
102168404Spjd  struct sockaddr_dl hwa;
103168404Spjd  char extra[128];
104219089Spjd} arpmsg;
105168404Spjd
106168404Spjdstatic int arpmsg_valid;
107168404Spjd
108168404Spjdint
109168404Spjdsifproxyarp(int unit, struct in_addr hisaddr)
110168404Spjd{
111219089Spjd  int routes;
112168404Spjd
113168404Spjd  /*
114219089Spjd   * Get the hardware address of an interface on the same subnet as our local
115185029Spjd   * address.
116219089Spjd   */
117168404Spjd  memset(&arpmsg, 0, sizeof arpmsg);
118168404Spjd  if (!get_ether_addr(unit, hisaddr, &arpmsg.hwa)) {
119168404Spjd    LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n");
120219089Spjd    return 0;
121168404Spjd  }
122168404Spjd  routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
123219089Spjd  if (routes < 0) {
124185029Spjd    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
125185029Spjd	      strerror(errno));
126219089Spjd    return 0;
127168404Spjd  }
128184413Strasz  arpmsg.hdr.rtm_type = RTM_ADD;
129219089Spjd  arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
130168404Spjd  arpmsg.hdr.rtm_version = RTM_VERSION;
131168404Spjd  arpmsg.hdr.rtm_seq = ++rtm_seq;
132184413Strasz  arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
133168404Spjd  arpmsg.hdr.rtm_inits = RTV_EXPIRE;
134219089Spjd  arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
135168404Spjd  arpmsg.dst.sin_family = AF_INET;
136168404Spjd  arpmsg.dst.sin_addr.s_addr = hisaddr.s_addr;
137219089Spjd  arpmsg.dst.sin_other = SIN_PROXY;
138168404Spjd
139168404Spjd  arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
140168404Spjd    + arpmsg.hwa.sdl_len;
141168404Spjd  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
142168404Spjd    LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno));
143168404Spjd    close(routes);
144219089Spjd    return 0;
145219089Spjd  }
146219089Spjd  close(routes);
147219089Spjd  arpmsg_valid = 1;
148168404Spjd  return 1;
149219089Spjd}
150219089Spjd
151168404Spjd/*
152219089Spjd * cifproxyarp - Delete the proxy ARP entry for the peer.
153168404Spjd */
154219089Spjdint
155219089Spjdcifproxyarp(int unit, struct in_addr hisaddr)
156219089Spjd{
157168404Spjd  int routes;
158219089Spjd
159219089Spjd  if (!arpmsg_valid)
160219089Spjd    return 0;
161219089Spjd  arpmsg_valid = 0;
162219089Spjd
163219089Spjd  arpmsg.hdr.rtm_type = RTM_DELETE;
164219089Spjd  arpmsg.hdr.rtm_seq = ++rtm_seq;
165219089Spjd
166219089Spjd  routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
167219089Spjd  if (routes < 0) {
168219089Spjd    LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
169219089Spjd	      strerror(errno));
170219089Spjd    return 0;
171219089Spjd  }
172219089Spjd  if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
173219089Spjd    LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno));
174219089Spjd    close(routes);
175185029Spjd    return 0;
176219089Spjd  }
177219089Spjd  close(routes);
178219089Spjd  return 1;
179219089Spjd}
180219089Spjd
181219089Spjd#else				/* RTM_VERSION */
182219089Spjd
183219089Spjd/*
184219089Spjd * sifproxyarp - Make a proxy ARP entry for the peer.
185219089Spjd */
186219089Spjdint
187219089Spjdsifproxyarp(int unit, struct in_addr hisaddr)
188219089Spjd{
189219089Spjd  struct arpreq arpreq;
190219089Spjd  struct {
191219089Spjd    struct sockaddr_dl sdl;
192219089Spjd    char space[128];
193219089Spjd  }      dls;
194219089Spjd
195219089Spjd  memset(&arpreq, '\0', sizeof arpreq);
196219089Spjd
197219089Spjd  /*
198219089Spjd   * Get the hardware address of an interface on the same subnet as our local
199168404Spjd   * address.
200168404Spjd   */
201168404Spjd  if (!get_ether_addr(unit, hisaddr, &dls.sdl)) {
202219089Spjd    LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n");
203219089Spjd    return 0;
204219089Spjd  }
205219089Spjd  arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
206219089Spjd  arpreq.arp_ha.sa_family = AF_UNSPEC;
207219089Spjd  memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen);
208219089Spjd  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
209219089Spjd  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr.s_addr;
210219089Spjd  arpreq.arp_flags = ATF_PERM | ATF_PUBL;
211219089Spjd  if (ID0ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) {
212219089Spjd    LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno));
213219089Spjd    return 0;
214168404Spjd  }
215219089Spjd  return 1;
216168404Spjd}
217168404Spjd
218168404Spjd/*
219168404Spjd * cifproxyarp - Delete the proxy ARP entry for the peer.
220168404Spjd */
221168404Spjdint
222168404Spjdcifproxyarp(int unit, struct in_addr hisaddr)
223219089Spjd{
224168404Spjd  struct arpreq arpreq;
225168404Spjd
226168404Spjd  memset(&arpreq, '\0', sizeof arpreq);
227168404Spjd  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
228168404Spjd  ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr.s_addr;
229168404Spjd  if (ID0ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) {
230168404Spjd    LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno));
231168404Spjd    return 0;
232168404Spjd  }
233168404Spjd  return 1;
234168404Spjd}
235168404Spjd
236219089Spjd#endif				/* RTM_VERSION */
237168404Spjd
238168404Spjd
239219089Spjd/*
240168404Spjd * get_ether_addr - get the hardware address of an interface on the
241168404Spjd * the same subnet as ipaddr.
242168404Spjd */
243168404Spjd#define MAX_IFS		32
244168404Spjd
245168404Spjdstatic int
246219089Spjdget_ether_addr(int s, struct in_addr ipaddr, struct sockaddr_dl *hwaddr)
247168404Spjd{
248168404Spjd  int idx;
249168404Spjd  const char *got;
250168404Spjd  char *sp, *ep, *cp, *wp;
251168404Spjd  struct ifreq ifrq;
252168404Spjd  struct in_addr addr, mask;
253168404Spjd  struct rt_msghdr *rtm;
254168404Spjd  struct sockaddr *sa_dst, *sa_gw;
255168404Spjd  struct sockaddr_dl *dl;
256168404Spjd  size_t needed;
257219089Spjd  int mib[6];
258219089Spjd
259219089Spjd  idx = 1;
260185029Spjd  while (strcmp(got = Index2Nam(idx), "???")) {
261185029Spjd    strncpy(ifrq.ifr_name, got, sizeof ifrq.ifr_name - 1);
262185029Spjd    ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
263168404Spjd    if (ID0ioctl(s, SIOCGIFADDR, &ifrq) == 0 &&
264168404Spjd        ifrq.ifr_addr.sa_family == AF_INET) {
265168404Spjd      addr = ((struct sockaddr_in *)&ifrq.ifr_addr)->sin_addr;
266168404Spjd      if (ID0ioctl(s, SIOCGIFNETMASK, &ifrq) == 0) {
267219089Spjd        mask = ((struct sockaddr_in *)&ifrq.ifr_broadaddr)->sin_addr;
268168404Spjd        if ((ipaddr.s_addr & mask.s_addr) == (addr.s_addr & mask.s_addr))
269168404Spjd          break;
270168404Spjd      }
271168404Spjd    }
272168404Spjd    idx++;
273168404Spjd  }
274168404Spjd
275168404Spjd  if (!strcmp(got, "???"))
276168404Spjd    return 0;
277168404Spjd
278168404Spjd  LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", got);
279219089Spjd
280168404Spjd  mib[0] = CTL_NET;
281219089Spjd  mib[1] = PF_ROUTE;
282168404Spjd  mib[2] = 0;
283168404Spjd  mib[3] = 0;
284168404Spjd  mib[4] = NET_RT_DUMP;
285168404Spjd  mib[5] = 0;
286168404Spjd  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
287168404Spjd    LogPrintf(LogERROR, "get_ether_addr: sysctl: estimate: %s\n",
288168404Spjd              strerror(errno));
289219089Spjd    return 0;
290168404Spjd  }
291168404Spjd  if (needed < 0)
292168404Spjd    return 0;
293168404Spjd  if ((sp = malloc(needed)) == NULL)
294168404Spjd    return 0;
295168404Spjd  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
296219089Spjd    LogPrintf(LogERROR, "ShowRoute: sysctl: getroute: %s\n", strerror(errno));
297168404Spjd    free(sp);
298219089Spjd    return (1);
299219089Spjd  }
300185029Spjd  ep = sp + needed;
301219089Spjd
302185029Spjd  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
303219089Spjd    rtm = (struct rt_msghdr *) cp;
304168404Spjd    if (rtm->rtm_index == idx) {
305168404Spjd      wp = (char *)(rtm+1);
306168404Spjd
307219089Spjd      if (rtm->rtm_addrs & RTA_DST) {
308185029Spjd        sa_dst = (struct sockaddr *)wp;
309168404Spjd        wp += sa_dst->sa_len;
310168404Spjd      } else
311219089Spjd        sa_dst = NULL;
312185029Spjd
313219089Spjd      if (rtm->rtm_addrs & RTA_GATEWAY) {
314168404Spjd        sa_gw = (struct sockaddr *)wp;
315168404Spjd        if (sa_gw->sa_family == AF_LINK) {
316168404Spjd          dl = (struct sockaddr_dl *)wp;
317219089Spjd          if (dl->sdl_alen && dl->sdl_type == IFT_ETHER) {
318168404Spjd            memcpy(hwaddr, dl, dl->sdl_len);
319168404Spjd            free(sp);
320219089Spjd            return 1;
321185029Spjd          }
322185029Spjd        }
323168404Spjd      }
324219089Spjd    }
325168404Spjd  }
326168404Spjd  free(sp);
327168404Spjd  return 0;
328168404Spjd}
329168404Spjd
330168404Spjd#ifdef DEBUG
331168404Spjdint
332219089Spjdmain(int argc, char **argv)
333219089Spjd{
334168404Spjd  struct in_addr ipaddr;
335168404Spjd  int s, f;
336168404Spjd
337219089Spjd  s = socket(AF_INET, SOCK_DGRAM, 0);
338185029Spjd  for (f = 1; f < argc; f++) {
339185029Spjd    if (inet_aton(argv[f], &ipaddr))
340168404Spjd      sifproxyarp(s, ipaddr);
341168404Spjd  }
342168404Spjd  close(s);
343168404Spjd}
344168404Spjd#endif
345168404Spjd