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