From e34d8872f4a713d904a4b34fb081060d1a7eba62 Mon Sep 17 00:00:00 2001 From: Sebastian Wiedenroth Date: Thu, 18 May 2017 23:19:20 +0200 Subject: [PATCH] 3729 getifaddrs must learn to stop worrying and love the other address families Reviewed by: Yuri Pankov Reviewed by: Robert Mustacchi Reviewed by: Dan McDonald Approved by: Gordon Ross --- usr/src/cmd/fs.d/nfs/statd/sm_proc.c | 8 +- usr/src/lib/libipadm/common/ipadm_addr.c | 3 + usr/src/lib/libipadm/common/ipadm_if.c | 2 + usr/src/lib/libsocket/inet/getifaddrs.c | 278 ++++++++++++++++++++++++++++-- usr/src/man/man3socket/getifaddrs.3socket | 18 +- 5 files changed, 284 insertions(+), 25 deletions(-) diff --git a/usr/src/cmd/fs.d/nfs/statd/sm_proc.c b/usr/src/cmd/fs.d/nfs/statd/sm_proc.c index 00d8d59f89..1a07de0877 100644 --- a/usr/src/cmd/fs.d/nfs/statd/sm_proc.c +++ b/usr/src/cmd/fs.d/nfs/statd/sm_proc.c @@ -1174,7 +1174,8 @@ in_host_array(char *host) * nothing and leaves host_name the way it was previous to the call. */ static void -add_to_host_array(char *host) { +add_to_host_array(char *host) +{ void *new_block = NULL; @@ -1182,7 +1183,7 @@ add_to_host_array(char *host) { if (addrix >= host_name_count) { host_name_count += HOST_NAME_INCR; new_block = realloc((void *)host_name, - host_name_count*sizeof (char *)); + host_name_count*sizeof (char *)); if (new_block != NULL) host_name = new_block; else { @@ -1338,6 +1339,9 @@ merge_ips(void) break; } + case AF_LINK: + continue; + default: syslog(LOG_WARNING, "Unknown address family %d for " "interface %s", sa->sa_family, cifap->ifa_name); diff --git a/usr/src/lib/libipadm/common/ipadm_addr.c b/usr/src/lib/libipadm/common/ipadm_addr.c index a62bcb165b..31f6f9cfc5 100644 --- a/usr/src/lib/libipadm/common/ipadm_addr.c +++ b/usr/src/lib/libipadm/common/ipadm_addr.c @@ -375,6 +375,9 @@ retry: for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { struct sockaddr_storage data; + if (ifap->ifa_addr->sa_family == AF_LINK) + continue; + (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname)); lnum = 0; if ((sep = strrchr(cifname, ':')) != NULL) { diff --git a/usr/src/lib/libipadm/common/ipadm_if.c b/usr/src/lib/libipadm/common/ipadm_if.c index 41f22e4eeb..c58eb6248e 100644 --- a/usr/src/lib/libipadm/common/ipadm_if.c +++ b/usr/src/lib/libipadm/common/ipadm_if.c @@ -276,6 +276,8 @@ retry: * to find the interface state. */ for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { + if (ifap->ifa_addr->sa_family == AF_LINK) + continue; if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0) break; } diff --git a/usr/src/lib/libsocket/inet/getifaddrs.c b/usr/src/lib/libsocket/inet/getifaddrs.c index df6d4199d1..42afca41bc 100644 --- a/usr/src/lib/libsocket/inet/getifaddrs.c +++ b/usr/src/lib/libsocket/inet/getifaddrs.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017 Sebastian Wiedenroth. All rights reserved. */ #include @@ -28,11 +29,21 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -78,10 +89,140 @@ freeifaddrs(struct ifaddrs *ifa) free(curr->ifa_addr); free(curr->ifa_netmask); free(curr->ifa_dstaddr); + free(curr->ifa_data); free(curr); } } +static uint_t +dlpi_iftype(uint_t dlpitype) +{ + switch (dlpitype) { + case DL_ETHER: + return (IFT_ETHER); + + case DL_ATM: + return (IFT_ATM); + + case DL_CSMACD: + return (IFT_ISO88023); + + case DL_TPB: + return (IFT_ISO88024); + + case DL_TPR: + return (IFT_ISO88025); + + case DL_FDDI: + return (IFT_FDDI); + + case DL_IB: + return (IFT_IB); + + case DL_OTHER: + return (IFT_OTHER); + } + + return (IFT_OTHER); +} + +/* + * Make a door call to dlmgmtd. + * If successful the result is stored in rbuf and 0 returned. + * On errors, return -1 and set `errno'. + */ +static int +dl_door_call(int door_fd, void *arg, size_t asize, void *rbuf, size_t *rsizep) +{ + int err; + door_arg_t darg; + darg.data_ptr = arg; + darg.data_size = asize; + darg.desc_ptr = NULL; + darg.desc_num = 0; + darg.rbuf = rbuf; + darg.rsize = *rsizep; + + if (door_call(door_fd, &darg) == -1) { + return (-1); + } + + if (darg.rbuf != rbuf) { + /* + * The size of the input rbuf was not big enough so that + * the door allocated the rbuf itself. In this case, return + * the required size to the caller. + */ + err = errno; + (void) munmap(darg.rbuf, darg.rsize); + *rsizep = darg.rsize; + errno = err; + return (-1); + } else if (darg.rsize != *rsizep) { + return (-1); + } + return (0); +} + + +/* + * Get the name from dlmgmtd by linkid. + * If successful the result is stored in name_retval and 0 returned. + * On errors, return -1 and set `errno'. + */ +static int +dl_get_name(int door_fd, datalink_id_t linkid, + dlmgmt_getname_retval_t *name_retval) +{ + size_t name_sz = sizeof (*name_retval); + dlmgmt_door_getname_t getname; + bzero(&getname, sizeof (dlmgmt_door_getname_t)); + getname.ld_cmd = DLMGMT_CMD_GETNAME; + getname.ld_linkid = linkid; + + if (dl_door_call(door_fd, &getname, sizeof (getname), name_retval, + &name_sz) < 0) { + return (-1); + } + if (name_retval->lr_err != 0) { + errno = name_retval->lr_err; + return (-1); + } + return (0); +} + +/* + * Get the next link from dlmgmtd. + * Start iterating by passing DATALINK_INVALID_LINKID as linkid. + * The end is marked by next_retval.lr_linkid set to DATALINK_INVALID_LINKID. + * If successful the result is stored in next_retval and 0 returned. + * On errors, return -1 and set `errno'. + */ +static int +dl_get_next(int door_fd, datalink_id_t linkid, datalink_class_t class, + datalink_media_t dmedia, uint32_t flags, + dlmgmt_getnext_retval_t *next_retval) +{ + size_t next_sz = sizeof (*next_retval); + dlmgmt_door_getnext_t getnext; + bzero(&getnext, sizeof (dlmgmt_door_getnext_t)); + getnext.ld_cmd = DLMGMT_CMD_GETNEXT; + getnext.ld_class = class; + getnext.ld_dmedia = dmedia; + getnext.ld_flags = flags; + getnext.ld_linkid = linkid; + + if (dl_door_call(door_fd, &getnext, sizeof (getnext), next_retval, + &next_sz) < 0) { + return (-1); + } + if (next_retval->lr_err != 0) { + errno = next_retval->lr_err; + return (-1); + } + return (0); +} + /* * Returns all addresses configured on the system. If flags contain * LIFC_ENABLED, only the addresses that are UP are returned. @@ -97,19 +238,33 @@ getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags) int ret; int s, n, numifs; struct ifaddrs *curr, *prev; + struct sockaddr_dl *ifa_addr = NULL; + if_data_t *ifa_data = NULL; sa_family_t lifr_af; - int sock4; - int sock6; + datalink_id_t linkid; + dld_ioc_attr_t dia; + dld_macaddrinfo_t *dmip; + dld_ioc_macaddrget_t *iomp = NULL; + dlmgmt_getnext_retval_t next_retval; + dlmgmt_getname_retval_t name_retval; + int bufsize; + int nmacaddr = 1024; + int sock4 = -1; + int sock6 = -1; + int door_fd = -1; + int dld_fd = -1; int err; - if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - return (-1); - if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - err = errno; - close(sock4); - errno = err; - return (-1); - } + if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || + (sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 || + (door_fd = open(DLMGMT_DOOR, O_RDONLY)) < 0 || + (dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + goto fail; + + bufsize = sizeof (dld_ioc_macaddrget_t) + nmacaddr * + sizeof (dld_macaddrinfo_t); + if ((iomp = calloc(1, bufsize)) == NULL) + goto fail; retry: /* Get all interfaces from SIOCGLIFCONF */ @@ -197,19 +352,114 @@ retry: } } + + /* add AF_LINK entries */ + if (af == AF_UNSPEC || af == AF_LINK) { + + linkid = DATALINK_INVALID_LINKID; + for (;;) { + if (dl_get_next(door_fd, linkid, DATALINK_CLASS_ALL, + DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE, + &next_retval) != 0) { + break; + } + + linkid = next_retval.lr_linkid; + if (linkid == DATALINK_INVALID_LINKID) + break; + + /* get mac addr */ + iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t); + iomp->dig_linkid = linkid; + + if (ioctl(dld_fd, DLDIOC_MACADDRGET, iomp) < 0) + continue; + + dmip = (dld_macaddrinfo_t *)(iomp + 1); + + /* get name */ + if (dl_get_name(door_fd, linkid, &name_retval) != 0) + continue; + + /* get MTU */ + dia.dia_linkid = linkid; + if (ioctl(dld_fd, DLDIOC_ATTR, &dia) < 0) + continue; + + curr = calloc(1, sizeof (struct ifaddrs)); + if (curr == NULL) + goto fail; + + curr->ifa_flags = prev->ifa_flags; + prev->ifa_next = curr; + prev = curr; + + if ((curr->ifa_name = strdup(name_retval.lr_link)) == + NULL) + goto fail; + + curr->ifa_addr = + calloc(1, sizeof (struct sockaddr_storage)); + if (curr->ifa_addr == NULL) + goto fail; + + curr->ifa_data = calloc(1, sizeof (if_data_t)); + if (curr->ifa_data == NULL) + goto fail; + + curr->ifa_addr->sa_family = AF_LINK; + ifa_addr = (struct sockaddr_dl *)curr->ifa_addr; + ifa_data = curr->ifa_data; + + (void) memcpy(ifa_addr->sdl_data, dmip->dmi_addr, + dmip->dmi_addrlen); + ifa_addr->sdl_alen = dmip->dmi_addrlen; + + ifa_data->ifi_mtu = dia.dia_max_sdu; + ifa_data->ifi_type = dlpi_iftype(next_retval.lr_media); + + /* + * get interface index + * This is only possible if the link has been plumbed. + */ + if (strlcpy(lifrl.lifr_name, name_retval.lr_link, + sizeof (lifrl.lifr_name)) >= + sizeof (lifrl.lifr_name)) + continue; + + if (ioctl(sock4, SIOCGLIFINDEX, (caddr_t)&lifrl) >= 0) { + ifa_addr->sdl_index = lifrl.lifr_index; + } else if (ioctl(sock6, SIOCGLIFINDEX, + (caddr_t)&lifrl) >= 0) { + /* retry for IPv6 */ + ifa_addr->sdl_index = lifrl.lifr_index; + } + } + } free(buf); - close(sock4); - close(sock6); + free(iomp); + (void) close(sock4); + (void) close(sock6); + (void) close(door_fd); + (void) close(dld_fd); return (0); fail: err = errno; free(buf); + free(iomp); freeifaddrs(*ifap); *ifap = NULL; if (err == ENXIO) goto retry; - close(sock4); - close(sock6); + + if (sock4 != -1) + (void) close(sock4); + if (sock6 != -1) + (void) close(sock6); + if (door_fd != -1) + (void) close(door_fd); + if (dld_fd != -1) + (void) close(dld_fd); errno = err; return (-1); } diff --git a/usr/src/man/man3socket/getifaddrs.3socket b/usr/src/man/man3socket/getifaddrs.3socket index 69324271af..4b9c40b7f5 100644 --- a/usr/src/man/man3socket/getifaddrs.3socket +++ b/usr/src/man/man3socket/getifaddrs.3socket @@ -12,7 +12,7 @@ .\" .\" Copyright (c) 2013, Joyent, Inc. All rights reserved. .\" -.TH GETIFADDRS 3SOCKET "Apr 18, 2013" +.TH GETIFADDRS 3SOCKET "May 18, 2017" .SH NAME getifaddrs, freeifaddrs \- get interface addresses .SH SYNOPSIS @@ -97,7 +97,9 @@ for more information. .sp .LP -The \fIifa_data\fR member is presently unused. +The \fIifa_data\fR member is specific to the address family. It is currently +only available for AF_LINK entries where it contains a pointer to the +\fBstruct if_data\fR (as defined in \fBif.h\fR(3HEAD)). .sp .LP @@ -135,11 +137,9 @@ MT-Level MT-Safe .SH NOTES .LP -On an illumos system, this function lists only interfaces with the \fBIFF_UP\fR -flag set; see \fBif_tcp\fR(7P) and \fBifconfig\fR(1M) for more information. +This function lists interfaces of type AF_INET, AF_INET6, and AF_LINK. +For AF_INET and AF_INET6 only interfaces with the \fBIFF_UP\fR +flag set are listed; see \fBif_tcp\fR(7P) and \fBifconfig\fR(1M) for more +information. For AF_LINK entries the interface index is only available when the +link is plumbed. -.SH BUGS -.LP -At present, this function only lists addresses from the \fBAF_INET\fR and -\fBAF_INET6\fR families. Other families, such as \fBAF_LINK\fR, are not -included. -- 2.11.4.GIT