arp.c revision 31061
16735Samurai/* 26735Samurai * sys-bsd.c - System-dependent procedures for setting up 36735Samurai * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.) 46735Samurai * 56735Samurai * Copyright (c) 1989 Carnegie Mellon University. 66735Samurai * All rights reserved. 76735Samurai * 86735Samurai * Redistribution and use in source and binary forms are permitted 96735Samurai * provided that the above copyright notice and this paragraph are 106735Samurai * duplicated in all such forms and that any documentation, 116735Samurai * advertising materials, and other materials related to such 126735Samurai * distribution and use acknowledge that the software was developed 136735Samurai * by Carnegie Mellon University. The name of the 146735Samurai * University may not be used to endorse or promote products derived 156735Samurai * from this software without specific prior written permission. 166735Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 176735Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 186735Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 198857Srgrimes * 2031061Sbrian * $Id: arp.c,v 1.16 1997/10/26 01:02:03 brian Exp $ 218857Srgrimes * 226735Samurai */ 236735Samurai 246735Samurai/* 256735Samurai * TODO: 266735Samurai */ 276735Samurai 286735Samurai#include <sys/types.h> 2930715Sbrian#include <sys/time.h> 306735Samurai#include <sys/socket.h> 316735Samurai#include <net/if.h> 3221264Swollman#include <net/if_var.h> 336735Samurai#include <net/route.h> 346735Samurai#include <net/if_dl.h> 356735Samurai#include <netinet/in.h> 366735Samurai#include <net/if_types.h> 376735Samurai#include <netinet/in_var.h> 386735Samurai#include <netinet/if_ether.h> 3930715Sbrian 4030715Sbrian#include <fcntl.h> 4130715Sbrian#include <stdio.h> 4230715Sbrian#include <string.h> 4330715Sbrian#include <sys/errno.h> 4430715Sbrian#include <sys/ioctl.h> 4530715Sbrian#include <sys/uio.h> 4630715Sbrian#include <unistd.h> 4730715Sbrian 4830715Sbrian#include "mbuf.h" 4929265Sbrian#include "log.h" 5031061Sbrian#include "id.h" 5130715Sbrian#include "arp.h" 526735Samurai 536735Samuraistatic int rtm_seq; 546735Samurai 5526516Sbrianstatic int get_ether_addr(int, u_long, struct sockaddr_dl *); 5613389Sphk 576735Samurai/* 586735Samurai * SET_SA_FAMILY - set the sa_family field of a struct sockaddr, 596735Samurai * if it exists. 606735Samurai */ 616735Samurai#define SET_SA_FAMILY(addr, family) \ 6230715Sbrian memset((char *) &(addr), '\0', sizeof(addr)); \ 636735Samurai addr.sa_family = (family); \ 646735Samurai addr.sa_len = sizeof(addr); 656735Samurai 666735Samurai 676735Samurai#if RTM_VERSION >= 3 686735Samurai 696735Samurai/* 706735Samurai * sifproxyarp - Make a proxy ARP entry for the peer. 716735Samurai */ 726735Samuraistatic struct { 7328679Sbrian struct rt_msghdr hdr; 7428679Sbrian struct sockaddr_inarp dst; 7528679Sbrian struct sockaddr_dl hwa; 7628679Sbrian char extra[128]; 7730715Sbrian} arpmsg; 786735Samurai 796735Samuraistatic int arpmsg_valid; 806735Samurai 816735Samuraiint 8228679Sbriansifproxyarp(int unit, u_long hisaddr) 836735Samurai{ 8428679Sbrian int routes; 856735Samurai 8628679Sbrian /* 8728679Sbrian * Get the hardware address of an interface on the same subnet as our local 8828679Sbrian * address. 8928679Sbrian */ 9028679Sbrian memset(&arpmsg, 0, sizeof(arpmsg)); 9128679Sbrian if (!get_ether_addr(unit, hisaddr, &arpmsg.hwa)) { 9228974Sbrian LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n"); 9328679Sbrian return 0; 9428679Sbrian } 9531061Sbrian routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); 9631061Sbrian if (routes < 0) { 9728679Sbrian LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 9828679Sbrian strerror(errno)); 9928679Sbrian return 0; 10028679Sbrian } 10128679Sbrian arpmsg.hdr.rtm_type = RTM_ADD; 10228679Sbrian arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; 10328679Sbrian arpmsg.hdr.rtm_version = RTM_VERSION; 10428679Sbrian arpmsg.hdr.rtm_seq = ++rtm_seq; 10528679Sbrian arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; 10628679Sbrian arpmsg.hdr.rtm_inits = RTV_EXPIRE; 10728679Sbrian arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp); 10828679Sbrian arpmsg.dst.sin_family = AF_INET; 10928679Sbrian arpmsg.dst.sin_addr.s_addr = hisaddr; 11028679Sbrian arpmsg.dst.sin_other = SIN_PROXY; 1116735Samurai 11228679Sbrian arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg 11328679Sbrian + arpmsg.hwa.sdl_len; 11428679Sbrian if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 11528679Sbrian LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno)); 1166735Samurai close(routes); 11728679Sbrian return 0; 11828679Sbrian } 11928679Sbrian close(routes); 12028679Sbrian arpmsg_valid = 1; 12128679Sbrian return 1; 1226735Samurai} 1236735Samurai 1246735Samurai/* 1256735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer. 1266735Samurai */ 1276735Samuraiint 12828679Sbriancifproxyarp(int unit, u_long hisaddr) 1296735Samurai{ 13028679Sbrian int routes; 1316735Samurai 13228679Sbrian if (!arpmsg_valid) 13328679Sbrian return 0; 13428679Sbrian arpmsg_valid = 0; 1356735Samurai 13628679Sbrian arpmsg.hdr.rtm_type = RTM_DELETE; 13728679Sbrian arpmsg.hdr.rtm_seq = ++rtm_seq; 1386735Samurai 13931061Sbrian routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); 14031061Sbrian if (routes < 0) { 14128679Sbrian LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 14228679Sbrian strerror(errno)); 14328679Sbrian return 0; 14428679Sbrian } 14528679Sbrian if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 14628679Sbrian LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno)); 1476735Samurai close(routes); 14828679Sbrian return 0; 14928679Sbrian } 15028679Sbrian close(routes); 15128679Sbrian return 1; 1526735Samurai} 1536735Samurai 15428679Sbrian#else /* RTM_VERSION */ 1556735Samurai 1566735Samurai/* 1576735Samurai * sifproxyarp - Make a proxy ARP entry for the peer. 1586735Samurai */ 1596735Samuraiint 16028679Sbriansifproxyarp(int unit, u_long hisaddr) 1616735Samurai{ 16228679Sbrian struct arpreq arpreq; 16328679Sbrian struct { 16428679Sbrian struct sockaddr_dl sdl; 16528679Sbrian char space[128]; 16628679Sbrian } dls; 1676735Samurai 16830715Sbrian memset(&arpreq, '\0', sizeof(arpreq)); 1696735Samurai 17028679Sbrian /* 17128679Sbrian * Get the hardware address of an interface on the same subnet as our local 17228679Sbrian * address. 17328679Sbrian */ 17428679Sbrian if (!get_ether_addr(unit, hisaddr, &dls.sdl)) { 17528679Sbrian LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n"); 17628679Sbrian return 0; 17728679Sbrian } 17828679Sbrian arpreq.arp_ha.sa_len = sizeof(struct sockaddr); 17928679Sbrian arpreq.arp_ha.sa_family = AF_UNSPEC; 18030715Sbrian memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen); 18128679Sbrian SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 18228679Sbrian ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 18328679Sbrian arpreq.arp_flags = ATF_PERM | ATF_PUBL; 18431061Sbrian if (ID0ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) { 18528974Sbrian LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno)); 18628679Sbrian return 0; 18728679Sbrian } 18828679Sbrian return 1; 1896735Samurai} 1906735Samurai 1916735Samurai/* 1926735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer. 1936735Samurai */ 1946735Samuraiint 19528679Sbriancifproxyarp(int unit, u_long hisaddr) 1966735Samurai{ 19728679Sbrian struct arpreq arpreq; 1986735Samurai 19930715Sbrian memset(&arpreq, '\0', sizeof(arpreq)); 20028679Sbrian SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 20128679Sbrian ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 20231061Sbrian if (ID0ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) { 20328974Sbrian LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno)); 20428679Sbrian return 0; 20528679Sbrian } 20628679Sbrian return 1; 2076735Samurai} 2086735Samurai 20928679Sbrian#endif /* RTM_VERSION */ 2106735Samurai 21128679Sbrian 2126735Samurai/* 2136735Samurai * get_ether_addr - get the hardware address of an interface on the 2146735Samurai * the same subnet as ipaddr. 2156735Samurai */ 2166735Samurai#define MAX_IFS 32 2176735Samurai 21830715Sbrianstatic int 21930715Sbrianget_ether_addr(int s, u_long ipaddr, struct sockaddr_dl *hwaddr) 2206735Samurai{ 22128679Sbrian struct ifreq *ifr, *ifend, *ifp; 22228679Sbrian u_long ina, mask; 22328679Sbrian struct sockaddr_dl *dla; 22428679Sbrian struct ifreq ifreq; 22528679Sbrian struct ifconf ifc; 22628679Sbrian struct ifreq ifs[MAX_IFS]; 2276735Samurai 22828679Sbrian ifc.ifc_len = sizeof(ifs); 22928679Sbrian ifc.ifc_req = ifs; 23028679Sbrian if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { 23128974Sbrian LogPrintf(LogERROR, "get_ether_addr: ioctl(SIOCGIFCONF): %s\n", 23228974Sbrian strerror(errno)); 23328679Sbrian return 0; 23428679Sbrian } 2356735Samurai 23628679Sbrian /* 23728679Sbrian * Scan through looking for an interface with an Internet address on the 23828679Sbrian * same subnet as `ipaddr'. 23928679Sbrian */ 24028679Sbrian ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); 24128679Sbrian for (ifr = ifc.ifc_req; ifr < ifend;) { 24228679Sbrian if (ifr->ifr_addr.sa_family == AF_INET) { 24328679Sbrian ina = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr.s_addr; 24428679Sbrian strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); 24528679Sbrian ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0'; 2466735Samurai 24728679Sbrian /* 24828679Sbrian * Check that the interface is up, and not point-to-point or loopback. 24928679Sbrian */ 25028679Sbrian if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) 25128679Sbrian continue; 25228679Sbrian if ((ifreq.ifr_flags & 25328679Sbrian (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP)) 25428679Sbrian != (IFF_UP | IFF_BROADCAST)) 25528679Sbrian goto nextif; 25628679Sbrian 25728679Sbrian /* 25828679Sbrian * Get its netmask and check that it's on the right subnet. 25928679Sbrian */ 26028679Sbrian if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0) 26128679Sbrian continue; 26228679Sbrian mask = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr.s_addr; 26328679Sbrian if ((ipaddr & mask) != (ina & mask)) 26428679Sbrian goto nextif; 26528679Sbrian 26628679Sbrian break; 26728679Sbrian } 2686735Samurainextif: 26928679Sbrian ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 27028679Sbrian } 2716735Samurai 27228679Sbrian if (ifr >= ifend) 27328679Sbrian return 0; 27428679Sbrian LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", ifr->ifr_name); 2756735Samurai 27628679Sbrian /* 27728679Sbrian * Now scan through again looking for a link-level address for this 27828679Sbrian * interface. 27928679Sbrian */ 28028679Sbrian ifp = ifr; 28128679Sbrian for (ifr = ifc.ifc_req; ifr < ifend;) { 28228679Sbrian if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 28328679Sbrian && ifr->ifr_addr.sa_family == AF_LINK) { 28428679Sbrian 28528679Sbrian /* 28628679Sbrian * Found the link-level address - copy it out 28728679Sbrian */ 28828679Sbrian dla = (struct sockaddr_dl *) & ifr->ifr_addr; 28930715Sbrian memcpy(hwaddr, dla, dla->sdl_len); 29028679Sbrian return 1; 2916735Samurai } 29228679Sbrian ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 29328679Sbrian } 2946735Samurai 29528679Sbrian return 0; 2966735Samurai} 2976735Samurai 2986735Samurai 29930715Sbrian#ifdef DEBUG 3006735Samuraiint 3016735Samuraimain() 3026735Samurai{ 30328679Sbrian u_long ipaddr; 30428679Sbrian int s; 3056735Samurai 30628679Sbrian s = socket(AF_INET, SOCK_DGRAM, 0); 30728679Sbrian ipaddr = inet_addr("192.168.1.32"); 30828679Sbrian sifproxyarp(s, ipaddr); 30928679Sbrian close(s); 3106735Samurai} 3116735Samurai#endif 312