1/* $NetBSD: wire.c,v 1.1.1.2 2009/03/20 20:26:56 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997-2009 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/libamu/wire.c 43 * 44 */ 45 46/* 47 * This function returns the subnet (address&netmask) for the primary network 48 * interface. If the resulting address has an entry in the hosts file, the 49 * corresponding name is returned, otherwise the address is returned in 50 * standard internet format. 51 * As a side-effect, a list of local IP/net address is recorded for use 52 * by the islocalnet() function. 53 * 54 * Derived from original by Paul Anderson (23/4/90) 55 * Updates from Dirk Grunwald (11/11/91) 56 */ 57 58#ifdef HAVE_CONFIG_H 59# include <config.h> 60#endif /* HAVE_CONFIG_H */ 61#include <am_defs.h> 62#include <amu.h> 63 64 65#ifdef HAVE_IFADDRS_H 66#include <ifaddrs.h> 67#endif /* HAVE_IFADDRS_H */ 68 69#ifdef HAVE_IRS_H 70# include <irs.h> 71#endif /* HAVE_IRS_H */ 72 73/* 74 * List of locally connected networks 75 */ 76typedef struct addrlist addrlist; 77struct addrlist { 78 addrlist *ip_next; 79 u_long ip_addr; /* address of network */ 80 u_long ip_mask; 81 char *ip_net_num; /* number of network */ 82 char *ip_net_name; /* name of network */ 83}; 84static addrlist *localnets = NULL; 85 86#if defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK) 87# define IFF_LOOPBACK IFF_LOCAL_LOOPBACK 88#endif /* defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK) */ 89 90#define C(x) ((x) & 0xff) 91#define GFBUFLEN 1024 92#define S2IN(s) (((struct sockaddr_in *)(s))->sin_addr.s_addr) 93 94 95/* return malloc'ed buffer. caller must free it */ 96char * 97print_wires(void) 98{ 99 addrlist *al; 100 char s[256]; 101 int i; 102 char *buf; 103 int bufcount = 0; 104 int buf_size = 1024; 105 106 buf = SALLOC(buf_size); /* initial allocation (may grow!) */ 107 if (!buf) 108 return NULL; 109 110 if (!localnets) { 111 xstrlcpy(buf, "No networks\n", buf_size); 112 return buf; 113 } 114 /* check if there's more than one network */ 115 if (!localnets->ip_next) { 116 /* use buf_size for sizeof(buf) because of the realloc() below */ 117 xsnprintf(buf, buf_size, 118 "Network: wire=\"%s\" (netnumber=%s).\n", 119 localnets->ip_net_name, localnets->ip_net_num); 120 return buf; 121 } 122 buf[0] = '\0'; /* null out buffer before appending */ 123 for (i = 1, al = localnets; al; al = al->ip_next, i++) { 124 xsnprintf(s, sizeof(s), "Network %d: wire=\"%s\" (netnumber=%s).\n", 125 i, al->ip_net_name, al->ip_net_num); 126 bufcount += strlen(s); 127 if (bufcount > buf_size) { 128 buf_size *= 2; 129 buf = xrealloc(buf, buf_size); 130 } 131 xstrlcat(buf, s, buf_size); 132 } 133 return buf; 134} 135 136 137static struct addrlist * 138getwire_lookup(u_long address, u_long netmask, int ishost) 139{ 140 struct addrlist *al; 141 u_long subnet; 142 char netNumberBuf[64]; 143 char buf[GFBUFLEN], *s; 144#ifdef HAVE_IRS_H 145 struct nwent *np; 146#else /* not HAVE_IRS_H */ 147 struct netent *np; 148#endif /* not HAVE_IRS_H */ 149 150 /* 151 * Add interface to local network singly linked list 152 */ 153 al = ALLOC(struct addrlist); 154 al->ip_addr = address; 155 al->ip_mask = netmask; 156 al->ip_net_name = NO_SUBNET; /* fill in a bit later */ 157 al->ip_net_num = "0.0.0.0"; /* fill in a bit later */ 158 al->ip_next = NULL; 159 160 subnet = ntohl(address) & ntohl(netmask); 161 162 if (ishost) 163 np = NULL; 164 else { 165#ifdef HAVE_IRS_H 166 u_long mask = ntohl(netmask); 167 static struct irs_acc *irs_gen; 168 static struct irs_nw *irs_nw; 169 u_long net; 170 int maskbits; 171 u_char addr[4]; 172 173 if (irs_gen == NULL) 174#ifdef irs_irp_acc 175 /* 176 * bsdi4 added another argument to this function, without changing 177 * its name. The irs_irp_acc is the one (hacky) distinguishing 178 * feature found in <irs.h> that can differentiate between bsdi3 and 179 * bsdi4. 180 */ 181 irs_gen = irs_gen_acc("", NULL); 182#else /* not irs_irp_acc */ 183 irs_gen = irs_gen_acc(""); 184#endif /* not irs_irp_acc */ 185 if (irs_gen && irs_nw == NULL) 186 irs_nw = (*irs_gen->nw_map)(irs_gen); 187 net = ntohl(address) & (mask = ntohl(netmask)); 188 addr[0] = (0xFF000000 & net) >> 24; 189 addr[1] = (0x00FF0000 & net) >> 16; 190 addr[2] = (0x0000FF00 & net) >> 8; 191 addr[3] = (0x000000FF & net); 192 for (maskbits = 32; !(mask & 1); mask >>= 1) 193 maskbits--; 194 np = (*irs_nw->byaddr)(irs_nw, addr, maskbits, AF_INET); 195#else /* not HAVE_IRS_H */ 196 np = getnetbyaddr(subnet, AF_INET); 197 /* 198 * Some systems (IRIX 6.4) cannot getnetbyaddr on networks such as 199 * "128.59.16.0". Instead, they need to look for the short form of 200 * the network, "128.59.16". So if the first getnetbyaddr failed, we 201 * shift the subnet way from zeros and try again. 202 */ 203 if (!np) { 204 u_long short_subnet = subnet; 205 while (short_subnet && (short_subnet & 0x000000ff) == 0) 206 short_subnet >>= 8; 207 np = getnetbyaddr(short_subnet, AF_INET); 208 if (np) 209 plog(XLOG_WARNING, "getnetbyaddr failed on 0x%x, succeeded on 0x%x", 210 (u_int) subnet, (u_int) short_subnet); 211 } 212#endif /* not HAVE_IRS_H */ 213 } 214 215 if ((subnet & 0xffffff) == 0) { 216 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu", C(subnet >> 24)); 217 } else if ((subnet & 0xffff) == 0) { 218 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu.%lu", 219 C(subnet >> 24), C(subnet >> 16)); 220 } else if ((subnet & 0xff) == 0) { 221 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu.%lu.%lu", 222 C(subnet >> 24), C(subnet >> 16), 223 C(subnet >> 8)); 224 } else { 225 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu.%lu.%lu.%lu", 226 C(subnet >> 24), C(subnet >> 16), 227 C(subnet >> 8), C(subnet)); 228 } 229 230 /* fill in network number (string) */ 231 al->ip_net_num = strdup(netNumberBuf); 232 233 if (np != NULL) 234 s = np->n_name; 235 else { 236 struct hostent *hp; 237 238 subnet = address & netmask; 239 hp = gethostbyaddr((char *) &subnet, 4, AF_INET); 240 if (hp != NULL) 241 s = (char *) hp->h_name; 242 else 243 s = inet_dquad(buf, sizeof(buf), subnet); 244 } 245 246 /* fill in network name (string) */ 247 al->ip_net_name = strdup(s); 248 /* Let's be cautious here about buffer overflows -Ion */ 249 if (strlen(s) > MAXHOSTNAMELEN) { 250 al->ip_net_name[MAXHOSTNAMELEN] = '\0'; 251 plog(XLOG_WARNING, "Long hostname %s truncated to %d characters", 252 s, MAXHOSTNAMELEN); 253 } 254 255 return (al); 256} 257 258 259/* 260 * Make a dotted quad from a 32bit IP address 261 * addr is in network byte order. 262 * sizeof(buf) needs to be at least 16. 263 */ 264char * 265inet_dquad(char *buf, size_t l, u_long addr) 266{ 267 addr = ntohl(addr); 268 xsnprintf(buf, l, "%ld.%ld.%ld.%ld", 269 ((addr >> 24) & 0xff), 270 ((addr >> 16) & 0xff), 271 ((addr >> 8) & 0xff), 272 ((addr >> 0) & 0xff)); 273 return buf; 274} 275 276 277/* 278 * Determine whether a network is on a local network 279 * (addr) is in network byte order. 280 */ 281int 282islocalnet(u_long addr) 283{ 284 addrlist *al; 285 286 for (al = localnets; al; al = al->ip_next) 287 if (((addr ^ al->ip_addr) & al->ip_mask) == 0) 288 return TRUE; 289 290#ifdef DEBUG 291 { 292 char buf[16]; 293 plog(XLOG_INFO, "%s is on a remote network", 294 inet_dquad(buf, sizeof(buf), addr)); 295 } 296#endif /* DEBUG */ 297 298 return FALSE; 299} 300 301 302/* 303 * Determine whether a network name is one of the local networks 304 * of a host. 305 */ 306int 307is_network_member(const char *net) 308{ 309 addrlist *al; 310 311 /* 312 * If the network name string does not contain a '/', use old behavior. 313 * If it does contain a '/' then interpret the string as a network/netmask 314 * pair. If "netmask" doesn't exist, use the interface's own netmask. 315 * Also support fully explicit netmasks such as 255.255.255.0 as well as 316 * bit-length netmask such as /24 (hex formats such 0xffffff00 work too). 317 */ 318 if (strchr(net, '/') == NULL) { 319 for (al = localnets; al; al = al->ip_next) 320 if (STREQ(net, al->ip_net_name) || STREQ(net, al->ip_net_num)) 321 return TRUE; 322 } else { 323 char *netstr = strdup(net), *maskstr; 324 u_long netnum, masknum = 0; 325 maskstr = strchr(netstr, '/'); 326 maskstr[0] = '\0'; /* null terminate netstr */ 327 maskstr++; 328 if (*maskstr == '\0') /* if empty string, make it NULL */ 329 maskstr = NULL; 330 /* check if netmask uses a dotted-quad or bit-length, or not defined at all */ 331 if (maskstr) { 332 if (strchr(maskstr, '.')) { 333 /* XXX: inet_addr is obsolste, convert to inet_aton() */ 334 masknum = inet_addr(maskstr); 335 if (masknum == INADDR_NONE) /* can be invalid (-1) or all-1s */ 336 masknum = 0xffffffff; 337 } else if (NSTRCEQ(maskstr, "0x", 2)) { 338 masknum = strtoul(maskstr, NULL, 16); 339 } else { 340 int bits = atoi(maskstr); 341 if (bits < 0) 342 bits = 0; 343 if (bits > 32) 344 bits = 32; 345 masknum = 0xffffffff << (32-bits); 346 } 347 } 348 netnum = inet_addr(netstr); /* not checking return value, b/c -1 (0xffffffff) is valid */ 349 XFREE(netstr); /* netstr not needed any longer */ 350 351 /* now check against each local interface */ 352 for (al = localnets; al; al = al->ip_next) { 353 if ((al->ip_addr & (maskstr ? masknum : al->ip_mask)) == netnum) 354 return TRUE; 355 } 356 } 357 358 return FALSE; 359} 360 361 362/* 363 * Determine whether a IP address (netnum) is one of the local interfaces, 364 * returns TRUE/FALSE. 365 * Does not include the loopback interface: caller needs to check that. 366 */ 367int 368is_interface_local(u_long netnum) 369{ 370 addrlist *al; 371 372 for (al = localnets; al; al = al->ip_next) { 373 if (al->ip_addr == netnum) 374 return TRUE; 375 } 376 return FALSE; 377} 378 379 380#ifdef HAVE_GETIFADDRS 381void 382getwire(char **name1, char **number1) 383{ 384 addrlist *al = NULL, *tail = NULL; 385 struct ifaddrs *ifaddrs, *ifap; 386#ifndef HAVE_STRUCT_IFADDRS_IFA_NEXT 387 int count = 0, i; 388#endif /* not HAVE_STRUCT_IFADDRS_IFA_NEXT */ 389 390 ifaddrs = NULL; 391#ifdef HAVE_STRUCT_IFADDRS_IFA_NEXT 392 if (getifaddrs(&ifaddrs) < 0) 393 goto out; 394 395 for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next) { 396#else /* not HAVE_STRUCT_IFADDRS_IFA_NEXT */ 397 if (getifaddrs(&ifaddrs, &count) < 0) 398 goto out; 399 400 for (i = 0,ifap = ifaddrs; i < count; ifap++, i++) { 401#endif /* HAVE_STRUCT_IFADDRS_IFA_NEXT */ 402 403 if (!ifap || !ifap->ifa_addr || ifap->ifa_addr->sa_family != AF_INET) 404 continue; 405 406 /* 407 * If the interface is the loopback, or it's not running, 408 * then ignore it. 409 */ 410 if (S2IN(ifap->ifa_addr) == htonl(INADDR_LOOPBACK)) 411 continue; 412 if ((ifap->ifa_flags & IFF_RUNNING) == 0) 413 continue; 414 415 if ((ifap->ifa_flags & IFF_POINTOPOINT) == 0) 416 al = getwire_lookup(S2IN(ifap->ifa_addr), S2IN(ifap->ifa_netmask), 0); 417 else 418 al = getwire_lookup(S2IN(ifap->ifa_dstaddr), 0xffffffff, 1); 419 420 /* append to the end of the list */ 421 if (!localnets || tail == NULL) { 422 localnets = tail = al; 423 tail->ip_next = NULL; 424 } else { 425 tail->ip_next = al; 426 tail = al; 427 } 428 } 429 430out: 431 if (ifaddrs) 432 XFREE(ifaddrs); 433 434 if (localnets) { 435 *name1 = localnets->ip_net_name; 436 *number1 = localnets->ip_net_num; 437 } else { 438 *name1 = NO_SUBNET; 439 *number1 = "0.0.0.0"; 440 } 441} 442 443#else /* not HAVE_GETIFADDRS */ 444 445#if defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN) 446# define SIZE(ifr) (MAX((ifr)->ifr_addr.sa_len, sizeof((ifr)->ifr_addr)) + sizeof(ifr->ifr_name)) 447#else /* not defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN) */ 448# define SIZE(ifr) sizeof(struct ifreq) 449#endif /* not defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN) */ 450 451#define clist (ifc.ifc_ifcu.ifcu_req) 452#define count (ifc.ifc_len/sizeof(struct ifreq)) 453 454 455void 456getwire(char **name1, char **number1) 457{ 458 struct ifconf ifc; 459 struct ifreq *ifr, ifrpool; 460 caddr_t cp, cplim; 461 int fd = -1; 462 u_long address; 463 addrlist *al = NULL, *tail = NULL; 464 char buf[GFBUFLEN]; 465 466#ifndef SIOCGIFFLAGS 467 /* if cannot get interface flags, return nothing */ 468 plog(XLOG_ERROR, "getwire unable to get interface flags"); 469 localnets = NULL; 470 return; 471#endif /* not SIOCGIFFLAGS */ 472 473 /* 474 * Get suitable socket 475 */ 476 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 477 goto out; 478 479 /* 480 * Fill in ifconf details 481 */ 482 memset(&buf[0], 0, GFBUFLEN); 483 ifc.ifc_len = sizeof(buf); 484 ifc.ifc_buf = buf; 485 486 /* 487 * Get network interface configurations 488 */ 489 if (ioctl(fd, SIOCGIFCONF, (caddr_t) & ifc) < 0) 490 goto out; 491 492 /* 493 * Upper bound on array 494 */ 495 cplim = buf + ifc.ifc_len; 496 497 /* 498 * This is some magic to cope with both "traditional" and the 499 * new 4.4BSD-style struct sockaddrs. The new structure has 500 * variable length and a size field to support longer addresses. 501 * AF_LINK is a new definition for 4.4BSD. 502 */ 503 504 /* 505 * Scan the list looking for a suitable interface 506 */ 507 for (cp = buf; cp < cplim; /* increment in the loop body */) { 508 memcpy(&ifrpool, cp, sizeof(ifrpool)); 509 ifr = &ifrpool; 510 cp += SIZE(ifr); 511 512 if (ifr->ifr_addr.sa_family != AF_INET) 513 continue; 514 515 address = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; 516 517 /* 518 * Get interface flags 519 */ 520 if (ioctl(fd, SIOCGIFFLAGS, (caddr_t) ifr) < 0) 521 continue; 522 523 /* 524 * If the interface is the loopback, or it's not running, 525 * then ignore it. 526 */ 527 if (address == htonl(INADDR_LOOPBACK)) 528 continue; 529 /* 530 * Fix for 0.0.0.0 loopback on SunOS 3.X which defines IFF_ROUTE 531 * instead of IFF_LOOPBACK. 532 */ 533#ifdef IFF_ROUTE 534 if (ifr->ifr_flags == (IFF_UP|IFF_RUNNING)) 535 continue; 536#endif /* IFF_ROUTE */ 537 538 /* if the interface is not UP or not RUNNING, skip it */ 539 if ((ifr->ifr_flags & IFF_RUNNING) == 0 || 540 (ifr->ifr_flags & IFF_UP) == 0) 541 continue; 542 543 if ((ifr->ifr_flags & IFF_POINTOPOINT) == 0) { 544 /* 545 * Get the netmask of this interface 546 */ 547 if (ioctl(fd, SIOCGIFNETMASK, (caddr_t) ifr) < 0) 548 continue; 549 550 al = getwire_lookup(address, S2IN(&ifr->ifr_addr), 0); 551 } else 552 al = getwire_lookup(address, 0xffffffff, 1); 553 554 /* append to the end of the list */ 555 if (!localnets) { 556 localnets = tail = al; 557 tail->ip_next = NULL; 558 } else { 559 tail->ip_next = al; 560 tail = al; 561 } 562 } 563 564out: 565 if (fd >= 0) 566 close(fd); 567 if (localnets) { 568 *name1 = localnets->ip_net_name; 569 *number1 = localnets->ip_net_num; 570 } else { 571 *name1 = NO_SUBNET; 572 *number1 = "0.0.0.0"; 573 } 574} 575#endif /* not HAVE_GETIFADDRS */ 576