1 /* 2 * Copyright (c) 2000 - 2002, 2005 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <config.h> 35 #include "roken.h" 36 37 #ifdef __osf__ 38 /* hate */ 39 struct rtentry; 40 struct mbuf; 41 #endif 42 #ifdef HAVE_NET_IF_H 43 #include <net/if.h> 44 #endif 45 46 #ifdef HAVE_SYS_SOCKIO_H 47 #include <sys/sockio.h> 48 #endif /* HAVE_SYS_SOCKIO_H */ 49 50 #ifdef HAVE_NETINET_IN6_VAR_H 51 #include <netinet/in6_var.h> 52 #endif /* HAVE_NETINET_IN6_VAR_H */ 53 54 #include <ifaddrs.h> 55 56 #ifdef __hpux 57 #define lifconf if_laddrconf 58 #define lifc_len iflc_len 59 #define lifc_buf iflc_buf 60 #define lifc_req iflc_req 61 62 #define lifreq if_laddrreq 63 #define lifr_addr iflr_addr 64 #define lifr_name iflr_name 65 #define lifr_dstaddr iflr_dstaddr 66 #define lifr_broadaddr iflr_broadaddr 67 #define lifr_flags iflr_flags 68 #define lifr_index iflr_index 69 #endif 70 71 #ifdef AF_NETLINK 72 73 /* 74 * The linux - AF_NETLINK version of getifaddrs - from Usagi. 75 * Linux does not return v6 addresses from SIOCGIFCONF. 76 */ 77 78 /* $USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp $ */ 79 80 /************************************************************************** 81 * ifaddrs.c 82 * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved. 83 * 84 * Redistribution and use in source and binary forms, with or without 85 * modification, are permitted provided that the following conditions 86 * are met: 87 * 1. Redistributions of source code must retain the above copyright 88 * notice, this list of conditions and the following disclaimer. 89 * 2. Redistributions in binary form must reproduce the above copyright 90 * notice, this list of conditions and the following disclaimer in the 91 * documentation and/or other materials provided with the distribution. 92 * 3. Neither the name of the author nor the names of its contributors 93 * may be used to endorse or promote products derived from this software 94 * without specific prior written permission. 95 * 96 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 97 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 98 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 99 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 100 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 101 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 102 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 103 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 104 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 105 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 106 * SUCH DAMAGE. 107 */ 108 109 #include "config.h" 110 111 #include <string.h> 112 #include <time.h> 113 #include <malloc.h> 114 #include <errno.h> 115 #include <unistd.h> 116 117 #include <sys/socket.h> 118 #include <asm/types.h> 119 #include <linux/netlink.h> 120 #include <linux/rtnetlink.h> 121 #include <sys/types.h> 122 #include <sys/socket.h> 123 #include <sys/poll.h> 124 #include <netpacket/packet.h> 125 #include <net/ethernet.h> /* the L2 protocols */ 126 #include <sys/uio.h> 127 #include <net/if.h> 128 #include <net/if_arp.h> 129 #include <ifaddrs.h> 130 #include <netinet/in.h> 131 132 #define __set_errno(e) (errno = (e)) 133 #define __close(fd) (close(fd)) 134 #undef ifa_broadaddr 135 #define ifa_broadaddr ifa_dstaddr 136 #define IFA_NETMASK 137 138 /* ====================================================================== */ 139 struct nlmsg_list{ 140 struct nlmsg_list *nlm_next; 141 struct nlmsghdr *nlh; 142 int size; 143 time_t seq; 144 }; 145 146 struct rtmaddr_ifamap { 147 void *address; 148 void *local; 149 #ifdef IFA_NETMASK 150 void *netmask; 151 #endif 152 void *broadcast; 153 #ifdef HAVE_IFADDRS_IFA_ANYCAST 154 void *anycast; 155 #endif 156 int address_len; 157 int local_len; 158 #ifdef IFA_NETMASK 159 int netmask_len; 160 #endif 161 int broadcast_len; 162 #ifdef HAVE_IFADDRS_IFA_ANYCAST 163 int anycast_len; 164 #endif 165 }; 166 167 /* ====================================================================== */ 168 static size_t 169 ifa_sa_len(sa_family_t family, int len) 170 { 171 size_t size; 172 switch(family){ 173 case AF_INET: 174 size = sizeof(struct sockaddr_in); 175 break; 176 case AF_INET6: 177 size = sizeof(struct sockaddr_in6); 178 break; 179 case AF_PACKET: 180 size = (size_t)(((struct sockaddr_ll *)NULL)->sll_addr) + len; 181 if (size < sizeof(struct sockaddr_ll)) 182 size = sizeof(struct sockaddr_ll); 183 break; 184 default: 185 size = (size_t)(((struct sockaddr *)NULL)->sa_data) + len; 186 if (size < sizeof(struct sockaddr)) 187 size = sizeof(struct sockaddr); 188 break; 189 } 190 return size; 191 } 192 193 static void 194 ifa_make_sockaddr(sa_family_t family, 195 struct sockaddr *sa, 196 void *p, size_t len, 197 uint32_t scope, uint32_t scopeid) 198 { 199 if (sa == NULL) return; 200 switch(family){ 201 case AF_INET: 202 memcpy(&((struct sockaddr_in*)sa)->sin_addr, (char *)p, len); 203 break; 204 case AF_INET6: 205 memcpy(&((struct sockaddr_in6*)sa)->sin6_addr, (char *)p, len); 206 if (IN6_IS_ADDR_LINKLOCAL(p) || 207 IN6_IS_ADDR_MC_LINKLOCAL(p)){ 208 ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid; 209 } 210 break; 211 case AF_PACKET: 212 memcpy(((struct sockaddr_ll*)sa)->sll_addr, (char *)p, len); 213 ((struct sockaddr_ll*)sa)->sll_halen = len; 214 break; 215 default: 216 memcpy(sa->sa_data, p, len); /*XXX*/ 217 break; 218 } 219 sa->sa_family = family; 220 #ifdef HAVE_SOCKADDR_SA_LEN 221 sa->sa_len = ifa_sa_len(family, len); 222 #endif 223 } 224 225 #ifndef IFA_NETMASK 226 static struct sockaddr * 227 ifa_make_sockaddr_mask(sa_family_t family, 228 struct sockaddr *sa, 229 uint32_t prefixlen) 230 { 231 int i; 232 char *p = NULL, c; 233 uint32_t max_prefixlen = 0; 234 235 if (sa == NULL) return NULL; 236 switch(family){ 237 case AF_INET: 238 memset(&((struct sockaddr_in*)sa)->sin_addr, 0, sizeof(((struct sockaddr_in*)sa)->sin_addr)); 239 p = (char *)&((struct sockaddr_in*)sa)->sin_addr; 240 max_prefixlen = 32; 241 break; 242 case AF_INET6: 243 memset(&((struct sockaddr_in6*)sa)->sin6_addr, 0, sizeof(((struct sockaddr_in6*)sa)->sin6_addr)); 244 p = (char *)&((struct sockaddr_in6*)sa)->sin6_addr; 245 #if 0 /* XXX: fill scope-id? */ 246 if (IN6_IS_ADDR_LINKLOCAL(p) || 247 IN6_IS_ADDR_MC_LINKLOCAL(p)){ 248 ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid; 249 } 250 #endif 251 max_prefixlen = 128; 252 break; 253 default: 254 return NULL; 255 } 256 sa->sa_family = family; 257 #ifdef HAVE_SOCKADDR_SA_LEN 258 sa->sa_len = ifa_sa_len(family, len); 259 #endif 260 if (p){ 261 if (prefixlen > max_prefixlen) 262 prefixlen = max_prefixlen; 263 for (i=0; i<(prefixlen / 8); i++) 264 *p++ = 0xff; 265 c = 0xff; 266 c <<= (8 - (prefixlen % 8)); 267 *p = c; 268 } 269 return sa; 270 } 271 #endif 272 273 /* ====================================================================== */ 274 static int 275 nl_sendreq(int sd, int request, int flags, int *seq) 276 { 277 char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + 278 NLMSG_ALIGN(sizeof(struct rtgenmsg))]; 279 struct sockaddr_nl nladdr; 280 struct nlmsghdr *req_hdr; 281 struct rtgenmsg *req_msg; 282 time_t t = time(NULL); 283 284 if (seq) *seq = t; 285 memset(&reqbuf, 0, sizeof(reqbuf)); 286 req_hdr = (struct nlmsghdr *)reqbuf; 287 req_msg = (struct rtgenmsg *)NLMSG_DATA(req_hdr); 288 req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg)); 289 req_hdr->nlmsg_type = request; 290 req_hdr->nlmsg_flags = flags | NLM_F_REQUEST; 291 req_hdr->nlmsg_pid = 0; 292 req_hdr->nlmsg_seq = t; 293 req_msg->rtgen_family = AF_UNSPEC; 294 memset(&nladdr, 0, sizeof(nladdr)); 295 nladdr.nl_family = AF_NETLINK; 296 return (sendto(sd, (void *)req_hdr, req_hdr->nlmsg_len, 0, 297 (struct sockaddr *)&nladdr, sizeof(nladdr))); 298 } 299 300 static int 301 nl_recvmsg(int sd, int request, int seq, 302 void *buf, size_t buflen, 303 int *flags) 304 { 305 struct msghdr msg; 306 struct iovec iov = { buf, buflen }; 307 struct sockaddr_nl nladdr; 308 int read_len; 309 310 for (;;){ 311 msg.msg_name = (void *)&nladdr; 312 msg.msg_namelen = sizeof(nladdr); 313 msg.msg_iov = &iov; 314 msg.msg_iovlen = 1; 315 msg.msg_control = NULL; 316 msg.msg_controllen = 0; 317 msg.msg_flags = 0; 318 read_len = recvmsg(sd, &msg, 0); 319 if ((read_len < 0 && errno == EINTR) || (msg.msg_flags & MSG_TRUNC)) 320 continue; 321 if (flags) *flags = msg.msg_flags; 322 break; 323 } 324 return read_len; 325 } 326 327 static int 328 nl_getmsg(int sd, int request, int seq, 329 struct nlmsghdr **nlhp, 330 int *done) 331 { 332 struct nlmsghdr *nh; 333 size_t bufsize = 65536, lastbufsize = 0; 334 void *buff = NULL; 335 int result = 0, read_size; 336 int msg_flags; 337 pid_t pid = getpid(); 338 for (;;){ 339 void *newbuff = realloc(buff, bufsize); 340 if (newbuff == NULL || bufsize < lastbufsize) { 341 result = -1; 342 break; 343 } 344 buff = newbuff; 345 result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags); 346 if (read_size < 0 || (msg_flags & MSG_TRUNC)){ 347 lastbufsize = bufsize; 348 bufsize *= 2; 349 continue; 350 } 351 if (read_size == 0) break; 352 nh = (struct nlmsghdr *)buff; 353 for (nh = (struct nlmsghdr *)buff; 354 NLMSG_OK(nh, read_size); 355 nh = (struct nlmsghdr *)NLMSG_NEXT(nh, read_size)){ 356 if (nh->nlmsg_pid != pid || 357 nh->nlmsg_seq != seq) 358 continue; 359 if (nh->nlmsg_type == NLMSG_DONE){ 360 (*done)++; 361 break; /* ok */ 362 } 363 if (nh->nlmsg_type == NLMSG_ERROR){ 364 struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(nh); 365 result = -1; 366 if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) 367 __set_errno(EIO); 368 else 369 __set_errno(-nlerr->error); 370 break; 371 } 372 } 373 break; 374 } 375 if (result < 0) 376 if (buff){ 377 int saved_errno = errno; 378 free(buff); 379 __set_errno(saved_errno); 380 } 381 *nlhp = (struct nlmsghdr *)buff; 382 return result; 383 } 384 385 static int 386 nl_getlist(int sd, int seq, 387 int request, 388 struct nlmsg_list **nlm_list, 389 struct nlmsg_list **nlm_end) 390 { 391 struct nlmsghdr *nlh = NULL; 392 int status; 393 int done = 0; 394 int tries = 3; 395 396 try_again: 397 status = nl_sendreq(sd, request, NLM_F_ROOT|NLM_F_MATCH, &seq); 398 if (status < 0) 399 return status; 400 if (seq == 0) 401 seq = (int)time(NULL); 402 while(!done){ 403 struct pollfd pfd; 404 405 pfd.fd = sd; 406 pfd.events = POLLIN | POLLPRI; 407 pfd.revents = 0; 408 status = poll(&pfd, 1, 1000); 409 if (status < 0) 410 return status; 411 else if (status == 0) { 412 seq++; 413 if (tries-- > 0) 414 goto try_again; 415 return -1; 416 } 417 418 status = nl_getmsg(sd, request, seq, &nlh, &done); 419 if (status < 0) 420 return status; 421 if (nlh){ 422 struct nlmsg_list *nlm_next = (struct nlmsg_list *)malloc(sizeof(struct nlmsg_list)); 423 if (nlm_next == NULL){ 424 int saved_errno = errno; 425 free(nlh); 426 __set_errno(saved_errno); 427 status = -1; 428 } else { 429 nlm_next->nlm_next = NULL; 430 nlm_next->nlh = (struct nlmsghdr *)nlh; 431 nlm_next->size = status; 432 nlm_next->seq = seq; 433 if (*nlm_list == NULL){ 434 *nlm_list = nlm_next; 435 *nlm_end = nlm_next; 436 } else { 437 (*nlm_end)->nlm_next = nlm_next; 438 *nlm_end = nlm_next; 439 } 440 } 441 } 442 } 443 return status >= 0 ? seq : status; 444 } 445 446 /* ---------------------------------------------------------------------- */ 447 static void 448 free_nlmsglist(struct nlmsg_list *nlm0) 449 { 450 struct nlmsg_list *nlm, *nlm_next; 451 int saved_errno; 452 if (!nlm0) 453 return; 454 saved_errno = errno; 455 for (nlm=nlm0; nlm; nlm=nlm_next){ 456 if (nlm->nlh) 457 free(nlm->nlh); 458 nlm_next=nlm->nlm_next; 459 free(nlm); 460 } 461 __set_errno(saved_errno); 462 } 463 464 static void 465 free_data(void *data, void *ifdata) 466 { 467 int saved_errno = errno; 468 if (data != NULL) free(data); 469 if (ifdata != NULL) free(ifdata); 470 __set_errno(saved_errno); 471 } 472 473 /* ---------------------------------------------------------------------- */ 474 static void 475 nl_close(int sd) 476 { 477 int saved_errno = errno; 478 if (sd >= 0) __close(sd); 479 __set_errno(saved_errno); 480 } 481 482 /* ---------------------------------------------------------------------- */ 483 static int 484 nl_open(void) 485 { 486 struct sockaddr_nl nladdr; 487 int sd; 488 489 sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 490 if (sd < 0) return -1; 491 memset(&nladdr, 0, sizeof(nladdr)); 492 nladdr.nl_family = AF_NETLINK; 493 if (bind(sd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0){ 494 nl_close(sd); 495 return -1; 496 } 497 return sd; 498 } 499 500 /* ====================================================================== */ 501 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 502 rk_getifaddrs(struct ifaddrs **ifap) 503 { 504 int sd; 505 struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; 506 /* - - - - - - - - - - - - - - - */ 507 int icnt; 508 size_t dlen, xlen, nlen; 509 uint32_t max_ifindex = 0; 510 511 pid_t pid = getpid(); 512 int seq; 513 int result; 514 int build ; /* 0 or 1 */ 515 516 /* ---------------------------------- */ 517 /* initialize */ 518 icnt = dlen = xlen = nlen = 0; 519 nlmsg_list = nlmsg_end = NULL; 520 521 if (ifap) 522 *ifap = NULL; 523 524 /* ---------------------------------- */ 525 /* open socket and bind */ 526 sd = nl_open(); 527 if (sd < 0) 528 return -1; 529 530 /* ---------------------------------- */ 531 /* gather info */ 532 if ((seq = nl_getlist(sd, 0, RTM_GETLINK, 533 &nlmsg_list, &nlmsg_end)) < 0){ 534 free_nlmsglist(nlmsg_list); 535 nl_close(sd); 536 return -1; 537 } 538 if ((seq = nl_getlist(sd, seq+1, RTM_GETADDR, 539 &nlmsg_list, &nlmsg_end)) < 0){ 540 free_nlmsglist(nlmsg_list); 541 nl_close(sd); 542 return -1; 543 } 544 545 /* ---------------------------------- */ 546 /* Estimate size of result buffer and fill it */ 547 for (build=0; build<=1; build++){ 548 struct ifaddrs *ifl = NULL, *ifa = NULL; 549 struct nlmsghdr *nlh, *nlh0; 550 char *data = NULL, *xdata = NULL; 551 void *ifdata = NULL; 552 char *ifname = NULL, **iflist = NULL; 553 uint16_t *ifflist = NULL; 554 struct rtmaddr_ifamap ifamap; 555 556 if (build){ 557 data = calloc(1, 558 NLMSG_ALIGN(sizeof(struct ifaddrs[icnt])) 559 + dlen + xlen + nlen); 560 ifa = (struct ifaddrs *)data; 561 ifdata = calloc(1, 562 NLMSG_ALIGN(sizeof(char *[max_ifindex+1])) 563 + NLMSG_ALIGN(sizeof(uint16_t [max_ifindex+1]))); 564 if (ifap != NULL) 565 *ifap = (ifdata != NULL) ? ifa : NULL; 566 else{ 567 free_data(data, ifdata); 568 result = 0; 569 break; 570 } 571 if (data == NULL || ifdata == NULL){ 572 free_data(data, ifdata); 573 result = -1; 574 break; 575 } 576 ifl = NULL; 577 data += NLMSG_ALIGN(sizeof(struct ifaddrs)) * icnt; 578 xdata = data + dlen; 579 ifname = xdata + xlen; 580 iflist = ifdata; 581 ifflist = (uint16_t *)(((char *)iflist) + NLMSG_ALIGN(sizeof(char *[max_ifindex+1]))); 582 } 583 584 for (nlm=nlmsg_list; nlm; nlm=nlm->nlm_next){ 585 int nlmlen = nlm->size; 586 if (!(nlh0 = nlm->nlh)) 587 continue; 588 for (nlh = nlh0; 589 NLMSG_OK(nlh, nlmlen); 590 nlh=NLMSG_NEXT(nlh,nlmlen)){ 591 struct ifinfomsg *ifim = NULL; 592 struct ifaddrmsg *ifam = NULL; 593 struct rtattr *rta; 594 595 size_t nlm_struct_size = 0; 596 sa_family_t nlm_family = 0; 597 uint32_t nlm_scope = 0, nlm_index = 0; 598 size_t sockaddr_size = 0; 599 uint32_t nlm_prefixlen = 0; 600 size_t rtasize; 601 602 memset(&ifamap, 0, sizeof(ifamap)); 603 604 /* check if the message is what we want */ 605 if (nlh->nlmsg_pid != pid || 606 nlh->nlmsg_seq != nlm->seq) 607 continue; 608 if (nlh->nlmsg_type == NLMSG_DONE){ 609 break; /* ok */ 610 } 611 switch (nlh->nlmsg_type){ 612 case RTM_NEWLINK: 613 ifim = (struct ifinfomsg *)NLMSG_DATA(nlh); 614 nlm_struct_size = sizeof(*ifim); 615 nlm_family = ifim->ifi_family; 616 nlm_scope = 0; 617 nlm_index = ifim->ifi_index; 618 nlm_prefixlen = 0; 619 if (build) 620 ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags; 621 break; 622 case RTM_NEWADDR: 623 ifam = (struct ifaddrmsg *)NLMSG_DATA(nlh); 624 nlm_struct_size = sizeof(*ifam); 625 nlm_family = ifam->ifa_family; 626 nlm_scope = ifam->ifa_scope; 627 nlm_index = ifam->ifa_index; 628 nlm_prefixlen = ifam->ifa_prefixlen; 629 if (build) 630 ifa->ifa_flags = ifflist[nlm_index]; 631 break; 632 default: 633 continue; 634 } 635 636 if (!build){ 637 if (max_ifindex < nlm_index) 638 max_ifindex = nlm_index; 639 } else { 640 if (ifl != NULL) 641 ifl->ifa_next = ifa; 642 } 643 644 rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size); 645 for (rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size)); 646 RTA_OK(rta, rtasize); 647 rta = RTA_NEXT(rta, rtasize)){ 648 struct sockaddr **sap = NULL; 649 void *rtadata = RTA_DATA(rta); 650 size_t rtapayload = RTA_PAYLOAD(rta); 651 socklen_t sa_len; 652 653 switch(nlh->nlmsg_type){ 654 case RTM_NEWLINK: 655 switch(rta->rta_type){ 656 case IFLA_ADDRESS: 657 case IFLA_BROADCAST: 658 if (build){ 659 sap = (rta->rta_type == IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa->ifa_broadaddr; 660 *sap = (struct sockaddr *)data; 661 } 662 sa_len = ifa_sa_len(AF_PACKET, rtapayload); 663 if (rta->rta_type == IFLA_ADDRESS) 664 sockaddr_size = NLMSG_ALIGN(sa_len); 665 if (!build){ 666 dlen += NLMSG_ALIGN(sa_len); 667 } else { 668 memset(*sap, 0, sa_len); 669 ifa_make_sockaddr(AF_PACKET, *sap, rtadata,rtapayload, 0,0); 670 ((struct sockaddr_ll *)*sap)->sll_ifindex = nlm_index; 671 ((struct sockaddr_ll *)*sap)->sll_hatype = ifim->ifi_type; 672 data += NLMSG_ALIGN(sa_len); 673 } 674 break; 675 case IFLA_IFNAME:/* Name of Interface */ 676 if (!build) 677 nlen += NLMSG_ALIGN(rtapayload + 1); 678 else{ 679 ifa->ifa_name = ifname; 680 if (iflist[nlm_index] == NULL) 681 iflist[nlm_index] = ifa->ifa_name; 682 strncpy(ifa->ifa_name, rtadata, rtapayload); 683 ifa->ifa_name[rtapayload] = '\0'; 684 ifname += NLMSG_ALIGN(rtapayload + 1); 685 } 686 break; 687 case IFLA_STATS:/* Statistics of Interface */ 688 if (!build) 689 xlen += NLMSG_ALIGN(rtapayload); 690 else{ 691 ifa->ifa_data = xdata; 692 memcpy(ifa->ifa_data, rtadata, rtapayload); 693 xdata += NLMSG_ALIGN(rtapayload); 694 } 695 break; 696 case IFLA_UNSPEC: 697 break; 698 case IFLA_MTU: 699 break; 700 case IFLA_LINK: 701 break; 702 case IFLA_QDISC: 703 break; 704 default: 705 break; 706 } 707 break; 708 case RTM_NEWADDR: 709 if (nlm_family == AF_PACKET) break; 710 switch(rta->rta_type){ 711 case IFA_ADDRESS: 712 ifamap.address = rtadata; 713 ifamap.address_len = rtapayload; 714 break; 715 case IFA_LOCAL: 716 ifamap.local = rtadata; 717 ifamap.local_len = rtapayload; 718 break; 719 case IFA_BROADCAST: 720 ifamap.broadcast = rtadata; 721 ifamap.broadcast_len = rtapayload; 722 break; 723 #ifdef HAVE_IFADDRS_IFA_ANYCAST 724 case IFA_ANYCAST: 725 ifamap.anycast = rtadata; 726 ifamap.anycast_len = rtapayload; 727 break; 728 #endif 729 case IFA_LABEL: 730 if (!build) 731 nlen += NLMSG_ALIGN(rtapayload + 1); 732 else{ 733 ifa->ifa_name = ifname; 734 if (iflist[nlm_index] == NULL) 735 iflist[nlm_index] = ifname; 736 strncpy(ifa->ifa_name, rtadata, rtapayload); 737 ifa->ifa_name[rtapayload] = '\0'; 738 ifname += NLMSG_ALIGN(rtapayload + 1); 739 } 740 break; 741 case IFA_UNSPEC: 742 break; 743 case IFA_CACHEINFO: 744 break; 745 default: 746 break; 747 } 748 } 749 } 750 if (nlh->nlmsg_type == RTM_NEWADDR && 751 nlm_family != AF_PACKET) { 752 if (!ifamap.local) { 753 ifamap.local = ifamap.address; 754 ifamap.local_len = ifamap.address_len; 755 } 756 if (!ifamap.address) { 757 ifamap.address = ifamap.local; 758 ifamap.address_len = ifamap.local_len; 759 } 760 if (ifamap.address_len != ifamap.local_len || 761 (ifamap.address != NULL && 762 memcmp(ifamap.address, ifamap.local, ifamap.address_len))) { 763 /* p2p; address is peer and local is ours */ 764 ifamap.broadcast = ifamap.address; 765 ifamap.broadcast_len = ifamap.address_len; 766 ifamap.address = ifamap.local; 767 ifamap.address_len = ifamap.local_len; 768 } 769 if (ifamap.address) { 770 #ifndef IFA_NETMASK 771 sockaddr_size = NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len)); 772 #endif 773 if (!build) 774 dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len)); 775 else { 776 ifa->ifa_addr = (struct sockaddr *)data; 777 ifa_make_sockaddr(nlm_family, ifa->ifa_addr, ifamap.address, ifamap.address_len, 778 nlm_scope, nlm_index); 779 data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.address_len)); 780 } 781 } 782 #ifdef IFA_NETMASK 783 if (ifamap.netmask) { 784 if (!build) 785 dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.netmask_len)); 786 else { 787 ifa->ifa_netmask = (struct sockaddr *)data; 788 ifa_make_sockaddr(nlm_family, ifa->ifa_netmask, ifamap.netmask, ifamap.netmask_len, 789 nlm_scope, nlm_index); 790 data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.netmask_len)); 791 } 792 } 793 #endif 794 if (ifamap.broadcast) { 795 if (!build) 796 dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.broadcast_len)); 797 else { 798 ifa->ifa_broadaddr = (struct sockaddr *)data; 799 ifa_make_sockaddr(nlm_family, ifa->ifa_broadaddr, ifamap.broadcast, ifamap.broadcast_len, 800 nlm_scope, nlm_index); 801 data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.broadcast_len)); 802 } 803 } 804 #ifdef HAVE_IFADDRS_IFA_ANYCAST 805 if (ifamap.anycast) { 806 if (!build) 807 dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.anycast_len)); 808 else { 809 ifa->ifa_anycast = (struct sockaddr *)data; 810 ifa_make_sockaddr(nlm_family, ifa->ifa_anyaddr, ifamap.anycast, ifamap.anycast_len, 811 nlm_scope, nlm_index); 812 data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.anycast_len)); 813 } 814 } 815 #endif 816 } 817 if (!build){ 818 #ifndef IFA_NETMASK 819 dlen += sockaddr_size; 820 #endif 821 icnt++; 822 } else { 823 if (ifa->ifa_name == NULL) 824 ifa->ifa_name = iflist[nlm_index]; 825 #ifndef IFA_NETMASK 826 if (ifa->ifa_addr && 827 ifa->ifa_addr->sa_family != AF_UNSPEC && 828 ifa->ifa_addr->sa_family != AF_PACKET){ 829 ifa->ifa_netmask = (struct sockaddr *)data; 830 ifa_make_sockaddr_mask(ifa->ifa_addr->sa_family, ifa->ifa_netmask, nlm_prefixlen); 831 } 832 data += sockaddr_size; 833 #endif 834 ifl = ifa++; 835 } 836 } 837 } 838 if (!build){ 839 if (icnt == 0 && (dlen + nlen + xlen == 0)){ 840 if (ifap != NULL) 841 *ifap = NULL; 842 break; /* cannot found any addresses */ 843 } 844 } 845 else 846 free_data(NULL, ifdata); 847 } 848 849 /* ---------------------------------- */ 850 /* Finalize */ 851 free_nlmsglist(nlmsg_list); 852 nl_close(sd); 853 return 0; 854 } 855 856 void ROKEN_LIB_FUNCTION 857 rk_freeifaddrs(struct ifaddrs *ifp) 858 { 859 /* AF_NETLINK method uses a single allocation for all interfaces */ 860 free(ifp); 861 } 862 863 #else /* !AF_NETLINK */ 864 865 /* 866 * The generic SIOCGIFCONF version. 867 */ 868 869 static int 870 getifaddrs2(struct ifaddrs **ifap, 871 int af, int siocgifconf, int siocgifflags, 872 size_t ifreq_sz) 873 { 874 int ret; 875 int fd; 876 size_t buf_size; 877 char *buf; 878 struct ifconf ifconf; 879 char *p; 880 size_t sz; 881 struct sockaddr sa_zero; 882 struct ifreq *ifr; 883 struct ifaddrs *start = NULL, **end = &start; 884 885 buf = NULL; 886 887 memset (&sa_zero, 0, sizeof(sa_zero)); 888 fd = socket(af, SOCK_DGRAM, 0); 889 if (fd < 0) 890 return -1; 891 892 buf_size = 8192; 893 for (;;) { 894 buf = calloc(1, buf_size); 895 if (buf == NULL) { 896 ret = ENOMEM; 897 goto error_out; 898 } 899 ifconf.ifc_len = buf_size; 900 ifconf.ifc_buf = buf; 901 902 /* 903 * Solaris returns EINVAL when the buffer is too small. 904 */ 905 if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) { 906 ret = errno; 907 goto error_out; 908 } 909 /* 910 * Can the difference between a full and a overfull buf 911 * be determined? 912 */ 913 914 if (ifconf.ifc_len < buf_size) 915 break; 916 free (buf); 917 buf_size *= 2; 918 } 919 920 for (p = ifconf.ifc_buf; 921 p < ifconf.ifc_buf + ifconf.ifc_len; 922 p += sz) { 923 struct ifreq ifreq; 924 struct sockaddr *sa; 925 size_t salen; 926 927 ifr = (struct ifreq *)p; 928 sa = &ifr->ifr_addr; 929 930 sz = ifreq_sz; 931 salen = sizeof(struct sockaddr); 932 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 933 salen = sa->sa_len; 934 sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); 935 #endif 936 #ifdef SA_LEN 937 salen = SA_LEN(sa); 938 sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa)); 939 #endif 940 memset (&ifreq, 0, sizeof(ifreq)); 941 memcpy (ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name)); 942 943 if (ioctl(fd, siocgifflags, &ifreq) < 0) { 944 ret = errno; 945 goto error_out; 946 } 947 948 *end = malloc(sizeof(**end)); 949 if (*end == NULL) { 950 ret = ENOMEM; 951 goto error_out; 952 } 953 954 (*end)->ifa_next = NULL; 955 (*end)->ifa_name = strdup(ifr->ifr_name); 956 if ((*end)->ifa_name == NULL) { 957 ret = ENOMEM; 958 goto error_out; 959 } 960 (*end)->ifa_flags = ifreq.ifr_flags; 961 (*end)->ifa_addr = malloc(salen); 962 if ((*end)->ifa_addr == NULL) { 963 ret = ENOMEM; 964 goto error_out; 965 } 966 memcpy((*end)->ifa_addr, sa, salen); 967 (*end)->ifa_netmask = NULL; 968 969 #if 0 970 /* fix these when we actually need them */ 971 if(ifreq.ifr_flags & IFF_BROADCAST) { 972 (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr)); 973 if ((*end)->ifa_broadaddr == NULL) { 974 ret = ENOMEM; 975 goto error_out; 976 } 977 memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr, 978 sizeof(ifr->ifr_broadaddr)); 979 } else if(ifreq.ifr_flags & IFF_POINTOPOINT) { 980 (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr)); 981 if ((*end)->ifa_dstaddr == NULL) { 982 ret = ENOMEM; 983 goto error_out; 984 } 985 memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr, 986 sizeof(ifr->ifr_dstaddr)); 987 } else 988 (*end)->ifa_dstaddr = NULL; 989 #else 990 (*end)->ifa_dstaddr = NULL; 991 #endif 992 993 (*end)->ifa_data = NULL; 994 995 end = &(*end)->ifa_next; 996 997 } 998 *ifap = start; 999 close(fd); 1000 free(buf); 1001 return 0; 1002 error_out: 1003 rk_freeifaddrs(start); 1004 close(fd); 1005 free(buf); 1006 errno = ret; 1007 return -1; 1008 } 1009 1010 #if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) 1011 static int 1012 getlifaddrs2(struct ifaddrs **ifap, 1013 int af, int siocgifconf, int siocgifflags, 1014 size_t ifreq_sz) 1015 { 1016 int ret; 1017 int fd; 1018 size_t buf_size; 1019 char *buf; 1020 struct lifconf ifconf; 1021 char *p; 1022 size_t sz; 1023 struct sockaddr sa_zero; 1024 struct lifreq *ifr; 1025 struct ifaddrs *start = NULL, **end = &start; 1026 1027 buf = NULL; 1028 1029 memset (&sa_zero, 0, sizeof(sa_zero)); 1030 fd = socket(af, SOCK_DGRAM, 0); 1031 if (fd < 0) 1032 return -1; 1033 1034 buf_size = 8192; 1035 for (;;) { 1036 buf = calloc(1, buf_size); 1037 if (buf == NULL) { 1038 ret = ENOMEM; 1039 goto error_out; 1040 } 1041 #ifndef __hpux 1042 ifconf.lifc_family = af; 1043 ifconf.lifc_flags = 0; 1044 #endif 1045 ifconf.lifc_len = buf_size; 1046 ifconf.lifc_buf = buf; 1047 1048 /* 1049 * Solaris returns EINVAL when the buffer is too small. 1050 */ 1051 if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) { 1052 ret = errno; 1053 goto error_out; 1054 } 1055 /* 1056 * Can the difference between a full and a overfull buf 1057 * be determined? 1058 */ 1059 1060 if (ifconf.lifc_len < buf_size) 1061 break; 1062 free (buf); 1063 buf_size *= 2; 1064 } 1065 1066 for (p = ifconf.lifc_buf; 1067 p < ifconf.lifc_buf + ifconf.lifc_len; 1068 p += sz) { 1069 struct lifreq ifreq; 1070 struct sockaddr_storage *sa; 1071 size_t salen; 1072 1073 ifr = (struct lifreq *)p; 1074 sa = &ifr->lifr_addr; 1075 1076 sz = ifreq_sz; 1077 salen = sizeof(struct sockaddr_storage); 1078 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 1079 salen = sa->sa_len; 1080 sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); 1081 #endif 1082 #ifdef SA_LEN 1083 salen = SA_LEN(sa); 1084 sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa)); 1085 #endif 1086 memset (&ifreq, 0, sizeof(ifreq)); 1087 memcpy (ifreq.lifr_name, ifr->lifr_name, sizeof(ifr->lifr_name)); 1088 1089 if (ioctl(fd, siocgifflags, &ifreq) < 0) { 1090 ret = errno; 1091 goto error_out; 1092 } 1093 1094 *end = malloc(sizeof(**end)); 1095 if (*end == NULL) { 1096 ret = ENOMEM; 1097 goto error_out; 1098 } 1099 1100 (*end)->ifa_next = NULL; 1101 (*end)->ifa_name = strdup(ifr->lifr_name); 1102 if ((*end)->ifa_name == NULL) { 1103 ret = ENOMEM; 1104 goto error_out; 1105 } 1106 (*end)->ifa_flags = ifreq.lifr_flags; 1107 (*end)->ifa_addr = malloc(salen); 1108 if ((*end)->ifa_addr == NULL) { 1109 ret = ENOMEM; 1110 goto error_out; 1111 } 1112 memcpy((*end)->ifa_addr, sa, salen); 1113 (*end)->ifa_netmask = NULL; 1114 1115 #if 0 1116 /* fix these when we actually need them */ 1117 if(ifreq.ifr_flags & IFF_BROADCAST) { 1118 (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr)); 1119 if ((*end)->ifa_broadaddr == NULL) { 1120 ret = ENOMEM; 1121 goto error_out; 1122 } 1123 memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr, 1124 sizeof(ifr->ifr_broadaddr)); 1125 } else if(ifreq.ifr_flags & IFF_POINTOPOINT) { 1126 (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr)); 1127 if ((*end)->ifa_dstaddr == NULL) { 1128 ret = ENOMEM; 1129 goto error_out; 1130 } 1131 memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr, 1132 sizeof(ifr->ifr_dstaddr)); 1133 } else 1134 (*end)->ifa_dstaddr = NULL; 1135 #else 1136 (*end)->ifa_dstaddr = NULL; 1137 #endif 1138 1139 (*end)->ifa_data = NULL; 1140 1141 end = &(*end)->ifa_next; 1142 1143 } 1144 *ifap = start; 1145 close(fd); 1146 free(buf); 1147 return 0; 1148 error_out: 1149 rk_freeifaddrs(start); 1150 close(fd); 1151 free(buf); 1152 errno = ret; 1153 return -1; 1154 } 1155 #endif /* defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) */ 1156 1157 /** 1158 * Join two struct ifaddrs lists by appending supp to base. 1159 * Either may be NULL. The new list head (usually base) will be 1160 * returned. 1161 */ 1162 static struct ifaddrs * 1163 append_ifaddrs(struct ifaddrs *base, struct ifaddrs *supp) { 1164 if (!base) 1165 return supp; 1166 1167 if (!supp) 1168 return base; 1169 1170 while (base->ifa_next) 1171 base = base->ifa_next; 1172 1173 base->ifa_next = supp; 1174 1175 return base; 1176 } 1177 1178 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 1179 rk_getifaddrs(struct ifaddrs **ifap) 1180 { 1181 int ret = -1; 1182 errno = ENXIO; 1183 #if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS) 1184 if (ret) 1185 ret = getifaddrs2 (ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS, 1186 sizeof(struct in6_ifreq)); 1187 #endif 1188 #if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) 1189 /* Do IPv6 and IPv4 queries separately then join the result. 1190 * 1191 * HP-UX only returns IPv6 addresses using SIOCGLIFCONF, 1192 * SIOCGIFCONF has to be used for IPv4 addresses. The result is then 1193 * merged. 1194 * 1195 * Solaris needs particular care, because a SIOCGLIFCONF lookup using 1196 * AF_UNSPEC can fail in a Zone requiring an AF_INET lookup, so we just 1197 * do them separately the same as for HP-UX. See 1198 * http://repo.or.cz/w/heimdal.git/commitdiff/76afc31e9ba2f37e64c70adc006ade9e37e9ef73 1199 */ 1200 if (ret) { 1201 int v6err, v4err; 1202 struct ifaddrs *v6addrs, *v4addrs; 1203 1204 v6err = getlifaddrs2 (&v6addrs, AF_INET6, SIOCGLIFCONF, SIOCGLIFFLAGS, 1205 sizeof(struct lifreq)); 1206 v4err = getifaddrs2 (&v4addrs, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, 1207 sizeof(struct ifreq)); 1208 if (v6err) 1209 v6addrs = NULL; 1210 if (v4err) 1211 v4addrs = NULL; 1212 1213 if (v6addrs) { 1214 if (v4addrs) 1215 *ifap = append_ifaddrs(v6addrs, v4addrs); 1216 else 1217 *ifap = v6addrs; 1218 } else if (v4addrs) { 1219 *ifap = v4addrs; 1220 } else { 1221 *ifap = NULL; 1222 } 1223 1224 ret = (v6err || v4err) ? -1 : 0; 1225 } 1226 #endif 1227 #if defined(HAVE_IPV6) && defined(SIOCGIFCONF) 1228 if (ret) 1229 ret = getifaddrs2 (ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS, 1230 sizeof(struct ifreq)); 1231 #endif 1232 #if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS) 1233 if (ret) 1234 ret = getifaddrs2 (ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, 1235 sizeof(struct ifreq)); 1236 #endif 1237 return ret; 1238 } 1239 1240 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL 1241 rk_freeifaddrs(struct ifaddrs *ifp) 1242 { 1243 struct ifaddrs *p, *q; 1244 1245 for(p = ifp; p; ) { 1246 free(p->ifa_name); 1247 if(p->ifa_addr) 1248 free(p->ifa_addr); 1249 if(p->ifa_dstaddr) 1250 free(p->ifa_dstaddr); 1251 if(p->ifa_netmask) 1252 free(p->ifa_netmask); 1253 if(p->ifa_data) 1254 free(p->ifa_data); 1255 q = p; 1256 p = p->ifa_next; 1257 free(q); 1258 } 1259 } 1260 1261 #endif /* !AF_NETLINK */ 1262 1263 #ifdef TEST 1264 1265 void 1266 print_addr(const char *s, struct sockaddr *sa) 1267 { 1268 int i; 1269 printf(" %s=%d/", s, sa->sa_family); 1270 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 1271 for(i = 0; i < sa->sa_len - ((long)sa->sa_data - (long)&sa->sa_family); i++) 1272 printf("%02x", ((unsigned char*)sa->sa_data)[i]); 1273 #else 1274 for(i = 0; i < sizeof(sa->sa_data); i++) 1275 printf("%02x", ((unsigned char*)sa->sa_data)[i]); 1276 #endif 1277 printf("\n"); 1278 } 1279 1280 void 1281 print_ifaddrs(struct ifaddrs *x) 1282 { 1283 struct ifaddrs *p; 1284 1285 for(p = x; p; p = p->ifa_next) { 1286 printf("%s\n", p->ifa_name); 1287 printf(" flags=%x\n", p->ifa_flags); 1288 if(p->ifa_addr) 1289 print_addr("addr", p->ifa_addr); 1290 if(p->ifa_dstaddr) 1291 print_addr("dstaddr", p->ifa_dstaddr); 1292 if(p->ifa_netmask) 1293 print_addr("netmask", p->ifa_netmask); 1294 printf(" %p\n", p->ifa_data); 1295 } 1296 } 1297 1298 int 1299 main() 1300 { 1301 struct ifaddrs *a = NULL, *b; 1302 getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, sizeof(struct ifreq)); 1303 print_ifaddrs(a); 1304 printf("---\n"); 1305 getifaddrs(&b); 1306 print_ifaddrs(b); 1307 return 0; 1308 } 1309 #endif 1310