#include #include #if !defined(WIN32) && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) && !defined(NN_COMPILER_RVCT) #include #endif #include #include #include #include #if !defined(NN_COMPILER_RVCT) && !defined(SN_TARGET_PSP2) #include #endif #if !defined(WIN32) && !defined(NN_COMPILER_RVCT) #include #include #endif #if LINUX #include #include #include #endif /* def LINUX */ #if !defined(WIN32) && !defined(NN_COMPILER_RVCT) && !defined(SN_TARGET_PSP2) #include #endif #if ANDROID #include #endif #include "logger.h" #include "xamarin_getifaddrs.h" /* These aren't defined in android's rtnetlink.h (as of ndk 9d). We define fake values for them if * they aren't found so that the debug code works properly. We could skip them but future versions * of the NDK might include definitions for them. */ #ifndef IFLA_LINKINFO #define IFLA_LINKINFO 1000 #endif #ifndef IFLA_NET_NS_PID #define IFLA_NET_NS_PID 1001 #endif #ifndef IFLA_IFALIAS #define IFLA_IFALIAS 1002 #endif #ifndef IFLA_NUM_VF #define IFLA_NUM_VF 1003 #endif #ifndef IFLA_VFINFO_LIST #define IFLA_VFINFO_LIST 1004 #endif #ifndef IFLA_STATS64 #define IFLA_STATS64 1005 #endif #ifndef IFLA_VF_PORTS #define IFLA_VF_PORTS 1006 #endif #ifndef IFLA_PORT_SELF #define IFLA_PORT_SELF 1007 #endif #ifndef IFLA_AF_SPEC #define IFLA_AF_SPEC 1008 #endif #ifndef IFLA_GROUP #define IFLA_GROUP 1009 #endif #ifndef IFLA_NET_NS_FD #define IFLA_NET_NS_FD 1010 #endif #ifndef IFLA_EXT_MASK #define IFLA_EXT_MASK 1011 #endif #ifndef IFLA_PROMISCUITY #define IFLA_PROMISCUITY 1012 #endif #ifndef IFLA_NUM_TX_QUEUES #define IFLA_NUM_TX_QUEUES 1013 #endif #ifndef IFLA_NUM_RX_QUEUES #define IFLA_NUM_RX_QUEUES 1014 #endif #ifndef IFLA_CARRIER #define IFLA_CARRIER 1015 #endif #ifndef IFLA_PHYS_PORT_ID #define IFLA_PHYS_PORT_ID 1016 #endif /* The amount of data we read from the kernel in one call */ #define RESPONSE_BUFFER_SIZE 1024 /* Maximum interface address label size, should be more than enough */ #define MAX_IFA_LABEL_SIZE 1024 #if LINUX /* This is the message we send to the kernel */ typedef struct { struct nlmsghdr header; struct rtgenmsg message; } netlink_request; typedef struct { int sock_fd; int seq; struct sockaddr_nl them; /* kernel end */ struct sockaddr_nl us; /* our end */ struct msghdr message_header; /* for use with sendmsg */ struct iovec payload_vector; /* Used to send netlink_request */ } netlink_session; /* Turns out that quite a few link types have address length bigger than the 8 bytes allocated in * this structure as defined by the OS. Examples are Infiniband or ipv6 tunnel devices */ struct sockaddr_ll_extended { unsigned short int sll_family; unsigned short int sll_protocol; int sll_ifindex; unsigned short int sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[24]; }; static int parse_netlink_reply(netlink_session *session, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr); static struct _monodroid_ifaddrs *get_link_info(const struct nlmsghdr *message); static struct _monodroid_ifaddrs *get_link_address(const struct nlmsghdr *message, struct _monodroid_ifaddrs **ifaddrs_head); static int open_netlink_session(netlink_session *session); static int send_netlink_dump_request(netlink_session *session, int type); static int append_ifaddr(struct _monodroid_ifaddrs *addr, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr); static int fill_ll_address(struct sockaddr_ll_extended **sa, struct ifinfomsg *net_interface, void *rta_data, int rta_payload_length); static int fill_sa_address(struct sockaddr **sa, struct ifaddrmsg *net_address, void *rta_data, int rta_payload_length); static void free_single_xamarin_ifaddrs(struct _monodroid_ifaddrs **ifap); static void get_ifaddrs_impl(int(**getifaddrs_impl) (struct _monodroid_ifaddrs **ifap), void(**freeifaddrs_impl) (struct _monodroid_ifaddrs *ifa)); static struct _monodroid_ifaddrs *find_interface_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head); static char *get_interface_name_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head); static int get_interface_flags_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head); static int calculate_address_netmask(struct _monodroid_ifaddrs *ifa, struct ifaddrmsg *net_address); #if DEBUG static void print_ifla_name(int id); static void print_address_list(char *title, struct _monodroid_ifaddrs *list); #endif /* We don't use 'struct ifaddrs' since that doesn't exist in Android's bionic, but since our * version of the structure is 100% compatible we can just use it instead */ typedef int (*getifaddrs_impl_fptr)(struct _monodroid_ifaddrs **); typedef void (*freeifaddrs_impl_fptr)(struct _monodroid_ifaddrs *ifa); static getifaddrs_impl_fptr getifaddrs_impl = NULL; static freeifaddrs_impl_fptr freeifaddrs_impl = NULL; void _monodroid_getifaddrs_init() { get_ifaddrs_impl(&getifaddrs_impl, &freeifaddrs_impl); } int _monodroid_getifaddrs(struct _monodroid_ifaddrs **ifap) { netlink_session session; struct _monodroid_ifaddrs *ifaddrs_head; struct _monodroid_ifaddrs *last_ifaddr; int ret = -1; if (getifaddrs_impl) return (*getifaddrs_impl)(ifap); if (!ifap) { goto cleanup; } *ifap = NULL; ifaddrs_head = 0; last_ifaddr = 0; if (open_netlink_session(&session) < 0) { goto cleanup; } /* Request information about the specified link. In our case it will be all of them since we request the root of the link tree below */ if ((send_netlink_dump_request(&session, RTM_GETLINK) < 0) || (parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0) || (send_netlink_dump_request(&session, RTM_GETADDR) < 0) || (parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0)) { _monodroid_freeifaddrs(ifaddrs_head); goto cleanup; } ret = 0; *ifap = ifaddrs_head; #if DEBUG print_address_list("Initial interfaces list", *ifap); #endif cleanup: if (session.sock_fd >= 0) { close(session.sock_fd); session.sock_fd = -1; } return ret; } void _monodroid_freeifaddrs(struct _monodroid_ifaddrs *ifa) { struct _monodroid_ifaddrs *cur, *next; if (!ifa) return; if (freeifaddrs_impl) { (*freeifaddrs_impl)(ifa); return; } #if DEBUG print_address_list("List passed to freeifaddrs", ifa); #endif cur = ifa; while (cur) { next = cur->ifa_next; free_single_xamarin_ifaddrs(&cur); cur = next; } } static void get_ifaddrs_impl(int(**getifaddrs_impl) (struct _monodroid_ifaddrs **ifap), void(**freeifaddrs_impl) (struct _monodroid_ifaddrs *ifa)) { void *libc; assert(getifaddrs_impl); assert(freeifaddrs_impl); libc = dlopen("libc.so", RTLD_NOW); if (libc) { *getifaddrs_impl = dlsym(libc, "getifaddrs"); if (*getifaddrs_impl) *freeifaddrs_impl = dlsym(libc, "freeifaddrs"); } if (!*getifaddrs_impl) log_info(LOG_NET, "This libc does not have getifaddrs/freeifaddrs, using Xamarin's\n"); else log_info(LOG_NET, "This libc has getifaddrs/freeifaddrs\n"); } static void free_single_xamarin_ifaddrs(struct _monodroid_ifaddrs **ifap) { struct _monodroid_ifaddrs *ifa = ifap ? *ifap : NULL; if (!ifa) return; if (ifa->ifa_name) free(ifa->ifa_name); if (ifa->ifa_addr) free(ifa->ifa_addr); if (ifa->ifa_netmask) free(ifa->ifa_netmask); if (ifa->_monodroid_ifa_broadaddr) free(ifa->_monodroid_ifa_broadaddr); if (ifa->ifa_data) free(ifa->ifa_data); free(ifa); *ifap = NULL; } static int open_netlink_session(netlink_session *session) { assert(session != 0); memset(session, 0, sizeof(*session)); session->sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (session->sock_fd == -1) { log_warn(LOG_NETLINK, "Failed to create a netlink socket. %s\n", strerror(errno)); return -1; } /* Fill out addresses */ session->us.nl_family = AF_NETLINK; session->us.nl_pid = getpid(); session->us.nl_groups = 0; session->them.nl_family = AF_NETLINK; if (bind(session->sock_fd, (struct sockaddr *)&session->us, sizeof(session->us)) < 0) { log_warn(LOG_NETLINK, "Failed to bind to the netlink socket. %s\n", strerror(errno)); return -1; } return 0; } static int send_netlink_dump_request(netlink_session *session, int type) { netlink_request request; memset(&request, 0, sizeof(request)); request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); /* Flags (from netlink.h): NLM_F_REQUEST - it's a request message NLM_F_DUMP - gives us the root of the link tree and returns all links matching our requested AF, which in our case means all of them (AF_PACKET) */ request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; request.header.nlmsg_seq = ++session->seq; request.header.nlmsg_pid = session->us.nl_pid; request.header.nlmsg_type = type; /* AF_PACKET means we want to see everything */ request.message.rtgen_family = AF_PACKET; memset(&session->payload_vector, 0, sizeof(session->payload_vector)); session->payload_vector.iov_len = request.header.nlmsg_len; session->payload_vector.iov_base = &request; memset(&session->message_header, 0, sizeof(session->message_header)); session->message_header.msg_namelen = sizeof(session->them); session->message_header.msg_name = &session->them; session->message_header.msg_iovlen = 1; session->message_header.msg_iov = &session->payload_vector; if (sendmsg(session->sock_fd, (const struct msghdr*)&session->message_header, 0) < 0) { log_warn(LOG_NETLINK, "Failed to send netlink message. %s\n", strerror(errno)); return -1; } return 0; } static int append_ifaddr(struct _monodroid_ifaddrs *addr, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr) { assert(addr); assert(ifaddrs_head); assert(last_ifaddr); if (!*ifaddrs_head) { *ifaddrs_head = *last_ifaddr = addr; if (!*ifaddrs_head) return -1; } else if (!*last_ifaddr) { struct _monodroid_ifaddrs *last = *ifaddrs_head; while (last->ifa_next) last = last->ifa_next; *last_ifaddr = last; } addr->ifa_next = NULL; if (addr == *last_ifaddr) return 0; assert(addr != *last_ifaddr); (*last_ifaddr)->ifa_next = addr; *last_ifaddr = addr; assert((*last_ifaddr)->ifa_next == NULL); return 0; } static int parse_netlink_reply(netlink_session *session, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr) { ssize_t length; struct msghdr netlink_reply; struct iovec reply_vector; struct nlmsghdr *current_message; struct _monodroid_ifaddrs *addr; unsigned char response[RESPONSE_BUFFER_SIZE]; assert(session); assert(ifaddrs_head); assert(last_ifaddr); while (1) { memset(response, 0, RESPONSE_BUFFER_SIZE); memset(&reply_vector, 0, sizeof(reply_vector)); reply_vector.iov_len = RESPONSE_BUFFER_SIZE; reply_vector.iov_base = response; memset(&netlink_reply, 0, sizeof(netlink_reply)); netlink_reply.msg_namelen = sizeof(&session->them); netlink_reply.msg_name = &session->them; netlink_reply.msg_iovlen = 1; netlink_reply.msg_iov = &reply_vector; log_debug(LOG_NETLINK, "receiving message...\n"); length = recvmsg(session->sock_fd, &netlink_reply, 0); log_debug(LOG_NETLINK, " length == %d\n", (int)length); if (length < 0) { log_debug(LOG_NETLINK, "Failed to receive reply from netlink. %s\n", strerror(errno)); return -1; } if (length == 0) return 0; /* done, apparently */ for (current_message = (struct nlmsghdr*)response; current_message && NLMSG_OK(current_message, length); current_message = NLMSG_NEXT(current_message, length)) { log_debug(LOG_NETLINK, "next message... (type: %u)\n", current_message->nlmsg_type); switch (current_message->nlmsg_type) { /* See rtnetlink.h */ case RTM_NEWLINK: log_debug(LOG_NETLINK, " dumping link...\n"); addr = get_link_info(current_message); if (!addr || append_ifaddr(addr, ifaddrs_head, last_ifaddr) < 0) return -1; log_debug(LOG_NETLINK, " done\n"); break; case RTM_NEWADDR: log_debug(LOG_NETLINK, " got an address\n"); addr = get_link_address(current_message, ifaddrs_head); if (!addr || append_ifaddr(addr, ifaddrs_head, last_ifaddr) < 0) return -1; break; case NLMSG_DONE: log_debug(LOG_NETLINK, " message done\n"); return 0; } } } } static int fill_sa_address(struct sockaddr **sa, struct ifaddrmsg *net_address, void *rta_data, int rta_payload_length) { assert(sa); assert(net_address); assert(rta_data); switch (net_address->ifa_family) { case AF_INET: { struct sockaddr_in *sa4; assert(rta_payload_length == 4); /* IPv4 address length */ sa4 = (struct sockaddr_in*)calloc(1, sizeof(*sa4)); if (!sa4) return -1; sa4->sin_family = AF_INET; memcpy(&sa4->sin_addr, rta_data, rta_payload_length); *sa = (struct sockaddr*)sa4; break; } case AF_INET6: { struct sockaddr_in6 *sa6; assert(rta_payload_length == 16); /* IPv6 address length */ sa6 = (struct sockaddr_in6*)calloc(1, sizeof(*sa6)); if (!sa6) return -1; sa6->sin6_family = AF_INET6; memcpy(&sa6->sin6_addr, rta_data, rta_payload_length); if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) sa6->sin6_scope_id = net_address->ifa_index; *sa = (struct sockaddr*)sa6; break; } default: { struct sockaddr *sagen; assert(rta_payload_length <= sizeof(sagen->sa_data)); *sa = sagen = (struct sockaddr*)calloc(1, sizeof(*sagen)); if (sagen) return -1; sagen->sa_family = net_address->ifa_family; memcpy(&sagen->sa_data, rta_data, rta_payload_length); break; } } return 0; } static int fill_ll_address(struct sockaddr_ll_extended **sa, struct ifinfomsg *net_interface, void *rta_data, int rta_payload_length) { assert(sa); assert(net_interface); /* Always allocate, do not free - caller may reuse the same variable */ *sa = calloc(1, sizeof(**sa)); if (!*sa) return -1; (*sa)->sll_family = AF_PACKET; /* Always for physical links */ /* The assert can only fail for Iniband links, which are quite unlikely to be found * in any mobile devices */ log_debug(LOG_NETLINK, "rta_payload_length == %d; sizeof sll_addr == %d; hw type == 0x%X\n", rta_payload_length, sizeof((*sa)->sll_addr), net_interface->ifi_type); if (rta_payload_length > sizeof((*sa)->sll_addr)) { log_info(LOG_NETLINK, "Address is too long to place in sockaddr_ll (%d > %d)", rta_payload_length, sizeof((*sa)->sll_addr)); free(*sa); *sa = NULL; return -1; } (*sa)->sll_ifindex = net_interface->ifi_index; (*sa)->sll_hatype = net_interface->ifi_type; (*sa)->sll_halen = rta_payload_length; memcpy((*sa)->sll_addr, rta_data, rta_payload_length); return 0; } static struct _monodroid_ifaddrs * find_interface_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head) { struct _monodroid_ifaddrs *cur; if (!ifaddrs_head || !*ifaddrs_head) return NULL; /* Normally expensive, but with the small amount of links in the chain we'll deal with it's not * worth the extra houskeeping and memory overhead */ cur = *ifaddrs_head; while (cur) { if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_PACKET && ((struct sockaddr_ll_extended*)cur->ifa_addr)->sll_ifindex == index) return cur; if (cur == cur->ifa_next) break; cur = cur->ifa_next; } return NULL; } static char * get_interface_name_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head) { struct _monodroid_ifaddrs *iface = find_interface_by_index(index, ifaddrs_head); if (!iface || !iface->ifa_name) return NULL; return iface->ifa_name; } static int get_interface_flags_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head) { struct _monodroid_ifaddrs *iface = find_interface_by_index(index, ifaddrs_head); if (!iface) return 0; return iface->ifa_flags; } static int calculate_address_netmask(struct _monodroid_ifaddrs *ifa, struct ifaddrmsg *net_address) { if (ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_UNSPEC && ifa->ifa_addr->sa_family != AF_PACKET) { uint32_t prefix_length = 0; uint32_t data_length = 0; unsigned char *netmask_data = NULL; switch (ifa->ifa_addr->sa_family) { case AF_INET: { struct sockaddr_in *sa = (struct sockaddr_in*)calloc(1, sizeof(struct sockaddr_in)); if (!sa) return -1; ifa->ifa_netmask = (struct sockaddr*)sa; prefix_length = net_address->ifa_prefixlen; if (prefix_length > 32) prefix_length = 32; data_length = sizeof(sa->sin_addr); netmask_data = (unsigned char*)&sa->sin_addr; break; } case AF_INET6: { struct sockaddr_in6 *sa = (struct sockaddr_in6*)calloc(1, sizeof(struct sockaddr_in6)); if (!sa) return -1; ifa->ifa_netmask = (struct sockaddr*)sa; prefix_length = net_address->ifa_prefixlen; if (prefix_length > 128) prefix_length = 128; data_length = sizeof(sa->sin6_addr); netmask_data = (unsigned char*)&sa->sin6_addr; break; } } if (ifa->ifa_netmask && netmask_data) { /* Fill the first X bytes with 255 */ uint32_t prefix_bytes = prefix_length / 8; uint32_t postfix_bytes; int i; if (prefix_bytes > data_length) { errno = EINVAL; return -1; } postfix_bytes = data_length - prefix_bytes; memset(netmask_data, 0xFF, prefix_bytes); if (postfix_bytes > 0) memset(netmask_data + prefix_bytes + 1, 0x00, postfix_bytes); log_debug(LOG_NETLINK, " calculating netmask, prefix length is %u bits (%u bytes), data length is %u bytes\n", prefix_length, prefix_bytes, data_length); if (prefix_bytes + 2 < data_length) /* Set the rest of the mask bits in the byte following the last 0xFF value */ netmask_data[prefix_bytes + 1] = 0xff << (8 - (prefix_length % 8)); log_debug(LOG_NETLINK, " netmask is: "); for (i = 0; i < data_length; i++) { log_debug(LOG_NETLINK, "%s%u", i == 0 ? "" : ".", (unsigned char)ifa->ifa_netmask->sa_data[i]); } log_debug(LOG_NETLINK, "\n"); } } return 0; } static struct _monodroid_ifaddrs * get_link_address(const struct nlmsghdr *message, struct _monodroid_ifaddrs **ifaddrs_head) { size_t length; struct rtattr *attribute; struct ifaddrmsg *net_address; struct _monodroid_ifaddrs *ifa = NULL; struct sockaddr **sa; int payload_size; assert(message); net_address = NLMSG_DATA(message); length = IFA_PAYLOAD(message); if (length <= 0) { goto error; } ifa = calloc(1, sizeof(*ifa)); if (!ifa) { goto error; } ifa->ifa_flags = get_interface_flags_by_index(net_address->ifa_index, ifaddrs_head); attribute = IFA_RTA(net_address); while (RTA_OK(attribute, length)) { payload_size = RTA_PAYLOAD(attribute); sa = NULL; switch (attribute->rta_type) { case IFA_LABEL: { int room_for_trailing_null = 0; if (payload_size > MAX_IFA_LABEL_SIZE) { payload_size = MAX_IFA_LABEL_SIZE; room_for_trailing_null = 1; } if (payload_size > 0) { ifa->ifa_name = (char*)malloc(payload_size + room_for_trailing_null); if (!ifa->ifa_name) { goto error; } memcpy(ifa->ifa_name, RTA_DATA(attribute), payload_size); if (room_for_trailing_null) ifa->ifa_name[payload_size] = '\0'; } break; } case IFA_LOCAL: if (ifa->ifa_addr) { /* P2P protocol, set the dst/broadcast address union from the original address. * Since ifa_addr is set it means IFA_ADDRESST occured earlier and that address * is indeed the P2P destination one. */ ifa->_monodroid_ifa_dstaddr = ifa->ifa_addr; ifa->ifa_addr = 0; } sa = &ifa->ifa_addr; break; case IFA_BROADCAST: if (ifa->_monodroid_ifa_dstaddr) { /* IFA_LOCAL happened earlier, undo its effect here */ free(ifa->_monodroid_ifa_dstaddr); ifa->_monodroid_ifa_dstaddr = NULL; } sa = &ifa->_monodroid_ifa_broadaddr; break; case IFA_ADDRESS: if (ifa->ifa_addr) { /* Apparently IFA_LOCAL occured earlier and we have a P2P connection * here. IFA_LOCAL carries the destination address, move it there */ ifa->_monodroid_ifa_dstaddr = ifa->ifa_addr; ifa->ifa_addr = NULL; } sa = &ifa->ifa_addr; break; } if (sa) { if (fill_sa_address(sa, net_address, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { goto error; } } attribute = RTA_NEXT(attribute, length); } /* glibc stores the associated interface name in the address if IFA_LABEL never occured */ if (!ifa->ifa_name) { char *name = get_interface_name_by_index(net_address->ifa_index, ifaddrs_head); log_debug(LOG_NETLINK, " address has no name/label, getting one from interface\n"); ifa->ifa_name = name ? strdup(name) : NULL; } log_debug(LOG_NETLINK, " address label: %s\n", ifa->ifa_name); if (calculate_address_netmask(ifa, net_address) < 0) { goto error; } return ifa; error : { /* errno may be modified by free, or any other call inside the free_single_xamarin_ifaddrs * function. We don't care about errors in there since it is more important to know how we * failed to obtain the link address and not that we went OOM. Save and restore the value * after the resources are freed. */ int errno_save = errno; free_single_xamarin_ifaddrs(&ifa); errno = errno_save; return NULL; } } static struct _monodroid_ifaddrs * get_link_info(const struct nlmsghdr *message) { ssize_t length; struct rtattr *attribute; struct ifinfomsg *net_interface; struct _monodroid_ifaddrs *ifa = NULL; struct sockaddr_ll_extended *sa = NULL; assert(message); net_interface = NLMSG_DATA(message); length = message->nlmsg_len - NLMSG_LENGTH(sizeof(*net_interface)); if (length <= 0) { goto error; } ifa = calloc(1, sizeof(*ifa)); if (!ifa) { goto error; } ifa->ifa_flags = net_interface->ifi_flags; attribute = IFLA_RTA(net_interface); while (RTA_OK(attribute, length)) { switch (attribute->rta_type) { case IFLA_IFNAME: ifa->ifa_name = strdup(RTA_DATA(attribute)); if (!ifa->ifa_name) { goto error; } log_debug(LOG_NETLINK, " interface name (payload length: %d; string length: %d)\n", RTA_PAYLOAD(attribute), strlen(ifa->ifa_name)); log_debug(LOG_NETLINK, " %s\n", ifa->ifa_name); break; case IFLA_BROADCAST: log_debug(LOG_NETLINK, " interface broadcast (%d bytes)\n", RTA_PAYLOAD(attribute)); if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { goto error; } ifa->_monodroid_ifa_broadaddr = (struct sockaddr*)sa; break; case IFLA_ADDRESS: log_debug(LOG_NETLINK, " interface address (%d bytes)\n", RTA_PAYLOAD(attribute)); if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { goto error; } ifa->ifa_addr = (struct sockaddr*)sa; break; default: log_debug(LOG_NETLINK, " rta_type: "); #if DEBUG print_ifla_name(attribute->rta_type); #endif break; } attribute = RTA_NEXT(attribute, length); } log_debug(LOG_NETLINK, "link flags: 0x%X", ifa->ifa_flags); return ifa; error: if (sa) free(sa); free_single_xamarin_ifaddrs(&ifa); return NULL; } #else void _monodroid_getifaddrs_init(void) { } int _monodroid_getifaddrs(struct _monodroid_ifaddrs **ifap) { *ifap = NULL; return 0; } void _monodroid_freeifaddrs(struct _monodroid_ifaddrs *ifa) { } #endif #if LINUX && DEBUG #define ENUM_VALUE_ENTRY(enumvalue) { enumvalue, #enumvalue } struct enumvalue { int value; char *name; }; struct enumvalue iflas[] = { ENUM_VALUE_ENTRY(IFLA_UNSPEC), ENUM_VALUE_ENTRY(IFLA_ADDRESS), ENUM_VALUE_ENTRY(IFLA_BROADCAST), ENUM_VALUE_ENTRY(IFLA_IFNAME), ENUM_VALUE_ENTRY(IFLA_MTU), ENUM_VALUE_ENTRY(IFLA_LINK), ENUM_VALUE_ENTRY(IFLA_QDISC), ENUM_VALUE_ENTRY(IFLA_STATS), ENUM_VALUE_ENTRY(IFLA_COST), ENUM_VALUE_ENTRY(IFLA_PRIORITY), ENUM_VALUE_ENTRY(IFLA_MASTER), ENUM_VALUE_ENTRY(IFLA_WIRELESS), ENUM_VALUE_ENTRY(IFLA_PROTINFO), ENUM_VALUE_ENTRY(IFLA_TXQLEN), ENUM_VALUE_ENTRY(IFLA_MAP), ENUM_VALUE_ENTRY(IFLA_WEIGHT), ENUM_VALUE_ENTRY(IFLA_OPERSTATE), ENUM_VALUE_ENTRY(IFLA_LINKMODE), ENUM_VALUE_ENTRY(IFLA_LINKINFO), ENUM_VALUE_ENTRY(IFLA_NET_NS_PID), ENUM_VALUE_ENTRY(IFLA_IFALIAS), ENUM_VALUE_ENTRY(IFLA_NUM_VF), ENUM_VALUE_ENTRY(IFLA_VFINFO_LIST), ENUM_VALUE_ENTRY(IFLA_STATS64), ENUM_VALUE_ENTRY(IFLA_VF_PORTS), ENUM_VALUE_ENTRY(IFLA_PORT_SELF), ENUM_VALUE_ENTRY(IFLA_AF_SPEC), ENUM_VALUE_ENTRY(IFLA_GROUP), ENUM_VALUE_ENTRY(IFLA_NET_NS_FD), ENUM_VALUE_ENTRY(IFLA_EXT_MASK), ENUM_VALUE_ENTRY(IFLA_PROMISCUITY), ENUM_VALUE_ENTRY(IFLA_NUM_TX_QUEUES), ENUM_VALUE_ENTRY(IFLA_NUM_RX_QUEUES), ENUM_VALUE_ENTRY(IFLA_CARRIER), ENUM_VALUE_ENTRY(IFLA_PHYS_PORT_ID), { -1, 0 } }; static void print_ifla_name(int id) { int i = 0; while (1) { if (iflas[i].value == -1 && iflas[i].name == 0) { log_info(LOG_NETLINK, "Unknown ifla->name: unknown id %d\n", id); break; } if (iflas[i].value != id) { i++; continue; } log_info(LOG_NETLINK, "ifla->name: %s (%d)\n", iflas[i].name, iflas[i].value); break; } } static void print_address_list(char *title, struct _monodroid_ifaddrs *list) { struct _monodroid_ifaddrs *cur; char *msg, *tmp; if (!list) { log_info(LOG_NETLINK, "monodroid-net", "No list to print in %s", __FUNCTION__); return; } cur = list; msg = NULL; while (cur) { tmp = NULL; asprintf(&tmp, "%s%s%p (%s; %p)", msg ? msg : "", msg ? " -> " : "", cur, cur->ifa_name, cur->ifa_name); if (msg) free(msg); msg = tmp; cur = cur->ifa_next; } log_info(LOG_NETLINK, "%s: %s", title, msg ? msg : "[no addresses]"); free(msg); } #endif