xref: /freebsd/usr.sbin/ppp/arp.c (revision 2f7866811e62f81cb5c8adc015e3bb373410eaeb)
11ae349f5Scvs2svn /*
21ae349f5Scvs2svn  * sys-bsd.c - System-dependent procedures for setting up
31ae349f5Scvs2svn  * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
41ae349f5Scvs2svn  *
51ae349f5Scvs2svn  * Copyright (c) 1989 Carnegie Mellon University.
61ae349f5Scvs2svn  * All rights reserved.
71ae349f5Scvs2svn  *
81ae349f5Scvs2svn  * Redistribution and use in source and binary forms are permitted
91ae349f5Scvs2svn  * provided that the above copyright notice and this paragraph are
101ae349f5Scvs2svn  * duplicated in all such forms and that any documentation,
111ae349f5Scvs2svn  * advertising materials, and other materials related to such
121ae349f5Scvs2svn  * distribution and use acknowledge that the software was developed
131ae349f5Scvs2svn  * by Carnegie Mellon University.  The name of the
141ae349f5Scvs2svn  * University may not be used to endorse or promote products derived
151ae349f5Scvs2svn  * from this software without specific prior written permission.
161ae349f5Scvs2svn  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
171ae349f5Scvs2svn  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
181ae349f5Scvs2svn  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
191ae349f5Scvs2svn  *
202f786681SBrian Somers  * $Id: arp.c,v 1.27.2.8 1998/03/16 22:53:25 brian Exp $
211ae349f5Scvs2svn  *
221ae349f5Scvs2svn  */
231ae349f5Scvs2svn 
241ae349f5Scvs2svn /*
251ae349f5Scvs2svn  * TODO:
261ae349f5Scvs2svn  */
271ae349f5Scvs2svn 
281ae349f5Scvs2svn #include <sys/param.h>
291ae349f5Scvs2svn #include <sys/time.h>
301ae349f5Scvs2svn #include <sys/socket.h>
311ae349f5Scvs2svn #include <net/if.h>
321ae349f5Scvs2svn #include <net/route.h>
331ae349f5Scvs2svn #include <net/if_dl.h>
341ae349f5Scvs2svn #include <netinet/in.h>
351ae349f5Scvs2svn #include <net/if_types.h>
361ae349f5Scvs2svn #include <netinet/if_ether.h>
371ae349f5Scvs2svn #include <arpa/inet.h>
38eaa4df37SBrian Somers #include <netinet/in_systm.h>
39eaa4df37SBrian Somers #include <netinet/ip.h>
401ae349f5Scvs2svn 
411ae349f5Scvs2svn #include <fcntl.h>
421ae349f5Scvs2svn #include <stdio.h>
431ae349f5Scvs2svn #include <stdlib.h>
441ae349f5Scvs2svn #include <string.h>
451ae349f5Scvs2svn #include <sys/errno.h>
461ae349f5Scvs2svn #include <sys/ioctl.h>
471ae349f5Scvs2svn #include <sys/sysctl.h>
481ae349f5Scvs2svn #include <sys/uio.h>
491ae349f5Scvs2svn #include <unistd.h>
501ae349f5Scvs2svn 
511ae349f5Scvs2svn #include "command.h"
521ae349f5Scvs2svn #include "mbuf.h"
531ae349f5Scvs2svn #include "log.h"
541ae349f5Scvs2svn #include "id.h"
551ae349f5Scvs2svn #include "route.h"
56455aabc3SBrian Somers #include "timer.h"
57455aabc3SBrian Somers #include "fsm.h"
58455aabc3SBrian Somers #include "defs.h"
59455aabc3SBrian Somers #include "iplist.h"
605828db6dSBrian Somers #include "throughput.h"
61eaa4df37SBrian Somers #include "slcompress.h"
625828db6dSBrian Somers #include "ipcp.h"
635ca5389aSBrian Somers #include "filter.h"
642f786681SBrian Somers #include "descriptor.h"
655828db6dSBrian Somers #include "bundle.h"
661ae349f5Scvs2svn #include "arp.h"
671ae349f5Scvs2svn 
681ae349f5Scvs2svn static int get_ether_addr(int, struct in_addr, struct sockaddr_dl *);
691ae349f5Scvs2svn 
701ae349f5Scvs2svn /*
711ae349f5Scvs2svn  * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
721ae349f5Scvs2svn  * if it exists.
731ae349f5Scvs2svn  */
741ae349f5Scvs2svn #define SET_SA_FAMILY(addr, family)		\
751ae349f5Scvs2svn     memset((char *) &(addr), '\0', sizeof(addr));	\
761ae349f5Scvs2svn     addr.sa_family = (family); 			\
771ae349f5Scvs2svn     addr.sa_len = sizeof(addr);
781ae349f5Scvs2svn 
791ae349f5Scvs2svn 
801ae349f5Scvs2svn #if RTM_VERSION >= 3
811ae349f5Scvs2svn 
821ae349f5Scvs2svn /*
831ae349f5Scvs2svn  * sifproxyarp - Make a proxy ARP entry for the peer.
841ae349f5Scvs2svn  */
851ae349f5Scvs2svn static struct {
861ae349f5Scvs2svn   struct rt_msghdr hdr;
871ae349f5Scvs2svn   struct sockaddr_inarp dst;
881ae349f5Scvs2svn   struct sockaddr_dl hwa;
891ae349f5Scvs2svn   char extra[128];
901ae349f5Scvs2svn } arpmsg;
911ae349f5Scvs2svn 
921ae349f5Scvs2svn static int arpmsg_valid;
931ae349f5Scvs2svn 
941ae349f5Scvs2svn int
95503a7782SBrian Somers sifproxyarp(struct bundle *bundle, struct in_addr addr, int s)
961ae349f5Scvs2svn {
971ae349f5Scvs2svn   int routes;
981ae349f5Scvs2svn 
991ae349f5Scvs2svn   /*
1001ae349f5Scvs2svn    * Get the hardware address of an interface on the same subnet as our local
1011ae349f5Scvs2svn    * address.
1021ae349f5Scvs2svn    */
1031ae349f5Scvs2svn   memset(&arpmsg, 0, sizeof arpmsg);
104503a7782SBrian Somers   if (!get_ether_addr(s, addr, &arpmsg.hwa)) {
1051ae349f5Scvs2svn     LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n");
1061ae349f5Scvs2svn     return 0;
1071ae349f5Scvs2svn   }
1081ae349f5Scvs2svn   routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
1091ae349f5Scvs2svn   if (routes < 0) {
1101ae349f5Scvs2svn     LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
1111ae349f5Scvs2svn 	      strerror(errno));
1121ae349f5Scvs2svn     return 0;
1131ae349f5Scvs2svn   }
1141ae349f5Scvs2svn   arpmsg.hdr.rtm_type = RTM_ADD;
1151ae349f5Scvs2svn   arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
1161ae349f5Scvs2svn   arpmsg.hdr.rtm_version = RTM_VERSION;
117820de6ebSBrian Somers   arpmsg.hdr.rtm_seq = ++bundle->routing_seq;
1181ae349f5Scvs2svn   arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
1191ae349f5Scvs2svn   arpmsg.hdr.rtm_inits = RTV_EXPIRE;
1201ae349f5Scvs2svn   arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
1211ae349f5Scvs2svn   arpmsg.dst.sin_family = AF_INET;
122503a7782SBrian Somers   arpmsg.dst.sin_addr.s_addr = addr.s_addr;
1231ae349f5Scvs2svn   arpmsg.dst.sin_other = SIN_PROXY;
1241ae349f5Scvs2svn 
1251ae349f5Scvs2svn   arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
1261ae349f5Scvs2svn     + arpmsg.hwa.sdl_len;
1271ae349f5Scvs2svn   if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
1281ae349f5Scvs2svn     LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno));
1291ae349f5Scvs2svn     close(routes);
1301ae349f5Scvs2svn     return 0;
1311ae349f5Scvs2svn   }
1321ae349f5Scvs2svn   close(routes);
1331ae349f5Scvs2svn   arpmsg_valid = 1;
1341ae349f5Scvs2svn   return 1;
1351ae349f5Scvs2svn }
1361ae349f5Scvs2svn 
1371ae349f5Scvs2svn /*
1381ae349f5Scvs2svn  * cifproxyarp - Delete the proxy ARP entry for the peer.
1391ae349f5Scvs2svn  */
1401ae349f5Scvs2svn int
141503a7782SBrian Somers cifproxyarp(struct bundle *bundle, struct in_addr addr, int s)
1421ae349f5Scvs2svn {
1431ae349f5Scvs2svn   int routes;
1441ae349f5Scvs2svn 
1451ae349f5Scvs2svn   if (!arpmsg_valid)
1461ae349f5Scvs2svn     return 0;
1471ae349f5Scvs2svn   arpmsg_valid = 0;
1481ae349f5Scvs2svn 
1491ae349f5Scvs2svn   arpmsg.hdr.rtm_type = RTM_DELETE;
150820de6ebSBrian Somers   arpmsg.hdr.rtm_seq = ++bundle->routing_seq;
1511ae349f5Scvs2svn 
1521ae349f5Scvs2svn   routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
1531ae349f5Scvs2svn   if (routes < 0) {
1541ae349f5Scvs2svn     LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
1551ae349f5Scvs2svn 	      strerror(errno));
1561ae349f5Scvs2svn     return 0;
1571ae349f5Scvs2svn   }
1581ae349f5Scvs2svn   if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
1591ae349f5Scvs2svn     LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno));
1601ae349f5Scvs2svn     close(routes);
1611ae349f5Scvs2svn     return 0;
1621ae349f5Scvs2svn   }
1631ae349f5Scvs2svn   close(routes);
1641ae349f5Scvs2svn   return 1;
1651ae349f5Scvs2svn }
1661ae349f5Scvs2svn 
1671ae349f5Scvs2svn #else				/* RTM_VERSION */
1681ae349f5Scvs2svn 
1691ae349f5Scvs2svn /*
1701ae349f5Scvs2svn  * sifproxyarp - Make a proxy ARP entry for the peer.
1711ae349f5Scvs2svn  */
1721ae349f5Scvs2svn int
173503a7782SBrian Somers sifproxyarp(struct bundle *bundle, struct in_addr addr, int s)
1741ae349f5Scvs2svn {
1751ae349f5Scvs2svn   struct arpreq arpreq;
1761ae349f5Scvs2svn   struct {
1771ae349f5Scvs2svn     struct sockaddr_dl sdl;
1781ae349f5Scvs2svn     char space[128];
1791ae349f5Scvs2svn   }      dls;
1801ae349f5Scvs2svn 
1811ae349f5Scvs2svn   memset(&arpreq, '\0', sizeof arpreq);
1821ae349f5Scvs2svn 
1831ae349f5Scvs2svn   /*
1841ae349f5Scvs2svn    * Get the hardware address of an interface on the same subnet as our local
1851ae349f5Scvs2svn    * address.
1861ae349f5Scvs2svn    */
187503a7782SBrian Somers   if (!get_ether_addr(s, addr, &dls.sdl)) {
1881ae349f5Scvs2svn     LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n");
1891ae349f5Scvs2svn     return 0;
1901ae349f5Scvs2svn   }
1911ae349f5Scvs2svn   arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
1921ae349f5Scvs2svn   arpreq.arp_ha.sa_family = AF_UNSPEC;
1931ae349f5Scvs2svn   memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen);
1941ae349f5Scvs2svn   SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
195503a7782SBrian Somers   ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
1961ae349f5Scvs2svn   arpreq.arp_flags = ATF_PERM | ATF_PUBL;
197820de6ebSBrian Somers   if (ID0ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) {
1981ae349f5Scvs2svn     LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno));
1991ae349f5Scvs2svn     return 0;
2001ae349f5Scvs2svn   }
2011ae349f5Scvs2svn   return 1;
2021ae349f5Scvs2svn }
2031ae349f5Scvs2svn 
2041ae349f5Scvs2svn /*
2051ae349f5Scvs2svn  * cifproxyarp - Delete the proxy ARP entry for the peer.
2061ae349f5Scvs2svn  */
2071ae349f5Scvs2svn int
208503a7782SBrian Somers cifproxyarp(struct bundle *bundle, struct in_addr addr, int s)
2091ae349f5Scvs2svn {
2101ae349f5Scvs2svn   struct arpreq arpreq;
2111ae349f5Scvs2svn 
2121ae349f5Scvs2svn   memset(&arpreq, '\0', sizeof arpreq);
2131ae349f5Scvs2svn   SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
214503a7782SBrian Somers   ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
215820de6ebSBrian Somers   if (ID0ioctl(s, SIOCDARP, (caddr_t) & arpreq) < 0) {
2161ae349f5Scvs2svn     LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno));
2171ae349f5Scvs2svn     return 0;
2181ae349f5Scvs2svn   }
2191ae349f5Scvs2svn   return 1;
2201ae349f5Scvs2svn }
2211ae349f5Scvs2svn 
2221ae349f5Scvs2svn #endif				/* RTM_VERSION */
2231ae349f5Scvs2svn 
2241ae349f5Scvs2svn 
2251ae349f5Scvs2svn /*
2261ae349f5Scvs2svn  * get_ether_addr - get the hardware address of an interface on the
2271ae349f5Scvs2svn  * the same subnet as ipaddr.
2281ae349f5Scvs2svn  */
2291ae349f5Scvs2svn 
2301ae349f5Scvs2svn static int
2311ae349f5Scvs2svn get_ether_addr(int s, struct in_addr ipaddr, struct sockaddr_dl *hwaddr)
2321ae349f5Scvs2svn {
2331ae349f5Scvs2svn   int mib[6], sa_len, skip, b;
2341ae349f5Scvs2svn   size_t needed;
2351ae349f5Scvs2svn   char *buf, *ptr, *end;
2361ae349f5Scvs2svn   struct if_msghdr *ifm;
2371ae349f5Scvs2svn   struct ifa_msghdr *ifam;
2381ae349f5Scvs2svn   struct sockaddr *sa;
2391ae349f5Scvs2svn   struct sockaddr_dl *dl;
2401ae349f5Scvs2svn   struct sockaddr_in *ifa, *mask;
2411ae349f5Scvs2svn 
2421ae349f5Scvs2svn   mib[0] = CTL_NET;
2431ae349f5Scvs2svn   mib[1] = PF_ROUTE;
2441ae349f5Scvs2svn   mib[2] = 0;
2451ae349f5Scvs2svn   mib[3] = 0;
2461ae349f5Scvs2svn   mib[4] = NET_RT_IFLIST;
2471ae349f5Scvs2svn   mib[5] = 0;
2481ae349f5Scvs2svn 
2491ae349f5Scvs2svn   if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
2501ae349f5Scvs2svn     LogPrintf(LogERROR, "get_ether_addr: sysctl: estimate: %s\n",
2511ae349f5Scvs2svn               strerror(errno));
2521ae349f5Scvs2svn     return 0;
2531ae349f5Scvs2svn   }
2541ae349f5Scvs2svn 
2551ae349f5Scvs2svn   if ((buf = malloc(needed)) == NULL)
2561ae349f5Scvs2svn     return 0;
2571ae349f5Scvs2svn 
2581ae349f5Scvs2svn   if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
2591ae349f5Scvs2svn     free(buf);
2601ae349f5Scvs2svn     return 0;
2611ae349f5Scvs2svn   }
2621ae349f5Scvs2svn   end = buf + needed;
2631ae349f5Scvs2svn 
2641ae349f5Scvs2svn   ptr = buf;
2651ae349f5Scvs2svn   while (ptr < end) {
2661ae349f5Scvs2svn     ifm = (struct if_msghdr *)ptr;		/* On if_msghdr */
2671ae349f5Scvs2svn     if (ifm->ifm_type != RTM_IFINFO)
2681ae349f5Scvs2svn       break;
2691ae349f5Scvs2svn     dl = (struct sockaddr_dl *)(ifm + 1);	/* Single _dl at end */
2701ae349f5Scvs2svn     skip = (ifm->ifm_flags & (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT |
2711ae349f5Scvs2svn             IFF_NOARP | IFF_LOOPBACK)) != (IFF_UP | IFF_BROADCAST);
2721ae349f5Scvs2svn     ptr += ifm->ifm_msglen;			/* First ifa_msghdr */
2731ae349f5Scvs2svn     while (ptr < end) {
2741ae349f5Scvs2svn       ifam = (struct ifa_msghdr *)ptr;	/* Next ifa_msghdr (alias) */
2751ae349f5Scvs2svn       if (ifam->ifam_type != RTM_NEWADDR)	/* finished ? */
2761ae349f5Scvs2svn         break;
2771ae349f5Scvs2svn       sa = (struct sockaddr *)(ifam+1);	/* pile of sa's at end */
2781ae349f5Scvs2svn       ptr += ifam->ifam_msglen;
2791ae349f5Scvs2svn       if (skip || (ifam->ifam_addrs & (RTA_NETMASK|RTA_IFA)) !=
2801ae349f5Scvs2svn           (RTA_NETMASK|RTA_IFA))
2811ae349f5Scvs2svn         continue;
2821ae349f5Scvs2svn       /* Found a candidate.  Do the addresses match ? */
2831ae349f5Scvs2svn       if (LogIsKept(LogDEBUG) &&
2841ae349f5Scvs2svn           ptr == (char *)ifm + ifm->ifm_msglen + ifam->ifam_msglen)
2851ae349f5Scvs2svn         LogPrintf(LogDEBUG, "%.*s interface is a candidate for proxy\n",
2861ae349f5Scvs2svn                   dl->sdl_nlen, dl->sdl_data);
2871ae349f5Scvs2svn       b = 1;
2881ae349f5Scvs2svn       ifa = mask = NULL;
2891ae349f5Scvs2svn       while (b < (RTA_NETMASK|RTA_IFA) && sa < (struct sockaddr *)ptr) {
2901ae349f5Scvs2svn         switch (b) {
2911ae349f5Scvs2svn         case RTA_IFA:
2921ae349f5Scvs2svn           ifa = (struct sockaddr_in *)sa;
2931ae349f5Scvs2svn           break;
2941ae349f5Scvs2svn         case RTA_NETMASK:
2951ae349f5Scvs2svn           /*
2961ae349f5Scvs2svn            * Careful here !  this sockaddr doesn't have sa_family set to
2971ae349f5Scvs2svn            * AF_INET, and is only 8 bytes big !  I have no idea why !
2981ae349f5Scvs2svn            */
2991ae349f5Scvs2svn           mask = (struct sockaddr_in *)sa;
3001ae349f5Scvs2svn           break;
3011ae349f5Scvs2svn         }
3021ae349f5Scvs2svn         if (ifam->ifam_addrs & b) {
3031ae349f5Scvs2svn #define ALN sizeof(ifa->sin_addr.s_addr)
3041ae349f5Scvs2svn           sa_len = sa->sa_len > 0 ? ((sa->sa_len-1)|(ALN-1))+1 : ALN;
3051ae349f5Scvs2svn           sa = (struct sockaddr *)((char *)sa + sa_len);
3061ae349f5Scvs2svn         }
3071ae349f5Scvs2svn         b <<= 1;
3081ae349f5Scvs2svn       }
3091ae349f5Scvs2svn       if (LogIsKept(LogDEBUG)) {
3101ae349f5Scvs2svn         char a[16];
3111ae349f5Scvs2svn         strncpy(a, inet_ntoa(mask->sin_addr), sizeof a - 1);
3121ae349f5Scvs2svn         a[sizeof a - 1] = '\0';
3131ae349f5Scvs2svn         LogPrintf(LogDEBUG, "Check addr %s, mask %s\n",
3141ae349f5Scvs2svn                   inet_ntoa(ifa->sin_addr), a);
3151ae349f5Scvs2svn       }
3161ae349f5Scvs2svn       if (ifa->sin_family == AF_INET &&
3171ae349f5Scvs2svn           (ifa->sin_addr.s_addr & mask->sin_addr.s_addr) ==
3181ae349f5Scvs2svn           (ipaddr.s_addr & mask->sin_addr.s_addr)) {
3191ae349f5Scvs2svn         LogPrintf(LogPHASE, "Found interface %.*s for proxy arp\n",
3201ae349f5Scvs2svn                   dl->sdl_alen, dl->sdl_data);
3211ae349f5Scvs2svn         memcpy(hwaddr, dl, dl->sdl_len);
3221ae349f5Scvs2svn         free(buf);
3231ae349f5Scvs2svn         return 1;
3241ae349f5Scvs2svn       }
3251ae349f5Scvs2svn     }
3261ae349f5Scvs2svn   }
3271ae349f5Scvs2svn   free(buf);
3281ae349f5Scvs2svn 
3291ae349f5Scvs2svn   return 0;
3301ae349f5Scvs2svn }
331