arp.c revision 89420
176479Swpaul/*
276479Swpaul * sys-bsd.c - System-dependent procedures for setting up
376479Swpaul * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
476479Swpaul *
576479Swpaul * Copyright (c) 1989 Carnegie Mellon University.
676479Swpaul * All rights reserved.
776479Swpaul *
876479Swpaul * Redistribution and use in source and binary forms are permitted
976479Swpaul * provided that the above copyright notice and this paragraph are
1076479Swpaul * duplicated in all such forms and that any documentation,
1176479Swpaul * advertising materials, and other materials related to such
1276479Swpaul * distribution and use acknowledge that the software was developed
1376479Swpaul * by Carnegie Mellon University.  The name of the
1476479Swpaul * University may not be used to endorse or promote products derived
1576479Swpaul * from this software without specific prior written permission.
1676479Swpaul * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1776479Swpaul * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1876479Swpaul * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1976479Swpaul *
2076479Swpaul * $FreeBSD: head/usr.sbin/ppp/arp.c 89420 2002-01-16 13:15:47Z brian $
2176479Swpaul *
2276479Swpaul */
2376479Swpaul
2476479Swpaul/*
2576479Swpaul * TODO:
2676479Swpaul */
2776479Swpaul
2876479Swpaul#include <sys/param.h>
2976479Swpaul#include <sys/socket.h>
3076479Swpaul#include <net/if.h>
3176479Swpaul#include <net/route.h>
3276479Swpaul#include <net/if_dl.h>
3376479Swpaul#include <netinet/in.h>
3476479Swpaul#include <netinet/if_ether.h>
3576479Swpaul#include <arpa/inet.h>
3676479Swpaul#include <netinet/in_systm.h>
3776479Swpaul#include <netinet/ip.h>
3876479Swpaul#include <sys/un.h>
3976479Swpaul
4076479Swpaul#include <errno.h>
4176479Swpaul#include <stdio.h>
4276479Swpaul#include <stdlib.h>
4376479Swpaul#include <string.h>
4476479Swpaul#include <sys/sysctl.h>
4576479Swpaul#include <termios.h>
4676479Swpaul#include <unistd.h>
4776479Swpaul
4876479Swpaul#include "layer.h"
4976479Swpaul#include "mbuf.h"
5076479Swpaul#include "log.h"
5176479Swpaul#include "id.h"
5276479Swpaul#include "timer.h"
5376479Swpaul#include "fsm.h"
5476479Swpaul#include "defs.h"
5576479Swpaul#include "iplist.h"
5676479Swpaul#include "throughput.h"
5776479Swpaul#include "slcompress.h"
5876479Swpaul#include "lqr.h"
5976479Swpaul#include "hdlc.h"
6076479Swpaul#include "ncpaddr.h"
6176479Swpaul#include "ipcp.h"
6276479Swpaul#include "ipv6cp.h"
6376479Swpaul#include "descriptor.h"
6476479Swpaul#include "lcp.h"
6576479Swpaul#include "ccp.h"
6676479Swpaul#include "link.h"
6776479Swpaul#include "mp.h"
6876479Swpaul#include "ncp.h"
6976479Swpaul#include "filter.h"
7076479Swpaul#ifndef NORADIUS
7176479Swpaul#include "radius.h"
7278323Swpaul#endif
7378323Swpaul#include "bundle.h"
7478323Swpaul#include "iface.h"
7578323Swpaul#include "arp.h"
7678323Swpaul
7778323Swpaul/*
7878323Swpaul * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
7978323Swpaul * if it exists.
8078323Swpaul */
8178323Swpaul#define SET_SA_FAMILY(addr, family)		\
8278323Swpaul    memset((char *) &(addr), '\0', sizeof(addr));	\
8378323Swpaul    addr.sa_family = (family); 			\
8478323Swpaul    addr.sa_len = sizeof(addr);
8578323Swpaul
8678323Swpaul
8778323Swpaul#if RTM_VERSION >= 3
8876479Swpaul
8976479Swpaul/*
9076479Swpaul * arp_SetProxy - Make a proxy ARP entry for the peer.
9176479Swpaul */
9276479Swpaulstatic struct {
9376479Swpaul  struct rt_msghdr hdr;
9476479Swpaul  struct sockaddr_inarp dst;
9576479Swpaul  struct sockaddr_dl hwa;
9676479Swpaul  char extra[128];
9776479Swpaul} arpmsg;
9876479Swpaul
9976479Swpaulstatic int
10076479Swpaularp_ProxySub(struct bundle *bundle, struct in_addr addr, int add, int s)
10176479Swpaul{
10276479Swpaul  int routes;
10376479Swpaul
10476479Swpaul  /*
10576479Swpaul   * Get the hardware address of an interface on the same subnet as our local
10676479Swpaul   * address.
10776479Swpaul   */
10876479Swpaul
10976479Swpaul  memset(&arpmsg, 0, sizeof arpmsg);
11076479Swpaul  if (!arp_EtherAddr(s, addr, &arpmsg.hwa, 0)) {
11176479Swpaul    log_Printf(LogWARN, "%s: Cannot determine ethernet address for proxy ARP\n",
11276479Swpaul	       inet_ntoa(addr));
11376479Swpaul    return 0;
11476479Swpaul  }
11576479Swpaul  routes = ID0socket(AF_ROUTE, SOCK_RAW, AF_INET);
11676479Swpaul  if (routes < 0) {
11776479Swpaul    log_Printf(LogERROR, "arp_SetProxy: opening routing socket: %s\n",
11876479Swpaul	      strerror(errno));
11976479Swpaul    return 0;
12076479Swpaul  }
12176479Swpaul  arpmsg.hdr.rtm_type = add ? RTM_ADD : RTM_DELETE;
12276479Swpaul  arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
12376479Swpaul  arpmsg.hdr.rtm_version = RTM_VERSION;
12476479Swpaul  arpmsg.hdr.rtm_seq = ++bundle->routing_seq;
12576479Swpaul  arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
12676522Swpaul  arpmsg.hdr.rtm_inits = RTV_EXPIRE;
12776479Swpaul  arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
12876479Swpaul  arpmsg.dst.sin_family = AF_INET;
12976479Swpaul  arpmsg.dst.sin_addr.s_addr = addr.s_addr;
13076479Swpaul  arpmsg.dst.sin_other = SIN_PROXY;
13176479Swpaul
13276479Swpaul  arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
13376479Swpaul    + arpmsg.hwa.sdl_len;
13476479Swpaul
13576479Swpaul
13676479Swpaul  if (ID0write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0 &&
13776479Swpaul      !(!add && errno == ESRCH)) {
13876479Swpaul    log_Printf(LogERROR, "%s proxy arp entry %s: %s\n",
13976479Swpaul	add ? "Add" : "Delete", inet_ntoa(addr), strerror(errno));
14076479Swpaul    close(routes);
14176479Swpaul    return 0;
14276479Swpaul  }
14376479Swpaul  close(routes);
14476479Swpaul  return 1;
14576479Swpaul}
14676479Swpaul
14776479Swpaulint
14876479Swpaularp_SetProxy(struct bundle *bundle, struct in_addr addr, int s)
14992739Salfred{
15092739Salfred  return (arp_ProxySub(bundle, addr, 1, s));
15192739Salfred}
15276479Swpaul
15392739Salfred/*
15492739Salfred * arp_ClearProxy - Delete the proxy ARP entry for the peer.
15592739Salfred */
15692739Salfredint
15776479Swpaularp_ClearProxy(struct bundle *bundle, struct in_addr addr, int s)
15892739Salfred{
15992739Salfred  return (arp_ProxySub(bundle, addr, 0, s));
16092739Salfred}
16192739Salfred
16292739Salfred#else				/* RTM_VERSION */
16392739Salfred
16492739Salfred/*
16592739Salfred * arp_SetProxy - Make a proxy ARP entry for the peer.
16692739Salfred */
16792739Salfredint
16892739Salfredarp_SetProxy(struct bundle *bundle, struct in_addr addr, int s)
16992739Salfred{
17092739Salfred  struct arpreq arpreq;
17192739Salfred  struct {
17292739Salfred    struct sockaddr_dl sdl;
17392739Salfred    char space[128];
17476479Swpaul  }      dls;
17592739Salfred
17692739Salfred  memset(&arpreq, '\0', sizeof arpreq);
17792739Salfred
17892739Salfred  /*
17992739Salfred   * Get the hardware address of an interface on the same subnet as our local
18076479Swpaul   * address.
18192739Salfred   */
18292739Salfred  if (!arp_EtherAddr(s, addr, &dls.sdl, 1)) {
18392739Salfred    log_Printf(LOG_PHASE_BIT, "Cannot determine ethernet address for "
18492739Salfred               "proxy ARP\n");
18576479Swpaul    return 0;
18692739Salfred  }
18792739Salfred  arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
18892739Salfred  arpreq.arp_ha.sa_family = AF_UNSPEC;
18976479Swpaul  memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen);
19092739Salfred  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
19192739Salfred  ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
19292739Salfred  arpreq.arp_flags = ATF_PERM | ATF_PUBL;
19392739Salfred  if (ID0ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) {
19492739Salfred    log_Printf(LogERROR, "arp_SetProxy: ioctl(SIOCSARP): %s\n",
19576479Swpaul               strerror(errno));
19676479Swpaul    return 0;
19776479Swpaul  }
19876479Swpaul  return 1;
19976479Swpaul}
20076479Swpaul
20176479Swpaul/*
20276479Swpaul * arp_ClearProxy - Delete the proxy ARP entry for the peer.
20376479Swpaul */
20476479Swpaulint
20576479Swpaularp_ClearProxy(struct bundle *bundle, struct in_addr addr, int s)
20676479Swpaul{
20776479Swpaul  struct arpreq arpreq;
20876479Swpaul
20976479Swpaul  memset(&arpreq, '\0', sizeof arpreq);
21076479Swpaul  SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
21176479Swpaul  ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
21276479Swpaul  if (ID0ioctl(s, SIOCDARP, (caddr_t) & arpreq) < 0) {
21376479Swpaul    log_Printf(LogERROR, "arp_ClearProxy: ioctl(SIOCDARP): %s\n",
21476479Swpaul               strerror(errno));
21576479Swpaul    return 0;
21676479Swpaul  }
21776479Swpaul  return 1;
21876479Swpaul}
21976479Swpaul
22076479Swpaul#endif				/* RTM_VERSION */
22176479Swpaul
22276479Swpaul
22376479Swpaul/*
22476479Swpaul * arp_EtherAddr - get the hardware address of an interface on the
22576479Swpaul * the same subnet as ipaddr.
22676479Swpaul */
22776479Swpaul
22876479Swpaulint
22976479Swpaularp_EtherAddr(int s, struct in_addr ipaddr, struct sockaddr_dl *hwaddr,
23076479Swpaul              int verbose)
23176479Swpaul{
23276479Swpaul  int mib[6], skip;
23376479Swpaul  size_t needed;
23476479Swpaul  char *buf, *ptr, *end;
23576479Swpaul  struct if_msghdr *ifm;
23676479Swpaul  struct ifa_msghdr *ifam;
23776479Swpaul  struct sockaddr_dl *dl;
23876479Swpaul  struct sockaddr *sa[RTAX_MAX];
23976479Swpaul
24076479Swpaul  mib[0] = CTL_NET;
24176479Swpaul  mib[1] = PF_ROUTE;
24276479Swpaul  mib[2] = 0;
24376479Swpaul  mib[3] = 0;
24476479Swpaul  mib[4] = NET_RT_IFLIST;
24576479Swpaul  mib[5] = 0;
24676479Swpaul
24776479Swpaul  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
24876479Swpaul    log_Printf(LogERROR, "arp_EtherAddr: sysctl: estimate: %s\n",
24976479Swpaul              strerror(errno));
25076479Swpaul    return 0;
25176479Swpaul  }
25276479Swpaul
25376479Swpaul  if ((buf = malloc(needed)) == NULL)
25476479Swpaul    return 0;
25576479Swpaul
25676479Swpaul  if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
25776479Swpaul    free(buf);
25876479Swpaul    return 0;
25976479Swpaul  }
26076479Swpaul  end = buf + needed;
26176479Swpaul
26276479Swpaul  ptr = buf;
26376479Swpaul  while (ptr < end) {
26476479Swpaul    ifm = (struct if_msghdr *)ptr;		/* On if_msghdr */
26576479Swpaul    if (ifm->ifm_type != RTM_IFINFO)
26676479Swpaul      break;
26776479Swpaul    dl = (struct sockaddr_dl *)(ifm + 1);	/* Single _dl at end */
26876479Swpaul    skip = (ifm->ifm_flags & (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT |
26976479Swpaul            IFF_NOARP | IFF_LOOPBACK)) != (IFF_UP | IFF_BROADCAST);
27076479Swpaul    ptr += ifm->ifm_msglen;			/* First ifa_msghdr */
27176479Swpaul    while (ptr < end) {
27276479Swpaul      ifam = (struct ifa_msghdr *)ptr;	/* Next ifa_msghdr (alias) */
27376479Swpaul      if (ifam->ifam_type != RTM_NEWADDR)	/* finished ? */
27476479Swpaul        break;
27576479Swpaul      ptr += ifam->ifam_msglen;
27676479Swpaul      if (skip || (ifam->ifam_addrs & (RTA_NETMASK|RTA_IFA)) !=
27776479Swpaul          (RTA_NETMASK|RTA_IFA))
27876479Swpaul        continue;
27976479Swpaul      /* Found a candidate.  Do the addresses match ? */
28076479Swpaul      if (log_IsKept(LogDEBUG) &&
28176479Swpaul          ptr == (char *)ifm + ifm->ifm_msglen + ifam->ifam_msglen)
28276479Swpaul        log_Printf(LogDEBUG, "%.*s interface is a candidate for proxy\n",
28376479Swpaul                  dl->sdl_nlen, dl->sdl_data);
28476479Swpaul
28576479Swpaul      iface_ParseHdr(ifam, sa);
28676479Swpaul
28776479Swpaul      if (sa[RTAX_IFA]->sa_family == AF_INET) {
28876479Swpaul        struct sockaddr_in *ifa, *netmask;
28976479Swpaul
29076479Swpaul        ifa = (struct sockaddr_in *)sa[RTAX_IFA];
29176479Swpaul        netmask = (struct sockaddr_in *)sa[RTAX_NETMASK];
29276479Swpaul
29376479Swpaul        if (log_IsKept(LogDEBUG)) {
29476479Swpaul          char a[16];
29576479Swpaul
29676479Swpaul          strncpy(a, inet_ntoa(netmask->sin_addr), sizeof a - 1);
29776479Swpaul          a[sizeof a - 1] = '\0';
29876479Swpaul          log_Printf(LogDEBUG, "Check addr %s, mask %s\n",
29976479Swpaul                     inet_ntoa(ifa->sin_addr), a);
30076479Swpaul        }
30176479Swpaul
30276479Swpaul        if ((ifa->sin_addr.s_addr & netmask->sin_addr.s_addr) ==
30376479Swpaul            (ipaddr.s_addr & netmask->sin_addr.s_addr)) {
30476479Swpaul          log_Printf(verbose ? LogPHASE : LogDEBUG,
30576479Swpaul                     "Found interface %.*s for %s\n", dl->sdl_alen,
30676479Swpaul                     dl->sdl_data, inet_ntoa(ipaddr));
30776479Swpaul          memcpy(hwaddr, dl, dl->sdl_len);
30876479Swpaul          free(buf);
30976479Swpaul          return 1;
31076479Swpaul        }
31176479Swpaul      }
31276479Swpaul    }
31376479Swpaul  }
31476479Swpaul  free(buf);
31576479Swpaul
31676479Swpaul  return 0;
31776479Swpaul}
31876479Swpaul