arp.c revision 31343
1/* 2 * sys-bsd.c - System-dependent procedures for setting up 3 * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.) 4 * 5 * Copyright (c) 1989 Carnegie Mellon University. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by Carnegie Mellon University. The name of the 14 * University may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 * $Id: arp.c,v 1.17 1997/11/09 06:22:38 brian Exp $ 21 * 22 */ 23 24/* 25 * TODO: 26 */ 27 28#include <sys/types.h> 29#include <sys/time.h> 30#include <sys/socket.h> 31#include <net/if.h> 32#ifdef __FreeBSD__ 33#include <net/if_var.h> 34#endif 35#include <net/route.h> 36#include <net/if_dl.h> 37#include <netinet/in.h> 38#include <net/if_types.h> 39#include <netinet/in_var.h> 40#include <netinet/if_ether.h> 41 42#include <fcntl.h> 43#include <stdio.h> 44#include <string.h> 45#include <sys/errno.h> 46#include <sys/ioctl.h> 47#include <sys/uio.h> 48#include <unistd.h> 49 50#include "command.h" 51#include "mbuf.h" 52#include "log.h" 53#include "id.h" 54#include "arp.h" 55 56static int rtm_seq; 57 58static int get_ether_addr(int, u_long, struct sockaddr_dl *); 59 60/* 61 * SET_SA_FAMILY - set the sa_family field of a struct sockaddr, 62 * if it exists. 63 */ 64#define SET_SA_FAMILY(addr, family) \ 65 memset((char *) &(addr), '\0', sizeof(addr)); \ 66 addr.sa_family = (family); \ 67 addr.sa_len = sizeof(addr); 68 69 70#if RTM_VERSION >= 3 71 72/* 73 * sifproxyarp - Make a proxy ARP entry for the peer. 74 */ 75static struct { 76 struct rt_msghdr hdr; 77 struct sockaddr_inarp dst; 78 struct sockaddr_dl hwa; 79 char extra[128]; 80} arpmsg; 81 82static int arpmsg_valid; 83 84int 85sifproxyarp(int unit, u_long hisaddr) 86{ 87 int routes; 88 89 /* 90 * Get the hardware address of an interface on the same subnet as our local 91 * address. 92 */ 93 memset(&arpmsg, 0, sizeof(arpmsg)); 94 if (!get_ether_addr(unit, hisaddr, &arpmsg.hwa)) { 95 LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n"); 96 return 0; 97 } 98 routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); 99 if (routes < 0) { 100 LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 101 strerror(errno)); 102 return 0; 103 } 104 arpmsg.hdr.rtm_type = RTM_ADD; 105 arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; 106 arpmsg.hdr.rtm_version = RTM_VERSION; 107 arpmsg.hdr.rtm_seq = ++rtm_seq; 108 arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; 109 arpmsg.hdr.rtm_inits = RTV_EXPIRE; 110 arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp); 111 arpmsg.dst.sin_family = AF_INET; 112 arpmsg.dst.sin_addr.s_addr = hisaddr; 113 arpmsg.dst.sin_other = SIN_PROXY; 114 115 arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg 116 + arpmsg.hwa.sdl_len; 117 if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 118 LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno)); 119 close(routes); 120 return 0; 121 } 122 close(routes); 123 arpmsg_valid = 1; 124 return 1; 125} 126 127/* 128 * cifproxyarp - Delete the proxy ARP entry for the peer. 129 */ 130int 131cifproxyarp(int unit, u_long hisaddr) 132{ 133 int routes; 134 135 if (!arpmsg_valid) 136 return 0; 137 arpmsg_valid = 0; 138 139 arpmsg.hdr.rtm_type = RTM_DELETE; 140 arpmsg.hdr.rtm_seq = ++rtm_seq; 141 142 routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); 143 if (routes < 0) { 144 LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 145 strerror(errno)); 146 return 0; 147 } 148 if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 149 LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno)); 150 close(routes); 151 return 0; 152 } 153 close(routes); 154 return 1; 155} 156 157#else /* RTM_VERSION */ 158 159/* 160 * sifproxyarp - Make a proxy ARP entry for the peer. 161 */ 162int 163sifproxyarp(int unit, u_long hisaddr) 164{ 165 struct arpreq arpreq; 166 struct { 167 struct sockaddr_dl sdl; 168 char space[128]; 169 } dls; 170 171 memset(&arpreq, '\0', sizeof(arpreq)); 172 173 /* 174 * Get the hardware address of an interface on the same subnet as our local 175 * address. 176 */ 177 if (!get_ether_addr(unit, hisaddr, &dls.sdl)) { 178 LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n"); 179 return 0; 180 } 181 arpreq.arp_ha.sa_len = sizeof(struct sockaddr); 182 arpreq.arp_ha.sa_family = AF_UNSPEC; 183 memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen); 184 SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 185 ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 186 arpreq.arp_flags = ATF_PERM | ATF_PUBL; 187 if (ID0ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) { 188 LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno)); 189 return 0; 190 } 191 return 1; 192} 193 194/* 195 * cifproxyarp - Delete the proxy ARP entry for the peer. 196 */ 197int 198cifproxyarp(int unit, u_long hisaddr) 199{ 200 struct arpreq arpreq; 201 202 memset(&arpreq, '\0', sizeof(arpreq)); 203 SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 204 ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 205 if (ID0ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) { 206 LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno)); 207 return 0; 208 } 209 return 1; 210} 211 212#endif /* RTM_VERSION */ 213 214 215/* 216 * get_ether_addr - get the hardware address of an interface on the 217 * the same subnet as ipaddr. 218 */ 219#define MAX_IFS 32 220 221static int 222get_ether_addr(int s, u_long ipaddr, struct sockaddr_dl *hwaddr) 223{ 224 struct ifreq *ifr, *ifend, *ifp; 225 u_long ina, mask; 226 struct sockaddr_dl *dla; 227 struct ifreq ifreq; 228 struct ifconf ifc; 229 struct ifreq ifs[MAX_IFS]; 230 231 ifc.ifc_len = sizeof(ifs); 232 ifc.ifc_req = ifs; 233 if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { 234 LogPrintf(LogERROR, "get_ether_addr: ioctl(SIOCGIFCONF): %s\n", 235 strerror(errno)); 236 return 0; 237 } 238 239 /* 240 * Scan through looking for an interface with an Internet address on the 241 * same subnet as `ipaddr'. 242 */ 243 ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); 244 for (ifr = ifc.ifc_req; ifr < ifend;) { 245 if (ifr->ifr_addr.sa_family == AF_INET) { 246 ina = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr.s_addr; 247 strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); 248 ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0'; 249 250 /* 251 * Check that the interface is up, and not point-to-point or loopback. 252 */ 253 if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) 254 continue; 255 if ((ifreq.ifr_flags & 256 (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP)) 257 != (IFF_UP | IFF_BROADCAST)) 258 goto nextif; 259 260 /* 261 * Get its netmask and check that it's on the right subnet. 262 */ 263 if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0) 264 continue; 265 mask = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr.s_addr; 266 if ((ipaddr & mask) != (ina & mask)) 267 goto nextif; 268 269 break; 270 } 271nextif: 272 ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 273 } 274 275 if (ifr >= ifend) 276 return 0; 277 LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", ifr->ifr_name); 278 279 /* 280 * Now scan through again looking for a link-level address for this 281 * interface. 282 */ 283 ifp = ifr; 284 for (ifr = ifc.ifc_req; ifr < ifend;) { 285 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 286 && ifr->ifr_addr.sa_family == AF_LINK) { 287 288 /* 289 * Found the link-level address - copy it out 290 */ 291 dla = (struct sockaddr_dl *) & ifr->ifr_addr; 292 memcpy(hwaddr, dla, dla->sdl_len); 293 return 1; 294 } 295 ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 296 } 297 298 return 0; 299} 300 301 302#ifdef DEBUG 303int 304main() 305{ 306 u_long ipaddr; 307 int s; 308 309 s = socket(AF_INET, SOCK_DGRAM, 0); 310 ipaddr = inet_addr("192.168.1.32"); 311 sifproxyarp(s, ipaddr); 312 close(s); 313} 314#endif 315