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 = (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; }
void process(int sock, struct Interface *interfaces, unsigned char *msg, int len, struct sockaddr_in6 *addr, struct in6_pktinfo *pkt_info, int hoplimit) { char if_namebuf[IF_NAMESIZE] = { "" }; char *if_name = if_indextoname(pkt_info->ipi6_ifindex, if_namebuf); if (!if_name) { if_name = "unknown interface"; } dlog(LOG_DEBUG, 4, "%s received a packet", if_name); char addr_str[INET6_ADDRSTRLEN]; addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); if (!pkt_info) { flog(LOG_WARNING, "%s received packet with no pkt_info from %s!", if_name, addr_str); return; } /* * can this happen? */ if (len < sizeof(struct icmp6_hdr)) { flog(LOG_WARNING, "%s received icmpv6 packet with invalid length (%d) from %s", if_name, len, addr_str); return; } struct icmp6_hdr *icmph = (struct icmp6_hdr *)msg; if (icmph->icmp6_type != ND_ROUTER_SOLICIT && icmph->icmp6_type != ND_ROUTER_ADVERT) { /* * We just want to listen to RSs and RAs */ flog(LOG_ERR, "%s icmpv6 filter failed", if_name); return; } if (icmph->icmp6_type == ND_ROUTER_ADVERT) { if (len < sizeof(struct nd_router_advert)) { flog(LOG_WARNING, "%s received icmpv6 RA packet with invalid length (%d) from %s", if_name, len, addr_str); return; } if (!IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { flog(LOG_WARNING, "%s received icmpv6 RA packet with non-linklocal source address from %s", if_name, addr_str); return; } } if (icmph->icmp6_type == ND_ROUTER_SOLICIT) { if (len < sizeof(struct nd_router_solicit)) { flog(LOG_WARNING, "%s received icmpv6 RS packet with invalid length (%d) from %s", if_name, len, addr_str); return; } } if (icmph->icmp6_code != 0) { flog(LOG_WARNING, "%s received icmpv6 RS/RA packet with invalid code (%d) from %s", if_name, icmph->icmp6_code, addr_str); return; } /* get iface by received if_index */ struct Interface *iface = find_iface_by_index(interfaces, pkt_info->ipi6_ifindex); if (iface == NULL) { dlog(LOG_WARNING, 4, "%s received icmpv6 RS/RA packet on an unknown interface with index %d", if_name, pkt_info->ipi6_ifindex); return; } if (!iface->state_info.ready && (0 != setup_iface(sock, iface))) { flog(LOG_WARNING, "%s received RS or RA on %s but %s is not ready and setup_iface failed", if_name, iface->props.name, iface->props.name); return; } if (hoplimit != 255) { flog(LOG_WARNING, "%s received RS or RA with invalid hoplimit %d from %s", if_name, hoplimit, addr_str); return; } if (icmph->icmp6_type == ND_ROUTER_SOLICIT) { dlog(LOG_DEBUG, 3, "%s received RS from: %s", if_name, addr_str); process_rs(sock, iface, msg, len, addr); } else if (icmph->icmp6_type == ND_ROUTER_ADVERT) { if (0 == memcmp(&addr->sin6_addr, &iface->props.if_addr, sizeof(iface->props.if_addr))) { dlog(LOG_DEBUG, 3, "%s received RA from: %s (myself)", if_name, addr_str); } else { dlog(LOG_DEBUG, 3, "%s received RA from: %s", if_name, addr_str); } process_ra(iface, msg, len, addr); } }