예제 #1
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 = (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;
}
예제 #2
0
파일: process.c 프로젝트: reubenhwk/radvd
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);
    }
}