arp.c revision 31962
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 * 2031962Sbrian * $Id: arp.c,v 1.19 1997/12/23 22:38:51 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> 3231343Sbrian#ifdef __FreeBSD__ 3321264Swollman#include <net/if_var.h> 3431343Sbrian#endif 356735Samurai#include <net/route.h> 366735Samurai#include <net/if_dl.h> 376735Samurai#include <netinet/in.h> 386735Samurai#include <net/if_types.h> 396735Samurai#include <netinet/in_var.h> 406735Samurai#include <netinet/if_ether.h> 4130715Sbrian 4230715Sbrian#include <fcntl.h> 4330715Sbrian#include <stdio.h> 4430715Sbrian#include <string.h> 4530715Sbrian#include <sys/errno.h> 4630715Sbrian#include <sys/ioctl.h> 4730715Sbrian#include <sys/uio.h> 4830715Sbrian#include <unistd.h> 4930715Sbrian 5031343Sbrian#include "command.h" 5130715Sbrian#include "mbuf.h" 5229265Sbrian#include "log.h" 5331061Sbrian#include "id.h" 5430715Sbrian#include "arp.h" 556735Samurai 566735Samuraistatic int rtm_seq; 576735Samurai 5826516Sbrianstatic int get_ether_addr(int, u_long, struct sockaddr_dl *); 5913389Sphk 606735Samurai/* 616735Samurai * SET_SA_FAMILY - set the sa_family field of a struct sockaddr, 626735Samurai * if it exists. 636735Samurai */ 646735Samurai#define SET_SA_FAMILY(addr, family) \ 6530715Sbrian memset((char *) &(addr), '\0', sizeof(addr)); \ 666735Samurai addr.sa_family = (family); \ 676735Samurai addr.sa_len = sizeof(addr); 686735Samurai 696735Samurai 706735Samurai#if RTM_VERSION >= 3 716735Samurai 726735Samurai/* 736735Samurai * sifproxyarp - Make a proxy ARP entry for the peer. 746735Samurai */ 756735Samuraistatic struct { 7628679Sbrian struct rt_msghdr hdr; 7728679Sbrian struct sockaddr_inarp dst; 7828679Sbrian struct sockaddr_dl hwa; 7928679Sbrian char extra[128]; 8030715Sbrian} arpmsg; 816735Samurai 826735Samuraistatic int arpmsg_valid; 836735Samurai 846735Samuraiint 8528679Sbriansifproxyarp(int unit, u_long hisaddr) 866735Samurai{ 8728679Sbrian int routes; 886735Samurai 8928679Sbrian /* 9028679Sbrian * Get the hardware address of an interface on the same subnet as our local 9128679Sbrian * address. 9228679Sbrian */ 9331962Sbrian memset(&arpmsg, 0, sizeof arpmsg); 9428679Sbrian if (!get_ether_addr(unit, hisaddr, &arpmsg.hwa)) { 9528974Sbrian LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n"); 9628679Sbrian return 0; 9728679Sbrian } 9831061Sbrian routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); 9931061Sbrian if (routes < 0) { 10028679Sbrian LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 10128679Sbrian strerror(errno)); 10228679Sbrian return 0; 10328679Sbrian } 10428679Sbrian arpmsg.hdr.rtm_type = RTM_ADD; 10528679Sbrian arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; 10628679Sbrian arpmsg.hdr.rtm_version = RTM_VERSION; 10728679Sbrian arpmsg.hdr.rtm_seq = ++rtm_seq; 10828679Sbrian arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; 10928679Sbrian arpmsg.hdr.rtm_inits = RTV_EXPIRE; 11028679Sbrian arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp); 11128679Sbrian arpmsg.dst.sin_family = AF_INET; 11228679Sbrian arpmsg.dst.sin_addr.s_addr = hisaddr; 11328679Sbrian arpmsg.dst.sin_other = SIN_PROXY; 1146735Samurai 11528679Sbrian arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg 11628679Sbrian + arpmsg.hwa.sdl_len; 11728679Sbrian if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 11828679Sbrian LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno)); 1196735Samurai close(routes); 12028679Sbrian return 0; 12128679Sbrian } 12228679Sbrian close(routes); 12328679Sbrian arpmsg_valid = 1; 12428679Sbrian return 1; 1256735Samurai} 1266735Samurai 1276735Samurai/* 1286735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer. 1296735Samurai */ 1306735Samuraiint 13128679Sbriancifproxyarp(int unit, u_long hisaddr) 1326735Samurai{ 13328679Sbrian int routes; 1346735Samurai 13528679Sbrian if (!arpmsg_valid) 13628679Sbrian return 0; 13728679Sbrian arpmsg_valid = 0; 1386735Samurai 13928679Sbrian arpmsg.hdr.rtm_type = RTM_DELETE; 14028679Sbrian arpmsg.hdr.rtm_seq = ++rtm_seq; 1416735Samurai 14231061Sbrian routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); 14331061Sbrian if (routes < 0) { 14428679Sbrian LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 14528679Sbrian strerror(errno)); 14628679Sbrian return 0; 14728679Sbrian } 14828679Sbrian if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 14928679Sbrian LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno)); 1506735Samurai close(routes); 15128679Sbrian return 0; 15228679Sbrian } 15328679Sbrian close(routes); 15428679Sbrian return 1; 1556735Samurai} 1566735Samurai 15728679Sbrian#else /* RTM_VERSION */ 1586735Samurai 1596735Samurai/* 1606735Samurai * sifproxyarp - Make a proxy ARP entry for the peer. 1616735Samurai */ 1626735Samuraiint 16328679Sbriansifproxyarp(int unit, u_long hisaddr) 1646735Samurai{ 16528679Sbrian struct arpreq arpreq; 16628679Sbrian struct { 16728679Sbrian struct sockaddr_dl sdl; 16828679Sbrian char space[128]; 16928679Sbrian } dls; 1706735Samurai 17131962Sbrian memset(&arpreq, '\0', sizeof arpreq); 1726735Samurai 17328679Sbrian /* 17428679Sbrian * Get the hardware address of an interface on the same subnet as our local 17528679Sbrian * address. 17628679Sbrian */ 17728679Sbrian if (!get_ether_addr(unit, hisaddr, &dls.sdl)) { 17828679Sbrian LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n"); 17928679Sbrian return 0; 18028679Sbrian } 18128679Sbrian arpreq.arp_ha.sa_len = sizeof(struct sockaddr); 18228679Sbrian arpreq.arp_ha.sa_family = AF_UNSPEC; 18330715Sbrian memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen); 18428679Sbrian SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 18528679Sbrian ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 18628679Sbrian arpreq.arp_flags = ATF_PERM | ATF_PUBL; 18731061Sbrian if (ID0ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) { 18828974Sbrian LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno)); 18928679Sbrian return 0; 19028679Sbrian } 19128679Sbrian return 1; 1926735Samurai} 1936735Samurai 1946735Samurai/* 1956735Samurai * cifproxyarp - Delete the proxy ARP entry for the peer. 1966735Samurai */ 1976735Samuraiint 19828679Sbriancifproxyarp(int unit, u_long hisaddr) 1996735Samurai{ 20028679Sbrian struct arpreq arpreq; 2016735Samurai 20231962Sbrian memset(&arpreq, '\0', sizeof arpreq); 20328679Sbrian SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 20428679Sbrian ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 20531061Sbrian if (ID0ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) { 20628974Sbrian LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno)); 20728679Sbrian return 0; 20828679Sbrian } 20928679Sbrian return 1; 2106735Samurai} 2116735Samurai 21228679Sbrian#endif /* RTM_VERSION */ 2136735Samurai 21428679Sbrian 2156735Samurai/* 2166735Samurai * get_ether_addr - get the hardware address of an interface on the 2176735Samurai * the same subnet as ipaddr. 2186735Samurai */ 2196735Samurai#define MAX_IFS 32 2206735Samurai 22130715Sbrianstatic int 22230715Sbrianget_ether_addr(int s, u_long ipaddr, struct sockaddr_dl *hwaddr) 2236735Samurai{ 22428679Sbrian struct ifreq *ifr, *ifend, *ifp; 22528679Sbrian u_long ina, mask; 22628679Sbrian struct sockaddr_dl *dla; 22728679Sbrian struct ifreq ifreq; 22828679Sbrian struct ifconf ifc; 22928679Sbrian struct ifreq ifs[MAX_IFS]; 2306735Samurai 23131962Sbrian ifc.ifc_len = sizeof ifs; 23228679Sbrian ifc.ifc_req = ifs; 23328679Sbrian if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { 23428974Sbrian LogPrintf(LogERROR, "get_ether_addr: ioctl(SIOCGIFCONF): %s\n", 23528974Sbrian strerror(errno)); 23628679Sbrian return 0; 23728679Sbrian } 2386735Samurai 23928679Sbrian /* 24028679Sbrian * Scan through looking for an interface with an Internet address on the 24128679Sbrian * same subnet as `ipaddr'. 24228679Sbrian */ 24328679Sbrian ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); 24428679Sbrian for (ifr = ifc.ifc_req; ifr < ifend;) { 24528679Sbrian if (ifr->ifr_addr.sa_family == AF_INET) { 24628679Sbrian ina = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr.s_addr; 24731962Sbrian strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof ifreq.ifr_name - 1); 24831962Sbrian ifreq.ifr_name[sizeof ifreq.ifr_name - 1] = '\0'; 2496735Samurai 25028679Sbrian /* 25128679Sbrian * Check that the interface is up, and not point-to-point or loopback. 25228679Sbrian */ 25328679Sbrian if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) 25428679Sbrian continue; 25528679Sbrian if ((ifreq.ifr_flags & 25628679Sbrian (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP)) 25728679Sbrian != (IFF_UP | IFF_BROADCAST)) 25828679Sbrian goto nextif; 25928679Sbrian 26028679Sbrian /* 26128679Sbrian * Get its netmask and check that it's on the right subnet. 26228679Sbrian */ 26328679Sbrian if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0) 26428679Sbrian continue; 26528679Sbrian mask = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr.s_addr; 26628679Sbrian if ((ipaddr & mask) != (ina & mask)) 26728679Sbrian goto nextif; 26828679Sbrian 26928679Sbrian break; 27028679Sbrian } 2716735Samurainextif: 27228679Sbrian ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 27328679Sbrian } 2746735Samurai 27528679Sbrian if (ifr >= ifend) 27628679Sbrian return 0; 27728679Sbrian LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", ifr->ifr_name); 2786735Samurai 27928679Sbrian /* 28028679Sbrian * Now scan through again looking for a link-level address for this 28128679Sbrian * interface. 28228679Sbrian */ 28328679Sbrian ifp = ifr; 28428679Sbrian for (ifr = ifc.ifc_req; ifr < ifend;) { 28528679Sbrian if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 28628679Sbrian && ifr->ifr_addr.sa_family == AF_LINK) { 28728679Sbrian 28828679Sbrian /* 28928679Sbrian * Found the link-level address - copy it out 29028679Sbrian */ 29128679Sbrian dla = (struct sockaddr_dl *) & ifr->ifr_addr; 29230715Sbrian memcpy(hwaddr, dla, dla->sdl_len); 29328679Sbrian return 1; 2946735Samurai } 29528679Sbrian ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 29628679Sbrian } 2976735Samurai 29828679Sbrian return 0; 2996735Samurai} 3006735Samurai 3016735Samurai 30230715Sbrian#ifdef DEBUG 3036735Samuraiint 3046735Samuraimain() 3056735Samurai{ 30628679Sbrian u_long ipaddr; 30728679Sbrian int s; 3086735Samurai 30928679Sbrian s = socket(AF_INET, SOCK_DGRAM, 0); 31028679Sbrian ipaddr = inet_addr("192.168.1.32"); 31128679Sbrian sifproxyarp(s, ipaddr); 31228679Sbrian close(s); 3136735Samurai} 3146735Samurai#endif 315