arp.c revision 29265
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.14 1997/08/31 22:59:12 brian Exp $ 21 * 22 */ 23 24/* 25 * TODO: 26 */ 27 28#include <sys/ioctl.h> 29#include <sys/types.h> 30#include <sys/uio.h> 31#include <sys/socket.h> 32#include <sys/time.h> 33#include <sys/errno.h> 34#include <unistd.h> 35#include <string.h> 36 37#include <net/if.h> 38#include <net/if_var.h> 39#include <net/route.h> 40#include <net/if_dl.h> 41#include <netinet/in.h> 42#include <stdio.h> 43#include <fcntl.h> 44#ifdef __bsdi__ 45#include <kvm.h> 46#endif 47#include <net/if_types.h> 48#include <netinet/in_var.h> 49#if RTM_VERSION >= 3 50#include <netinet/if_ether.h> 51#endif 52#include "log.h" 53 54static int rtm_seq; 55 56static int get_ether_addr(int, u_long, struct sockaddr_dl *); 57 58#define BCOPY(s, d, l) memcpy(d, s, l) 59#define BZERO(s, n) memset(s, 0, n) 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 BZERO((char *) &(addr), 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 if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { 99 LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 100 strerror(errno)); 101 return 0; 102 } 103 arpmsg.hdr.rtm_type = RTM_ADD; 104 arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; 105 arpmsg.hdr.rtm_version = RTM_VERSION; 106 arpmsg.hdr.rtm_seq = ++rtm_seq; 107 arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; 108 arpmsg.hdr.rtm_inits = RTV_EXPIRE; 109 arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp); 110 arpmsg.dst.sin_family = AF_INET; 111 arpmsg.dst.sin_addr.s_addr = hisaddr; 112 arpmsg.dst.sin_other = SIN_PROXY; 113 114 arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg 115 + arpmsg.hwa.sdl_len; 116 if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 117 LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno)); 118 close(routes); 119 return 0; 120 } 121 close(routes); 122 arpmsg_valid = 1; 123 return 1; 124} 125 126/* 127 * cifproxyarp - Delete the proxy ARP entry for the peer. 128 */ 129int 130cifproxyarp(int unit, u_long hisaddr) 131{ 132 int routes; 133 134 if (!arpmsg_valid) 135 return 0; 136 arpmsg_valid = 0; 137 138 arpmsg.hdr.rtm_type = RTM_DELETE; 139 arpmsg.hdr.rtm_seq = ++rtm_seq; 140 141 if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { 142 LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n", 143 strerror(errno)); 144 return 0; 145 } 146 if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { 147 LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno)); 148 close(routes); 149 return 0; 150 } 151 close(routes); 152 return 1; 153} 154 155#else /* RTM_VERSION */ 156 157/* 158 * sifproxyarp - Make a proxy ARP entry for the peer. 159 */ 160int 161sifproxyarp(int unit, u_long hisaddr) 162{ 163 struct arpreq arpreq; 164 struct { 165 struct sockaddr_dl sdl; 166 char space[128]; 167 } dls; 168 169 BZERO(&arpreq, sizeof(arpreq)); 170 171 /* 172 * Get the hardware address of an interface on the same subnet as our local 173 * address. 174 */ 175 if (!get_ether_addr(unit, hisaddr, &dls.sdl)) { 176 LogPrintf(LOG_PHASE_BIT, "Cannot determine ethernet address for proxy ARP\n"); 177 return 0; 178 } 179 arpreq.arp_ha.sa_len = sizeof(struct sockaddr); 180 arpreq.arp_ha.sa_family = AF_UNSPEC; 181 BCOPY(LLADDR(&dls.sdl), arpreq.arp_ha.sa_data, dls.sdl.sdl_alen); 182 SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 183 ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 184 arpreq.arp_flags = ATF_PERM | ATF_PUBL; 185 if (ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) { 186 LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno)); 187 return 0; 188 } 189 return 1; 190} 191 192/* 193 * cifproxyarp - Delete the proxy ARP entry for the peer. 194 */ 195int 196cifproxyarp(int unit, u_long hisaddr) 197{ 198 struct arpreq arpreq; 199 200 BZERO(&arpreq, sizeof(arpreq)); 201 SET_SA_FAMILY(arpreq.arp_pa, AF_INET); 202 ((struct sockaddr_in *) & arpreq.arp_pa)->sin_addr.s_addr = hisaddr; 203 if (ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) { 204 LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno)); 205 return 0; 206 } 207 return 1; 208} 209 210#endif /* RTM_VERSION */ 211 212 213/* 214 * get_ether_addr - get the hardware address of an interface on the 215 * the same subnet as ipaddr. 216 */ 217#define MAX_IFS 32 218 219int 220get_ether_addr(int s, u_long ipaddr, struct sockaddr_dl * hwaddr) 221{ 222 struct ifreq *ifr, *ifend, *ifp; 223 u_long ina, mask; 224 struct sockaddr_dl *dla; 225 struct ifreq ifreq; 226 struct ifconf ifc; 227 struct ifreq ifs[MAX_IFS]; 228 229 ifc.ifc_len = sizeof(ifs); 230 ifc.ifc_req = ifs; 231 if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { 232 LogPrintf(LogERROR, "get_ether_addr: ioctl(SIOCGIFCONF): %s\n", 233 strerror(errno)); 234 return 0; 235 } 236 237 /* 238 * Scan through looking for an interface with an Internet address on the 239 * same subnet as `ipaddr'. 240 */ 241 ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); 242 for (ifr = ifc.ifc_req; ifr < ifend;) { 243 if (ifr->ifr_addr.sa_family == AF_INET) { 244 ina = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr.s_addr; 245 strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); 246 ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0'; 247 248 /* 249 * Check that the interface is up, and not point-to-point or loopback. 250 */ 251 if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) 252 continue; 253 if ((ifreq.ifr_flags & 254 (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP)) 255 != (IFF_UP | IFF_BROADCAST)) 256 goto nextif; 257 258 /* 259 * Get its netmask and check that it's on the right subnet. 260 */ 261 if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0) 262 continue; 263 mask = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr.s_addr; 264 if ((ipaddr & mask) != (ina & mask)) 265 goto nextif; 266 267 break; 268 } 269nextif: 270 ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 271 } 272 273 if (ifr >= ifend) 274 return 0; 275 LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", ifr->ifr_name); 276 277 /* 278 * Now scan through again looking for a link-level address for this 279 * interface. 280 */ 281 ifp = ifr; 282 for (ifr = ifc.ifc_req; ifr < ifend;) { 283 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 284 && ifr->ifr_addr.sa_family == AF_LINK) { 285 286 /* 287 * Found the link-level address - copy it out 288 */ 289 dla = (struct sockaddr_dl *) & ifr->ifr_addr; 290#ifdef __bsdi__ 291 if (dla->sdl_alen == 0) 292 kmemgetether(ifr->ifr_name, dla); 293#endif 294 BCOPY(dla, hwaddr, dla->sdl_len); 295 return 1; 296 } 297 ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len); 298 } 299 300 return 0; 301} 302 303#ifdef __bsdi__ 304#include <nlist.h> 305 306struct nlist nl[] = { 307#define N_IFNET 0 308 {"_ifnet"}, 309 "", 310}; 311 312 313kvm_t *kvmd; 314 315/* 316 * Read kernel memory, return 0 on success. 317 */ 318int 319kread(u_long addr, char *buf, int size) 320{ 321 if (kvm_read(kvmd, addr, buf, size) != size) { 322 /* XXX this duplicates kvm_read's error printout */ 323 LogPrintf(LogERROR, "kvm_read %s\n", kvm_geterr(kvmd)); 324 return -1; 325 } 326 return 0; 327} 328 329void 330kmemgetether(char *ifname, struct sockaddr_dl * dlo) 331{ 332 struct ifnet ifnet; 333 int n; 334 u_long addr, ifaddraddr, ifnetfound, ifaddrfound; 335 char name[16 + 32]; 336 struct sockaddr *sa; 337 char *cp; 338 struct sockaddr_dl *sdl; 339 union { 340 struct ifaddr ifa; 341 struct in_ifaddr in; 342 } ifaddr; 343 struct arpcom ac; 344 345 kvmd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); 346 if (kvmd) { 347 n = kvm_nlist(kvmd, nl); 348 if (n >= 0) { 349 addr = nl[N_IFNET].n_value; 350 kread(addr, (char *) &addr, sizeof(addr)); 351 ifaddraddr = ifnetfound = 0; 352 while (addr || ifaddraddr) { 353 ifnetfound = addr; 354 if (ifaddraddr == 0) { 355 if (kread(addr, (char *) &ifnet, sizeof(ifnet)) || 356 kread((u_long) ifnet.if_name, name, 16)) 357 return; 358 name[15] = 0; 359 addr = (u_long) ifnet.if_next; 360 cp = (char *) index(name, '\0'); 361 cp += sprintf(cp, "%d", ifnet.if_unit); 362 *cp = '\0'; 363 ifaddraddr = (u_long) ifnet.if_addrlist; 364 } 365 ifaddrfound = ifaddraddr; 366 if (ifaddraddr) { 367 if (kread(ifaddraddr, (char *) &ifaddr, sizeof ifaddr)) { 368 ifaddraddr = 0; 369 continue; 370 } 371#define CP(x) ((char *)(x)) 372 cp = (CP(ifaddr.ifa.ifa_addr) - CP(ifaddraddr)) + CP(&ifaddr); 373 sa = (struct sockaddr *) cp; 374 if (sa->sa_family == AF_LINK && strcmp(ifname, name) == 0) { 375 sdl = (struct sockaddr_dl *) sa; 376 cp = (char *) LLADDR(sdl); 377 n = sdl->sdl_alen; 378 if (ifnet.if_type == IFT_ETHER) { 379 if (n == 0) { 380 kread(ifnetfound, (char *) &ac, sizeof(ac)); 381 cp = (char *) LLADDR(sdl); 382 bcopy((char *) ac.ac_enaddr, cp, 6); 383 sdl->sdl_alen = 6; 384 } 385 bcopy(sdl, dlo, sizeof(*sdl)); 386 return; 387 } 388 } 389 ifaddraddr = (u_long) ifaddr.ifa.ifa_next; 390 } 391 } 392 } 393 } 394} 395 396#endif 397 398#ifdef DEBUG 399main() 400{ 401 u_long ipaddr; 402 int s; 403 404 s = socket(AF_INET, SOCK_DGRAM, 0); 405 ipaddr = inet_addr("192.168.1.32"); 406 sifproxyarp(s, ipaddr); 407 close(s); 408} 409 410#endif 411