/* ====================================================================== */ int getifaddrs(struct ifaddrs **ifap) { int sd; struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; /* - - - - - - - - - - - - - - - */ int icnt; size_t dlen, xlen, nlen; uint32_t max_ifindex = 0; pid_t pid = getpid(); int seq; int result; int build ; /* 0 or 1 */ /* ---------------------------------- */ /* initialize */ icnt = dlen = xlen = nlen = 0; nlmsg_list = nlmsg_end = NULL; if (ifap) *ifap = NULL; /* ---------------------------------- */ /* open socket and bind */ sd = nl_open(); if (sd < 0) return -1; /* ---------------------------------- */ /* gather info */ if ((seq = nl_getlist(sd, 0, RTM_GETLINK, &nlmsg_list, &nlmsg_end)) < 0){ free_nlmsglist(nlmsg_list); nl_close(sd); return -1; } if ((seq = nl_getlist(sd, seq+1, RTM_GETADDR, &nlmsg_list, &nlmsg_end)) < 0){ free_nlmsglist(nlmsg_list); nl_close(sd); return -1; } /* ---------------------------------- */ /* Estimate size of result buffer and fill it */ for (build=0; build<=1; build++){ struct ifaddrs *ifl = NULL, *ifa = NULL; struct nlmsghdr *nlh, *nlh0; void *data = NULL, *xdata = NULL, *ifdata = NULL; char *ifname = NULL, **iflist = NULL; uint16_t *ifflist = NULL; struct rtmaddr_ifamap ifamap; if (build){ ifa = data = calloc(1, NLMSG_ALIGN(sizeof(struct ifaddrs[icnt])) + dlen + xlen + nlen); ifdata = calloc(1, NLMSG_ALIGN(sizeof(char *[max_ifindex+1])) + NLMSG_ALIGN(sizeof(uint16_t [max_ifindex+1]))); if (ifap != NULL) *ifap = (ifdata != NULL) ? ifa : NULL; else{ free_data(data, ifdata); result = 0; break; } if (data == NULL || ifdata == NULL){ free_data(data, ifdata); result = -1; break; } ifl = NULL; data += NLMSG_ALIGN(sizeof(struct ifaddrs)) * icnt; xdata = data + dlen; ifname = xdata + xlen; iflist = ifdata; ifflist = ((void *)iflist) + NLMSG_ALIGN(sizeof(char *[max_ifindex+1])); } for (nlm=nlmsg_list; nlm; nlm=nlm->nlm_next){ int nlmlen = nlm->size; if (!(nlh0 = nlm->nlh)) continue; for (nlh = nlh0; NLMSG_OK(nlh, nlmlen); nlh=NLMSG_NEXT(nlh,nlmlen)){ struct ifinfomsg *ifim = NULL; struct ifaddrmsg *ifam = NULL; struct rtattr *rta; size_t nlm_struct_size = 0; sa_family_t nlm_family = 0; uint32_t nlm_scope = 0, nlm_index = 0; #ifndef IFA_NETMASK size_t sockaddr_size = 0; uint32_t nlm_prefixlen = 0; #endif size_t rtasize; memset(&ifamap, 0, sizeof(ifamap)); /* check if the message is what we want */ if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq) continue; if (nlh->nlmsg_type == NLMSG_DONE){ break; /* ok */ } switch (nlh->nlmsg_type){ case RTM_NEWLINK: ifim = (struct ifinfomsg *)NLMSG_DATA(nlh); nlm_struct_size = sizeof(*ifim); nlm_family = ifim->ifi_family; nlm_scope = 0; nlm_index = ifim->ifi_index; nlm_prefixlen = 0; if (build) ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags; break; case RTM_NEWADDR: ifam = (struct ifaddrmsg *)NLMSG_DATA(nlh); nlm_struct_size = sizeof(*ifam); nlm_family = ifam->ifa_family; nlm_scope = ifam->ifa_scope; nlm_index = ifam->ifa_index; nlm_prefixlen = ifam->ifa_prefixlen; if (build) ifa->ifa_flags = ifflist[nlm_index]; break; default: continue; } if (!build){ if (max_ifindex < nlm_index) max_ifindex = nlm_index; } else { if (ifl != NULL) ifl->ifa_next = ifa; } rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size); for (rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size)); RTA_OK(rta, rtasize); rta = RTA_NEXT(rta, rtasize)){ struct sockaddr **sap = NULL; void *rtadata = RTA_DATA(rta); size_t rtapayload = RTA_PAYLOAD(rta); socklen_t sa_len; switch(nlh->nlmsg_type){ case RTM_NEWLINK: switch(rta->rta_type){ case IFLA_ADDRESS: case IFLA_BROADCAST: if (build){ sap = (rta->rta_type == IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa->ifa_broadaddr; *sap = (struct sockaddr *)data; } sa_len = ifa_sa_len(AF_PACKET, rtapayload); if (rta->rta_type == IFLA_ADDRESS) sockaddr_size = NLMSG_ALIGN(sa_len); if (!build){ dlen += NLMSG_ALIGN(sa_len); } else { memset(*sap, 0, sa_len); ifa_make_sockaddr(AF_PACKET, *sap, rtadata,rtapayload, 0,0); ((struct sockaddr_ll *)*sap)->sll_ifindex = nlm_index; ((struct sockaddr_ll *)*sap)->sll_hatype = ifim->ifi_type; data += NLMSG_ALIGN(sa_len); } break; case IFLA_IFNAME:/* Name of Interface */ if (!build) nlen += NLMSG_ALIGN(rtapayload + 1); else{ ifa->ifa_name = ifname; if (iflist[nlm_index] == NULL) iflist[nlm_index] = ifa->ifa_name; strncpy(ifa->ifa_name, rtadata, rtapayload); ifa->ifa_name[rtapayload] = '\0'; ifname += NLMSG_ALIGN(rtapayload + 1); } break; case IFLA_STATS:/* Statistics of Interface */ if (!build) xlen += NLMSG_ALIGN(rtapayload); else{ ifa->ifa_data = xdata; memcpy(ifa->ifa_data, rtadata, rtapayload); xdata += NLMSG_ALIGN(rtapayload); } break; case IFLA_UNSPEC: break; case IFLA_MTU: break; case IFLA_LINK: break; case IFLA_QDISC: break; } break; case RTM_NEWADDR: if (nlm_family == AF_PACKET) break; switch(rta->rta_type){ case IFA_ADDRESS: ifamap.address = rtadata; ifamap.address_len = rtapayload; break; case IFA_LOCAL: ifamap.local = rtadata; ifamap.local_len = rtapayload; break; case IFA_BROADCAST: ifamap.broadcast = rtadata; ifamap.broadcast_len = rtapayload; break; #ifdef HAVE_IFADDRS_IFA_ANYCAST case IFA_ANYCAST: ifamap.anycast = rtadata; ifamap.anycast_len = rtapayload; break; #endif case IFA_LABEL: if (!build) nlen += NLMSG_ALIGN(rtapayload + 1); else{ ifa->ifa_name = ifname; if (iflist[nlm_index] == NULL) iflist[nlm_index] = ifname; strncpy(ifa->ifa_name, rtadata, rtapayload); ifa->ifa_name[rtapayload] = '\0'; ifname += NLMSG_ALIGN(rtapayload + 1); } break; case IFA_UNSPEC: break; case IFA_CACHEINFO: break; } } } if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) { if (!ifamap.local) { ifamap.local = ifamap.address; ifamap.local_len = ifamap.address_len; } if (!ifamap.address) { ifamap.address = ifamap.local; ifamap.address_len = ifamap.local_len; } if (ifamap.address_len != ifamap.local_len || (ifamap.address != NULL && memcmp(ifamap.address, ifamap.local, ifamap.address_len))) { /* p2p; address is peer and local is ours */ ifamap.broadcast = ifamap.address; ifamap.broadcast_len = ifamap.address_len; ifamap.address = ifamap.local; ifamap.address_len = ifamap.local_len; } if (ifamap.address) { #ifndef IFA_NETMASK sockaddr_size = NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len)); #endif if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len)); else { ifa->ifa_addr = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_addr, ifamap.address, ifamap.address_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.address_len)); } } #ifdef IFA_NETMASK if (ifamap.netmask) { if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.netmask_len)); else { ifa->ifa_netmask = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_netmask, ifamap.netmask, ifamap.netmask_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.netmask_len)); } } #endif if (ifamap.broadcast) { if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.broadcast_len)); else { ifa->ifa_broadaddr = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_broadaddr, ifamap.broadcast, ifamap.broadcast_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.broadcast_len)); } } #ifdef HAVE_IFADDRS_IFA_ANYCAST if (ifamap.anycast) { if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.anycast_len)); else { ifa->ifa_anycast = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_anyaddr, ifamap.anycast, ifamap.anycast_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.anycast_len)); } } #endif } if (!build){ #ifndef IFA_NETMASK dlen += sockaddr_size; #endif icnt++; } else { if (ifa->ifa_name == NULL) ifa->ifa_name = iflist[nlm_index]; #ifndef IFA_NETMASK if (ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_UNSPEC && ifa->ifa_addr->sa_family != AF_PACKET){ ifa->ifa_netmask = (struct sockaddr *)data; ifa_make_sockaddr_mask(ifa->ifa_addr->sa_family, ifa->ifa_netmask, nlm_prefixlen); } data += sockaddr_size; #endif ifl = ifa++; } } } if (!build){ if (icnt == 0 && (dlen + nlen + xlen == 0)){ if (ifap != NULL) *ifap = NULL; break; /* cannot found any addresses */ } } else free_data(NULL, ifdata); } /* ---------------------------------- */ /* Finalize */ free_nlmsglist(nlmsg_list); nl_close(sd); return 0; }
/* ====================================================================== */ int ni_ifaddrs(struct ni_ifaddrs **ifap, sa_family_t family) { int sd; struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; /* - - - - - - - - - - - - - - - */ int icnt; size_t dlen, xlen; uint32_t max_ifindex = 0; pid_t pid = getpid(); int seq = 0; int result; int build; /* 0 or 1 */ /* ---------------------------------- */ /* initialize */ icnt = dlen = xlen = 0; nlmsg_list = nlmsg_end = NULL; if (ifap) *ifap = NULL; /* ---------------------------------- */ /* open socket and bind */ sd = nl_open(); if (sd < 0) return -1; /* ---------------------------------- */ /* gather info */ if ((seq = nl_getlist(sd, seq + 1, RTM_GETADDR, &nlmsg_list, &nlmsg_end)) < 0) { free_nlmsglist(nlmsg_list); nl_close(sd); return -1; } /* ---------------------------------- */ /* Estimate size of result buffer and fill it */ for (build = 0; build <= 1; build++) { struct ni_ifaddrs *ifl = NULL, *ifa = NULL; struct nlmsghdr *nlh, *nlh0; void *data = NULL, *xdata = NULL; uint16_t *ifflist = NULL; #ifndef IFA_LOCAL struct rtmaddr_ifamap ifamap; #endif if (build) { ifa = data = calloc(1, NLMSG_ALIGN(sizeof(struct ni_ifaddrs[icnt])) + dlen + xlen); if (ifap != NULL) *ifap = ifa; else { free_data(data); result = 0; break; } if (data == NULL) { free_data(data); result = -1; break; } ifl = NULL; data += NLMSG_ALIGN(sizeof(struct ni_ifaddrs)) * icnt; xdata = data + dlen; ifflist = xdata + xlen; } for (nlm = nlmsg_list; nlm; nlm = nlm->nlm_next) { int nlmlen = nlm->size; if (!(nlh0 = nlm->nlh)) continue; for (nlh = nlh0; NLMSG_OK(nlh, nlmlen); nlh = NLMSG_NEXT(nlh, nlmlen)) { struct ifaddrmsg *ifam = NULL; struct rtattr *rta; size_t nlm_struct_size = 0; sa_family_t nlm_family = 0; uint32_t nlm_scope = 0, nlm_index = 0; unsigned int nlm_flags; size_t rtasize; #ifndef IFA_LOCAL memset(&ifamap, 0, sizeof(ifamap)); #endif /* check if the message is what we want */ if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq) continue; if (nlh->nlmsg_type == NLMSG_DONE) { break; /* ok */ } switch (nlh->nlmsg_type) { case RTM_NEWADDR: ifam = (struct ifaddrmsg *) NLMSG_DATA(nlh); nlm_struct_size = sizeof(*ifam); nlm_family = ifam->ifa_family; nlm_scope = ifam->ifa_scope; nlm_index = ifam->ifa_index; nlm_flags = ifam->ifa_flags; if (family && nlm_family != family) continue; if (build) { ifa->ifa_ifindex = nlm_index; ifa->ifa_flags = nlm_flags; } break; default: continue; } if (!build) { if (max_ifindex < nlm_index) max_ifindex = nlm_index; } else { if (ifl != NULL) ifl->ifa_next = ifa; } rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size); for (rta = (struct rtattr *) (((char *) NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size)); RTA_OK(rta, rtasize); rta = RTA_NEXT(rta, rtasize)) { void *rtadata = RTA_DATA(rta); size_t rtapayload = RTA_PAYLOAD(rta); switch (nlh->nlmsg_type) { case RTM_NEWADDR: if (nlm_family == AF_PACKET) break; switch (rta->rta_type) { #ifndef IFA_LOCAL case IFA_ADDRESS: ifamap.address = rtadata; ifamap.address_len = rtapayload; break; case IFA_LOCAL: ifamap.local = rtadata; ifamap.local_len = rtapayload; break; case IFA_BROADCAST: ifamap.broadcast = rtadata; ifamap.broadcast_len = rtapayload; break; case IFA_LABEL: break; case IFA_UNSPEC: break; #else case IFA_LOCAL: if (!build) dlen += NLMSG_ALIGN(rtapayload); else { memcpy(data, rtadata, rtapayload); ifa->ifa_addr = data; data += NLMSG_ALIGN(rtapayload); } break; #endif case IFA_CACHEINFO: if (!build) xlen += NLMSG_ALIGN(rtapayload); else { memcpy(xdata, rtadata, rtapayload); ifa->ifa_cacheinfo = xdata; xdata += NLMSG_ALIGN(rtapayload); } break; } } } #ifndef IFA_LOCAL if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) { if (!ifamap.local) { ifamap.local = ifamap.address; ifamap.local_len = ifamap.address_len; } if (!ifamap.address) { ifamap.address = ifamap.local; ifamap.address_len = ifamap.local_len; } if (ifamap.address_len != ifamap.local_len || (ifamap.address != NULL && memcmp(ifamap.address, ifamap.local, ifamap.address_len))) { /* p2p; address is peer and local is ours */ ifamap.broadcast = ifamap.address; ifamap.broadcast_len = ifamap.address_len; ifamap.address = ifamap.local; ifamap.address_len = ifamap.local_len; } if (ifamap.address) { if (!build) dlen += NLMSG_ALIGN(ifamap.address_len); else { ifa->ifa_addr = (struct sockaddr *) data; memcpy(ifa->ifa_addr, ifamap.address, ifamap.address_len); data += NLMSG_ALIGN(ifamap.address_len); } } } #endif if (!build) { icnt++; } else { ifl = ifa++; } } } if (!build) { if (icnt == 0 && (dlen + xlen == 0)) { if (ifap != NULL) *ifap = NULL; break; /* cannot found any addresses */ } } } /* ---------------------------------- */ /* Finalize */ free_nlmsglist(nlmsg_list); nl_close(sd); return 0; }