static void ifa_make_sockaddr(sa_family_t family, struct sockaddr *sa, void *p, size_t len, uint32_t scope, uint32_t scopeid) { if (sa == NULL) return; switch(family){ case AF_INET: memcpy(&((struct sockaddr_in*)sa)->sin_addr, (char *)p, len); break; case AF_INET6: memcpy(&((struct sockaddr_in6*)sa)->sin6_addr, (char *)p, len); if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p)){ ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid; } break; case AF_PACKET: memcpy(((struct sockaddr_ll*)sa)->sll_addr, (char *)p, len); ((struct sockaddr_ll*)sa)->sll_halen = len; break; default: memcpy(sa->sa_data, p, len); /*XXX*/ break; } sa->sa_family = family; #ifdef HAVE_SOCKADDR_SA_LEN sa->sa_len = ifa_sa_len(family, len); #endif }
static struct sockaddr * ifa_make_sockaddr_mask (sa_family_t family, struct sockaddr *sa, uint32_t prefixlen) { int i; char *p = NULL, c; uint32_t max_prefixlen = 0; if (sa == NULL) return NULL; switch (family) { case AF_INET: memset (&((struct sockaddr_in *) sa)->sin_addr, 0, sizeof (((struct sockaddr_in *) sa)->sin_addr)); p = (char *) &((struct sockaddr_in *) sa)->sin_addr; max_prefixlen = 32; break; case AF_INET6: memset (&((struct sockaddr_in6 *) sa)->sin6_addr, 0, sizeof (((struct sockaddr_in6 *) sa)->sin6_addr)); p = (char *) &((struct sockaddr_in6 *) sa)->sin6_addr; #if 0 /* XXX: fill scope-id? */ if (IN6_IS_ADDR_LINKLOCAL (p) || IN6_IS_ADDR_MC_LINKLOCAL (p)) { ((struct sockaddr_in6 *) sa)->sin6_scope_id = scopeid; } #endif max_prefixlen = 128; break; default: return NULL; } sa->sa_family = family; #ifdef HAVE_SOCKADDR_SA_LEN sa->sa_len = ifa_sa_len (family, len); #endif if (p) { if (prefixlen > max_prefixlen) prefixlen = max_prefixlen; for (i = 0; i < (prefixlen / 8); i++) *p++ = 0xff; c = 0xff; c <<= (8 - (prefixlen % 8)); *p = c; } return sa; }
/* ====================================================================== */ 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 usagi_getifaddrs (struct ifaddrs **ifap) { struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; size_t dlen, xlen, nlen; int build; for (build = 0; build <= 1; build++) { struct ifaddrs *ifl = ((void *)0), *ifa = ((void *)0); struct nlmsghdr *nlh, *nlh0; uint16_t *ifflist = ((void *)0); struct rtmaddr_ifamap ifamap; for (nlm = nlmsg_list; nlm; nlm = nlm->nlm_next) { int nlmlen = nlm->size; for (nlh = nlh0; ((nlmlen) >= (int)sizeof(struct nlmsghdr) && (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && (nlh)->nlmsg_len <= (nlmlen)); nlh = ((nlmlen) -= ( (((nlh)->nlmsg_len)+4U -1) & ~(4U -1) ), (struct nlmsghdr*)(((char*)(nlh)) + ( (((nlh)->nlmsg_len)+4U -1) & ~(4U -1) )))) { struct ifinfomsg *ifim = ((void *)0); struct ifaddrmsg *ifam = ((void *)0); struct rtattr *rta; sa_family_t nlm_family = 0; uint32_t nlm_scope = 0, nlm_index = 0; memset (&ifamap, 0, sizeof (ifamap)); switch (nlh->nlmsg_type) { case RTM_NEWLINK: ifim = (struct ifinfomsg *) ((void*)(((char*)nlh) + ((0)+( ((((int) ( ((sizeof(struct nlmsghdr))+4U -1) & ~(4U -1) )))+4U -1) & ~(4U -1) )))); case RTM_NEWADDR: ifam = (struct ifaddrmsg *) ((void*)(((char*)nlh) + ((0)+( ((((int) ( ((sizeof(struct nlmsghdr))+4U -1) & ~(4U -1) )))+4U -1) & ~(4U -1) )))); nlm_family = ifam->ifa_family; if (build) ifa->ifa_flags = ifflist[nlm_index]; break; default: continue; } if (!build) { void *rtadata = ((void*)(((char*)(rta)) + (( ((sizeof(struct rtattr))+4 -1) & ~(4 -1) ) + (0)))); size_t rtapayload = ((int)((rta)->rta_len) - (( ((sizeof(struct rtattr))+4 -1) & ~(4 -1) ) + (0))); switch (nlh->nlmsg_type) { case RTM_NEWLINK: break; case RTM_NEWADDR: if (nlm_family == 17) break; switch (rta->rta_type) { case IFA_ADDRESS: ifamap.address = rtadata; ifamap.address_len = rtapayload; case IFA_LOCAL: ifamap.local = rtadata; } } } if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != 17) { if (!ifamap.local) { ifamap.local = ifamap.address; ifamap.local_len = ifamap.address_len; } if (!ifamap.address) { ifamap.address = ifamap.local; } if (ifamap.address_len != ifamap.local_len || (ifamap.address != ((void *)0) && memcmp (ifamap.address, ifamap.local, ifamap.address_len))) { if (!build) dlen += (((ifa_sa_len (nlm_family, ifamap.address_len))+4U -1) & ~(4U -1) ); } } } } } }