#include "common.h" unsigned int seqno; // PKS, pthread_self() = pointer to memory #define NETLINKID() ((getpid() << 16) | gettid()) typedef int (*netlink_cb_t)(struct nlmsghdr *nh, void *data); void address_calculate_netmask(struct iface_address *address, int ifa_prefixlen); void iface_entry_append_address(struct iface_entry *iface, struct iface_address *address); /* * Open a netlink socket. Maybe in the future we'll support another type. */ int netlink_socket(int type) { int fd = -1; struct sockaddr_nl snl; memset(&snl, 0, sizeof(struct sockaddr_nl)); dprintf("requesting netlink socket"); fd = socket(AF_NETLINK, SOCK_RAW, type); if(fd == -1) { dprintf("failed with %s", strerror(errno)); return -1; } snl.nl_family = AF_NETLINK; // some systems require pid to 0 snl.nl_pid = 0; if(bind(fd, (void *)&snl, sizeof(struct sockaddr_nl)) == -1) { dprintf("Failed to bind to netlink socket: %s", strerror(errno)); close(fd); return -1; } // let's add some random to seqno seqno = time(NULL); dprintf("fd %d is a suitable netlink socket", fd); return fd; } int netlink_request(int fd, int family, int type) { // send at least 16 bytes of data, some old kernels want it unsigned char buf[sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg)+15]; struct nlmsghdr *nh; struct rtgenmsg *ng; struct sockaddr_nl snl; struct iovec iov; nh = (struct nlmsghdr *)(buf); ng = (struct rtgenmsg *)(buf + sizeof(struct nlmsghdr)); dprintf("Setting up netlink request"); memset(&snl, 0, sizeof(struct sockaddr_nl)); memset(buf, 0, sizeof(buf)); snl.nl_family = AF_NETLINK; // I keep auto typing AF_INET :~( nh->nlmsg_len = NLMSG_LENGTH(sizeof(buf) - sizeof(struct nlmsghdr)); nh->nlmsg_type = type; // NLM_F_ROOT Return the complete table instead of a single entry. // Create, remove or receive information about a network route. These // messages contain an rtmsg structure with an optional sequence of // rtattr structures following. For RTM_GETROUTE setting rtm_dst_len // and rtm_src_len to 0 means you get all entries for the specified // routing table. For the other fields except rtm_table and // rtm_protocol 0 is the wildcard. nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; // NLM_F_ACK Request for an acknowledgment on success, maybe an idea. // would need to implement re-sending if it fails, etc. // for now we will assume it's reliable (even though the docs say it's not) //nh->nlmsg_pid = NETLINKID(); // some systems require pid to 0 nh->nlmsg_pid = 0; nh->nlmsg_seq = __atomic_inc(&seqno); ng->rtgen_family = family; dprintf("Sending request"); if(sendto(fd, buf, sizeof(buf), 0, (void *)(&snl), sizeof(struct sockaddr_nl)) == -1) { dprintf("Failed to send netlink request. Got %s", strerror(errno)); return -1; } dprintf("Request sent"); return seqno; // XXX, may wrap, etc. just use zero? } // man 7 netlink int netlink_parse(int fd, int seq, netlink_cb_t callback, void *data) { int len; int status; int end = 0; unsigned char buf[4096]; struct sockaddr_nl snl; struct msghdr msg; struct iovec iov = { buf, sizeof(buf) }; struct nlmsghdr *nh; memset(&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; msg.msg_name = (void *)&snl; msg.msg_namelen = sizeof(struct sockaddr_nl); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; status = 0; do { len = recvmsg(fd, &msg, 0); dprintf("recvmsg returned %d", len); //debug received data #if 0 int i; unsigned char buff_str[16192]; memset(buff_str,0,16192); for(i=0;i<len;i++) { sprintf(buff_str,"%s%02X ",buff_str,buf[i]); if (i%32 == 0 && i!= 0) strcat(buff_str,"\n"); } dprintf("\n%s",buff_str); #endif if(len <= 0) { status = errno; dprintf("socket dead? bailing (%s)", strerror(errno)); break; } if(msg.msg_flags & MSG_TRUNC) { dprintf("truncated message ? :("); status = ERROR_NOT_SUPPORTED; break; } for(nh = (struct nlmsghdr *)(buf); NLMSG_OK(nh, len); nh = (struct nlmsghdr *) NLMSG_NEXT(nh, len)) { //dprintf("buf = %p, nh = %p", buf, nh); //dprintf("nh->nlmsg_type = %d", nh->nlmsg_type); if(nh->nlmsg_type == NLMSG_DONE) { end = 1; break; } if(nh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *me = (struct nlmsgerr *) NLMSG_DATA (nh); //dprintf("in NLMSG_ERROR handling.. me = %p", me); //dprintf("me->error = %d", me->error); if(me->error) { //dprintf("so, we have: nlmsg_len: %d, nlmsg_type: %d, nlmsg_flags: %d, nlmsg_seq: %d, nlmsg_pid: %d", // me->msg.nlmsg_len, me->msg.nlmsg_type, me->msg.nlmsg_flags, // me->msg.nlmsg_seq, me->msg.nlmsg_pid); if(me->msg.nlmsg_seq == seq) { dprintf("Hum. kernel doesn't like our message :~("); status = ERROR_NOT_SUPPORTED; break; } dprintf("don't know how to handle this error at the moment. continuing"); } continue; // "yea, whatever" } // dprintf("dispatching into callback"); status = callback(nh, data); if(status) { dprintf("callback returned non zero(%d) , stopping process", status); break; } } } while(!end); return status; } // returns 1 if rm seems to be an rtmsg int likely_rtmsg(struct rtmsg *rm) { if ( (rm->rtm_family == AF_INET && rm->rtm_dst_len >= 0 && rm->rtm_dst_len <= 32) || (rm->rtm_family == AF_INET6 && rm->rtm_dst_len >= 0 && rm->rtm_dst_len <= 128) ) return 1; else return 0; } int netlink_parse_routing_table(struct nlmsghdr *nh, void *data) { struct routing_table * rt_table = ( struct routing_table *) data; struct ipv4_routing_table **table_v4 = rt_table->table_ipv4; struct ipv6_routing_table **table_v6 = rt_table->table_ipv6; struct ipv4_routing_table *tmp; struct ipv6_routing_table *tmp6; struct ipv4_route_entry *re; struct ipv6_route_entry *re6; struct rtmsg *rm; struct rtattr *ra; int len; int newsize; unsigned char is_ipv6; unsigned char int_name[IFNAMSIZ+1]; uint32_t interface_index, metric; __u32 dest, netmask, nexthop; __u32 *what; __u128 dest6, netmask6, nexthop6; unsigned char *what6; dest = netmask = nexthop = metric = 0; memset(&dest6, 0, sizeof(__u128)); memset(&netmask6, 0, sizeof(__u128)); memset(&nexthop6, 0, sizeof(__u128)); memset(int_name, 0, IFNAMSIZ+1); if(nh->nlmsg_type != RTM_NEWROUTE) { dprintf("got %d instead of RTM_NEWROUTE (%d)", nh->nlmsg_type, RTM_NEWROUTE); return 0; } rm = NLMSG_DATA(nh); // stumbled upon an old system with 4 bytes padding of 0 between nlmsghdr and rtmsg, try to detect it if(!likely_rtmsg(rm)) { rm = (struct rtmsg *)((unsigned char *)rm + 4); dprintf("Adjusted rm at +4"); } //dprintf("rtm_family : 0x%x , rtm_dst_len : 0x%x, rtm_src_len : 0x%x",rm->rtm_family, rm->rtm_dst_len, rm->rtm_src_len); // print directly connected routes if(rm->rtm_type != RTN_UNICAST && rm->rtm_type != RTN_LOCAL) { dprintf("got %d instead of RTN_UNICAST (%d) or RTN_LOCAL (%d)", rm->rtm_type, RTN_UNICAST,RTN_LOCAL); return 0; } if(rm->rtm_family != AF_INET && rm->rtm_family != AF_INET6) { dprintf("dunno what on earth to do with a rtm_family of %d governor", rm->rtm_family); return 0; } if(rm->rtm_flags & (RTM_F_CLONED | RTM_F_EQUALIZE)) { dprintf("cloned / equalized .. doesn't sound good. skipping for now"); return 0; } if(rm->rtm_table == RT_TABLE_LOCAL) { dprintf("don't want to parse local routing table"); return 0; } if (rm->rtm_family == AF_INET) is_ipv6 = 0; else is_ipv6 = 1; //dprintf("nh->nlmsg_len: %d, NLMSG_LENGTH(sizeof(..)): %d, is_ipv6 : %d", nh->nlmsg_len, NLMSG_LENGTH(sizeof(struct rtmsg)),is_ipv6); len = nh->nlmsg_len - NLMSG_LENGTH (sizeof(struct rtmsg)); if(len <= 0) { dprintf("back to the drawing board it seems"); return 0; } //dprintf("RTA_DST=%d, RTA_SRC=%d, RTA_GATEWAY=%d, RTA_PREFSRC=%d, RTA_OIF=%d, RTA_PRIORITY=%d", RTA_DST, RTA_SRC, RTA_GATEWAY, RTA_PREFSRC,RTA_OIF, RTA_PRIORITY); //dprintf("rtm_table : %d, RT_TABLE_UNSPEC=%d, RT_TABLE_DEFAULT =%d, RT_TABLE_MAIN=%d,RT_TABLE_LOCAL=%d", rm->rtm_table,RT_TABLE_UNSPEC, RT_TABLE_DEFAULT , RT_TABLE_MAIN, RT_TABLE_LOCAL); //dprintf("rtm_type : %d, RTN_UNICAST=%d, RTN_LOCAL=%d", rm->rtm_type,RTN_UNICAST, RTN_LOCAL); // okay, so. // for(ra = (struct rtattr *) RTM_RTA(rm) ; RTA_OK(ra, len); ra = (struct rtattr *) RTA_NEXT(ra, len)) { if (is_ipv6) { what6 = (unsigned char *) RTA_DATA(ra); //dprintf("ra @ %p, type = %d, length = %d, payload = %d, payload data = %08x %08x %08x %08x", ra, ra->rta_type, ra->rta_len, RTA_PAYLOAD(ra), *(__u32 *)what6, *(__u32 *)(what6+4), *(__u32 *)(what6+8), *(__u32 *)(what6+12)); } else { what = (__u32 *) RTA_DATA(ra); //dprintf("ra @ %p, type = %d, length = %d, payload = %d, payload data = %08x", ra, ra->rta_type, ra->rta_len, RTA_PAYLOAD(ra), *what); } switch(ra->rta_type) { case RTA_DST: if (is_ipv6) memcpy(&dest6,what6, sizeof(__u128)); else dest = *what; break; case RTA_GATEWAY: if (is_ipv6) memcpy(&nexthop6,what6, sizeof(__u128)); else nexthop = *what; break; case RTA_OIF: interface_index = *(uint32_t *)RTA_DATA(ra); if_indextoname(interface_index, int_name); break; case RTA_PRIORITY: // metric is a uint16_t but we must transmit 32bits integers metric = (*(uint32_t *)RTA_DATA(ra))&0x0000ffff; break; } } //dprintf("and while you're here, rtm_dst_len = %d", rm->rtm_dst_len); if (is_ipv6) { // if netmask is FFFFFFFF FFFFFFFF 00000000 00000000 (/64), netmask6.a1 and netmask6.a2 == 0xffffffff, and nestmask6.a3 and .a4 == 0 // netmask6 is set to 0 at the beginning of the function, no need to reset the values to 0 if it is needed // XXX really ugly, but works if (rm->rtm_dst_len >= 96) { netmask6.a4 = (rm->rtm_dst_len == 96) ? 0 : htonl(0xffffffff<<(32-(rm->rtm_dst_len%32))); netmask6.a1 = netmask6.a2 = netmask6.a3 = 0xffffffff; } else if (rm->rtm_dst_len >= 64) { netmask6.a3 = (rm->rtm_dst_len == 64) ? 0 : htonl(0xffffffff<<(32-(rm->rtm_dst_len%32))); netmask6.a1 = netmask6.a2 = 0xffffffff; } else if (rm->rtm_dst_len >= 32) { netmask6.a2 = (rm->rtm_dst_len == 32) ? 0 : htonl(0xffffffff<<(32-(rm->rtm_dst_len%32))); netmask6.a1 = 0xffffffff; } else netmask6.a1 = (rm->rtm_dst_len == 0) ? 0 : htonl(0xffffffff<<(32-rm->rtm_dst_len)); } else { netmask = (rm->rtm_dst_len == 0) ? 0 : htonl(0xffffffff<<(32-rm->rtm_dst_len)); } if (is_ipv6) { newsize = sizeof(struct ipv6_routing_table); newsize += ((*table_v6)->entries + 1) * sizeof(struct ipv6_route_entry); tmp6 = realloc(*table_v6, newsize); if(tmp6 == NULL) { return ENOMEM; } re6 = &(tmp6->routes[tmp6->entries]); memcpy(&re6->dest6, &dest6, sizeof(__u128)); memcpy(&re6->netmask6, &netmask6, sizeof(__u128)); memcpy(&re6->nexthop6, &nexthop6, sizeof(__u128)); strncpy(re6->interface, int_name, IFNAMSIZ); re6->metric = metric; //dprintf("re6->dest6 = %08x %08x %08x %08x, re6->netmask6 = %08x %08x %08x %08x, re6->nexthop6 = %08x %08x %08x %08x, interface = %s, metric = %d", // re6->dest6.a1,re6->dest6.a2,re6->dest6.a3,re6->dest6.a4, // re6->netmask6.a1,re6->netmask6.a2,re6->netmask6.a3,re6->netmask6.a4, // re6->nexthop6.a1,re6->nexthop6.a2,re6->nexthop6.a3,re6->nexthop6.a4, // re6->interface,re6->metric); tmp6->entries++; *table_v6 = tmp6; } else { newsize = sizeof(struct ipv4_routing_table); newsize += ((*table_v4)->entries + 1) * sizeof(struct ipv4_route_entry); tmp = realloc(*table_v4, newsize); if(tmp == NULL) { return ENOMEM; } re = &(tmp->routes[tmp->entries]); re->dest = dest; re->netmask = netmask; re->nexthop = nexthop; strncpy(re->interface, int_name, IFNAMSIZ); re->metric = metric; //dprintf("re->dest = %08x, re->netmask = %08x, re->nexthop = %08x, interface = %s, metric = %d", re->dest, re->netmask, re->nexthop,re->interface,re->metric); tmp->entries++; *table_v4 = tmp; } return 0; } int netlink_get_routing_table(struct ipv4_routing_table **table_ipv4, struct ipv6_routing_table **table_ipv6) { int fd; int seq; int status; struct routing_table table; *table_ipv4 = NULL; *table_ipv6 = NULL; table.table_ipv4 = table_ipv4; table.table_ipv6 = table_ipv6; *table_ipv4 = calloc(sizeof(struct ipv4_routing_table), 1); *table_ipv6 = calloc(sizeof(struct ipv6_routing_table), 1); if(*table_ipv4 == NULL) { return ENOMEM; } if(*table_ipv6 == NULL) { free(*table_ipv4); return ENOMEM; } fd = netlink_socket(NETLINK_ROUTE); if(fd == -1) { dprintf("failed with netlink"); return ERROR_NOT_SUPPORTED; } seq = netlink_request(fd, AF_UNSPEC, RTM_GETROUTE); if(seq == -1) { dprintf("netlink_request RTM_GETROUTE failed"); close(fd); return ERROR_NOT_SUPPORTED; } status = netlink_parse(fd, seq, netlink_parse_routing_table, &table); close(fd); if(status != 0) { if (*table_ipv4) free(*table_ipv4); if (*table_ipv6) free(*table_ipv6); *table_ipv4 = NULL; *table_ipv6 = NULL; } return status; } void flags_to_string(uint32_t flags, unsigned char * buffer, uint32_t buffer_len) { if ((flags & IFF_UP) == IFF_UP) strncat(buffer, "UP ",buffer_len - strlen(buffer)); if ((flags & IFF_BROADCAST) == IFF_BROADCAST) strncat(buffer, "BROADCAST ",buffer_len - strlen(buffer)); if ((flags & IFF_LOOPBACK) == IFF_LOOPBACK) strncat(buffer, "LOOPBACK ",buffer_len - strlen(buffer)); if ((flags & IFF_POINTOPOINT) == IFF_POINTOPOINT) strncat(buffer, "POINTOPOINT ",buffer_len - strlen(buffer)); if ((flags & IFF_RUNNING) == IFF_RUNNING) strncat(buffer, "RUNNING ",buffer_len - strlen(buffer)); if ((flags & IFF_PROMISC) == IFF_PROMISC) strncat(buffer, "PROMISC ",buffer_len - strlen(buffer)); if ((flags & IFF_MULTICAST) == IFF_MULTICAST) strncat(buffer, "MULTICAST ",buffer_len - strlen(buffer)); } // returns 1 if iface seems to be an ifinfomsg int likely_ifinfomsg(struct ifinfomsg *iface) { if (iface->ifi_family == 0 && //ifi_family == AF_UNSPEC iface->ifi_type > 0 && iface->ifi_index > 0 && // iface index should be between 1 and 4096 iface->ifi_index <= 0x1000 && ((iface->ifi_change == 0) || (iface->ifi_change == 0xffffffff)) ) return 1; else return 0; } int netlink_parse_interface_link(struct nlmsghdr *nh, void *data) { struct ifaces_list ** iface_list = ( struct ifaces_list **) data; struct ifaces_list *tmp; struct iface_entry iface_tmp; struct iface_entry * iff; struct ifinfomsg *iface; struct rtattr *attribute; uint32_t len; uint32_t newsize; // stumbled upon an old system with 4 bytes padding between nlmsghdr and ifinfomsg, try to detect it iface = NLMSG_DATA(nh); if (!likely_ifinfomsg(iface)) { iface = (struct ifinfomsg *)((unsigned char *)iface + 4); dprintf("Adjusted iface at +4"); } //dprintf("ifi_family : 0x%x , ifi_type : 0x%x, ifi_index : 0x%x",iface->ifi_family, iface->ifi_type, iface->ifi_index); len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*iface)); memset(&iface_tmp, 0, sizeof(iface_tmp)); // index of the interface iface_tmp.index = iface->ifi_index; //flags of the interface, string version flags_to_string(iface->ifi_flags, iface_tmp.flags, FLAGS_LEN); for (attribute = IFLA_RTA(iface); RTA_OK(attribute, len); attribute = RTA_NEXT(attribute, len)) { switch(attribute->rta_type) { case IFLA_IFNAME: strncpy(iface_tmp.name, (unsigned char *) RTA_DATA(attribute), IFNAMSIZ); break; case IFLA_ADDRESS: memcpy(iface_tmp.hwaddr, (unsigned char *) RTA_DATA(attribute), 6); break; case IFLA_MTU: iface_tmp.mtu = *(uint32_t *)RTA_DATA(attribute); break; default: break; } } newsize = sizeof(struct ifaces_list); newsize += ((*iface_list)->entries + 1) * sizeof(struct iface_entry); tmp = realloc(*iface_list, newsize); if(tmp == NULL) { return ENOMEM; } iff = &(tmp->ifaces[tmp->entries]); memset(iff, 0, sizeof(struct iface_entry)); iff->index = iface_tmp.index; strncpy(iff->name, iface_tmp.name, IFNAMSIZ); memcpy(iff->hwaddr, iface_tmp.hwaddr, 6); strncpy(iff->flags, iface_tmp.flags, FLAGS_LEN); iff->mtu = iface_tmp.mtu; dprintf("iff->index = %d, iff->name = %s, iff->hwaddr = %02x:%02x:%02x:%02x:%02x:%02x, iff->mtu = %d, iff->flags = %s, real_flags = 0x%08x", iff->index, iff->name, *(unsigned char *)(iff->hwaddr), *(unsigned char *)(iff->hwaddr+1),*(unsigned char *)(iff->hwaddr+2), *(unsigned char *)(iff->hwaddr+3), *(unsigned char *)(iff->hwaddr+4), *(unsigned char *)(iff->hwaddr+5), iff->mtu, iff->flags, iface->ifi_flags); tmp->entries++; *iface_list = tmp; return 0; } struct iface_entry * find_iface_by_index(struct ifaces_list * list, uint32_t index) { struct iface_entry * ret = NULL; uint32_t i; for(i=0; i<list->entries; i++) { if (list->ifaces[i].index == index) { ret = &list->ifaces[i]; break; } } return ret; } struct iface_entry * find_iface_by_index_and_name(struct ifaces_list * list, uint32_t index,unsigned char * name) { struct iface_entry * ret = NULL; uint32_t i; for(i=0; i<list->entries; i++) { if (list->ifaces[i].index == index && !strcmp(list->ifaces[i].name, name)) { ret = &list->ifaces[i]; break; } } return ret; } // returns 1 if iaddr seems to be an ifaddrmsg int likely_ifaddrmsg(struct ifaddrmsg *iaddr) { if ( (iaddr->ifa_family == AF_INET && iaddr->ifa_prefixlen >= 0 && iaddr->ifa_prefixlen <= 32) || //ifa_family == AF_INET 0 <= and prefix_len <= 32 (iaddr->ifa_family == AF_INET6 && iaddr->ifa_prefixlen >= 0 && iaddr->ifa_prefixlen <= 128) //ifa_family == AF_INET6 and 0 <= prefix_len <= 128 ) return 1; else return 0; } int netlink_parse_interface_address(struct nlmsghdr *nh, void *data) { struct ifaces_list ** iface_list = ( struct ifaces_list **) data; struct iface_entry * iff; struct ifaces_list * iface_list_tmp; struct iface_entry iface_tmp; struct ifaddrmsg *iaddr; struct rtattr *attribute; uint32_t len; uint32_t newsize; unsigned char is_ipv6; struct iface_address *addr_tmp; // strictly for debugging char addr_str[64]; iaddr = NLMSG_DATA(nh); // stumbled upon an old system with 4 bytes padding between nlmsghdr and ifaddrmsg, try to detect it if (!likely_ifaddrmsg(iaddr)) { iaddr = (struct ifaddrmsg *)((unsigned char *)iaddr + 4); dprintf("Adjusted iaddr at +4"); } len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*iaddr)); if (iaddr->ifa_family == AF_INET6) is_ipv6 = 1; else if (iaddr->ifa_family == AF_INET) is_ipv6 = 0; else { //dprintf("Got iaddr->ifa_family : %d which is unknown (iaddr->ifa_index : %d)", iaddr->ifa_family, iaddr->ifa_index); return 0; } memset(&iface_tmp, 0, sizeof(iface_tmp)); iface_tmp.index = iaddr->ifa_index; for (attribute = IFA_RTA(iaddr); RTA_OK(attribute, len); attribute = RTA_NEXT(attribute, len)) { switch(attribute->rta_type) { case IFA_ADDRESS: // Make room for a new address iface_tmp.addr_count++; iface_tmp.addr_list = realloc(iface_tmp.addr_list, sizeof(struct iface_address) * iface_tmp.addr_count); addr_tmp = &iface_tmp.addr_list[iface_tmp.addr_count-1]; if (is_ipv6) { addr_tmp->family = AF_INET6; memcpy(&addr_tmp->ip.addr6, (unsigned char *) RTA_DATA(attribute), sizeof(__u128)); } else { addr_tmp->family = AF_INET; addr_tmp->ip.addr = *(__u32 *) RTA_DATA(attribute); } address_calculate_netmask(addr_tmp, iaddr->ifa_prefixlen); inet_ntop(addr_tmp->family, &addr_tmp->ip, addr_str, sizeof(addr_str)); dprintf("Interface: %s", addr_str); inet_ntop(addr_tmp->family, &addr_tmp->nm, addr_str, sizeof(addr_str)); dprintf("Netmask: %s", addr_str); break; case IFA_LABEL: strncpy(iface_tmp.name, (unsigned char *) RTA_DATA(attribute), IFNAMSIZ); dprintf("Copied name %s", iface_tmp.name); break; default: break; } } /* * try to find the iface by index and name * An IP alias (eth0:0 for instance) will have the same index but not the * same name/label. There are no aliases when getting IPv6 address, so * just search using the index. */ if (is_ipv6) { iff = find_iface_by_index(*iface_list, iface_tmp.index); if (iff == NULL) { dprintf("Cannot find iface with index %d", iface_tmp.index); return 0; } } else iff = find_iface_by_index_and_name(*iface_list, iface_tmp.index, iface_tmp.name); if (iff == NULL) { /* Now we're dealing with an IPv4 alias such as eth0:0. With a regular * interface, the mac address, mtu, flags, etc. would already have been * initialized when we did the RTM_GETLINK request. Since an alias * doesn't count as a physical interface, that didn't happen, so copy * all of the parent interface's info to this one. */ dprintf("%s an alias?", iface_tmp.name); iff = find_iface_by_index(*iface_list, iface_tmp.index); if (iff == NULL) { dprintf("Cannot find iface with index %d", iface_tmp.index); return 0; } memcpy(iface_tmp.hwaddr, iff->hwaddr, 6); iface_tmp.mtu = iff->mtu; strncpy(iface_tmp.flags, iff->flags, FLAGS_LEN); // expand the list to accomodate the new one newsize = sizeof(struct ifaces_list); newsize += ((*iface_list)->entries + 1) * sizeof(struct iface_entry); iface_list_tmp = realloc(*iface_list, newsize); if(iface_list_tmp == NULL) { return ENOMEM; } iff = &(iface_list_tmp->ifaces[iface_list_tmp->entries]); memset(iff, 0, sizeof(struct iface_entry)); // copy back saved data in new iface_entry memcpy(iff->hwaddr, iface_tmp.hwaddr, 6); iff->mtu = iface_tmp.mtu; iff->index = iface_tmp.index; strncpy(iff->flags, iface_tmp.flags, FLAGS_LEN); strncpy(iff->name, iface_tmp.name, IFNAMSIZ); iface_list_tmp->entries++; *iface_list = iface_list_tmp; } inet_ntop(addr_tmp->family, &addr_tmp->ip, addr_str, sizeof(addr_str)); dprintf("Appending: %s", addr_str); iface_entry_append_address(iff, &iface_tmp.addr_list[0]); dprintf("iff->addr_count = %d; iface_tmp.addr_count = %d", iff->addr_count, iface_tmp.addr_count); return 0; } int netlink_get_interfaces(struct ifaces_list **iface_list) { int fd; int seq; int status; *iface_list = NULL; *iface_list = calloc(sizeof(struct ifaces_list), 1); if(*iface_list == NULL) { return ENOMEM; } fd = netlink_socket(NETLINK_ROUTE); if(fd == -1) { dprintf("failed with netlink"); return ERROR_NOT_SUPPORTED; } seq = netlink_request(fd, AF_UNSPEC, RTM_GETLINK); if(seq == -1) { dprintf("netlink_request RTM_GETLINK failed"); close(fd); return ERROR_NOT_SUPPORTED; } // will create one iface_entry for each interface status = netlink_parse(fd, seq, netlink_parse_interface_link, iface_list); if(status != 0) { if (*iface_list) free(*iface_list); *iface_list = NULL; return status; } seq = netlink_request(fd, AF_UNSPEC, RTM_GETADDR); if(seq == -1) { dprintf("netlink_request RTM_GETADDR failed"); close(fd); return ERROR_NOT_SUPPORTED; } // for each interface created before, will get the IPv4 / IPv6 addr status = netlink_parse(fd, seq, netlink_parse_interface_address, iface_list); close(fd); if(status != 0) { if (*iface_list) free(*iface_list); *iface_list = NULL; } return status; } void address_calculate_netmask(struct iface_address *address, int ifa_prefixlen) { if (address->family == AF_INET6) { // if netmask is FFFFFFFF FFFFFFFF 00000000 00000000 (/64), netmask6.a1 and netmask6.a2 == 0xffffffff, and nestmask6.a3 and .a4 == 0 // netmask6 is no longer set to 0 at the beginning of the function, need to reset the values to 0 // XXX really ugly, but works memset(&address->nm.netmask6, 0, sizeof(__u128)); if (ifa_prefixlen >= 96) { address->nm.netmask6.a4 = (ifa_prefixlen == 96) ? 0 : htonl(0xffffffff<<(32-(ifa_prefixlen%32))); address->nm.netmask6.a1 = address->nm.netmask6.a2 = address->nm.netmask6.a3 = 0xffffffff; } else if (ifa_prefixlen >= 64) { address->nm.netmask6.a3 = (ifa_prefixlen == 64) ? 0 : htonl(0xffffffff<<(32-(ifa_prefixlen%32))); address->nm.netmask6.a1 = address->nm.netmask6.a2 = 0xffffffff; } else if (ifa_prefixlen >= 32) { address->nm.netmask6.a2 = (ifa_prefixlen == 32) ? 0 : htonl(0xffffffff<<(32-(ifa_prefixlen%32))); address->nm.netmask6.a1 = 0xffffffff; } else address->nm.netmask6.a1 = (ifa_prefixlen == 0) ? 0 : htonl(0xffffffff<<(32-ifa_prefixlen)); } else { address->nm.netmask = (ifa_prefixlen == 0) ? 0 : htonl(0xffffffff<<(32-ifa_prefixlen)); } } void iface_entry_append_address(struct iface_entry *iface, struct iface_address *address) { iface->addr_count++; iface->addr_list = realloc(iface->addr_list, sizeof(struct iface_address) * iface->addr_count); dprintf("Realloc'd %p", iface->addr_list); memcpy(&iface->addr_list[iface->addr_count-1], address, sizeof(struct iface_address)); }