Пример #1
0
/*
 * See if left->addr or left->next is %defaultroute and change it to IP.
 *
 * Returns:
 * -1: failure
 *  0: done
 *  1: please call again: more to do
 */
static int resolve_defaultroute_one(struct starter_end *host,
				struct starter_end *peer)
{
	/*
	 * "left="         == host->addrtype and host->addr
	 * "leftnexthop="  == host->nexttype and host->nexthop
	 */

	/* What kind of result are we seeking? */
	bool seeking_src = (host->addrtype == KH_DEFAULTROUTE);
	bool seeking_gateway = (host->nexttype == KH_DEFAULTROUTE);

	char msgbuf[RTNL_BUFSIZE];
	bool has_dst = FALSE;
	int query_again = 0;

	if (!seeking_src && !seeking_gateway)
		return 0;	/* this end already figured out */

	/* Fill netlink request */
	netlink_query_init(msgbuf, host->addr_family);
	if (host->nexttype == KH_IPADDR) {
		/*
		 * My nexthop (gateway) is specified.
		 * We need to figure out our source IP to get there.
		 */
		netlink_query_add(msgbuf, RTA_DST, &host->nexthop);
		has_dst = TRUE;
	} else if (peer->addrtype == KH_IPADDR) {
		/*
		 * Peer IP is specified.
		 * We may need to figure out source IP
		 * and gateway IP to get there.
		 */
		netlink_query_add(msgbuf, RTA_DST, &peer->addr);
		has_dst = TRUE;
		if (seeking_src && seeking_gateway &&
			host->addr_family == AF_INET) {
			/*
			 * If we have only peer IP and no gateway/src we must
			 * do two queries:
			 * 1) find out gateway for dst
			 * 2) find out src for that gateway
			 * Doing both in one query returns src for dst.
			 *
			 * (IPv6 returns link-local for gateway so we can and
			 * do seek both in one query.)
			 */
			seeking_src = FALSE;
			query_again = 1;
		}
	}
	if (has_dst && host->addrtype == KH_IPADDR) {
		/* SRC works only with DST */
		netlink_query_add(msgbuf, RTA_SRC, &host->addr);
	}

	/*
	 * If we have for example host=%defaultroute + peer=%any
	 * (no destination) the netlink reply will be full routing table.
	 * We must do two queries:
	 * 1) find out default gateway
	 * 2) find out src for that default gateway
	 */
	if (!has_dst) {
		if (seeking_src && seeking_gateway) {
			seeking_src = FALSE;
			query_again = 1;
		}
	}
	if (seeking_gateway) {
		struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf;

		nlmsg->nlmsg_flags |= NLM_F_DUMP;
        }

	if (verbose)
		printf("\nseeking_src = %d, seeking_gateway = %d, has_dst = %d\n",
			seeking_src, seeking_gateway, has_dst);

	/* Send netlink get_route request */

	ssize_t len = netlink_query(msgbuf);

	if (len < 0)
		return -1;

	/* Parse reply */
	struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf;

	for (; NLMSG_OK(nlmsg, len); nlmsg = NLMSG_NEXT(nlmsg, len)) {
		struct rtmsg *rtmsg;
		struct rtattr *rtattr;
		int rtlen;
		char r_interface[IF_NAMESIZE+1];
		char r_source[ADDRTOT_BUF];
		char r_gateway[ADDRTOT_BUF];
		char r_destination[ADDRTOT_BUF];
		bool ignore;

		if (nlmsg->nlmsg_type == NLMSG_DONE)
			break;

		if (nlmsg->nlmsg_type == NLMSG_ERROR) {
			printf("netlink error\n");
			return -1;
			break;
		}

		/* ignore all but IPv4 and IPv6 */
		rtmsg = (struct rtmsg *) NLMSG_DATA(nlmsg);
		if (rtmsg->rtm_family != AF_INET &&
			rtmsg->rtm_family != AF_INET6)
			continue;

		/* Parse one route entry */
		r_interface[0] = r_interface[IF_NAMESIZE] = r_source[0] =
			r_gateway[0] = r_destination[0] = '\0';
		rtattr = (struct rtattr *) RTM_RTA(rtmsg);
		rtlen = RTM_PAYLOAD(nlmsg);
		for (;
			RTA_OK(rtattr, rtlen);
			rtattr = RTA_NEXT(rtattr, rtlen)) {
			switch (rtattr->rta_type) {
			case RTA_OIF:
				if_indextoname(*(int *)RTA_DATA(rtattr),
					r_interface);
				break;

			case RTA_PREFSRC:
				inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr),
					r_source, sizeof(r_source));
				break;

			case RTA_GATEWAY:
				inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr),
					r_gateway, sizeof(r_gateway));
				break;

			case RTA_DST:
				inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr),
					r_destination,
					sizeof(r_destination));
				break;
			}
		}

		/*
		 * Ignore if not main table.
		 * Ignore ipsecX or mastX interfaces.
		 */
		ignore = rtmsg->rtm_table != RT_TABLE_MAIN ||
			startswith(r_interface, "ipsec") ||
			startswith(r_interface, "mast");

		if (verbose) {
			printf("dst %s via %s dev %s src %s table %d%s\n",
				r_destination,
				r_gateway,
				r_interface,
				r_source, rtmsg->rtm_table,
				ignore ? "" : " (ignored)");
		}

		if (ignore)
			continue;

		if (seeking_src && r_source[0] != '\0') {
			err_t err = tnatoaddr(r_source, 0, rtmsg->rtm_family,
					&host->addr);

			if (err == NULL) {
				host->addrtype = KH_IPADDR;
				seeking_src = FALSE;
				if (verbose)
					printf("set addr: %s\n", r_source);
			} else if (verbose) {
				printf("unknown source results from kernel (%s): %s\n",
					r_source, err);
			}
		}

		if (seeking_gateway && r_destination[0] == '\0' &&
			(has_dst || r_source[0] == '\0')) {
			if (r_gateway[0] == '\0' && r_interface[0] != '\0') {
				/*
				 * Point-to-Point default gw without "via IP"
				 * Attempt to find r_gateway as the IP address
				 * on the interface.
				 */
				resolve_ppp_peer(r_interface, host->addr_family,
						 r_gateway);
			}
			if (r_gateway[0] != '\0') {
				err_t err = tnatoaddr(r_gateway, 0,
						rtmsg->rtm_family,
						&host->nexthop);

				if (err != NULL) {
					printf("unknown gateway results from kernel: %s\n",
						err);
				} else {
					/* Note: Use first even if multiple */
					host->nexttype = KH_IPADDR;
					seeking_gateway = FALSE;
					if (verbose)
						printf("set nexthop: %s\n",
							r_gateway);
				}
			}
		}
	}
	return query_again;
}
Пример #2
0
/*
 * See if left->addr or left->next is %defaultroute and change it to IP.
 */
int resolve_defaultroute_one(struct starter_end *left,
			     struct starter_end *right)
{
    /* TODO: this will probably not work with Point-to-Point links */

    /* "left="         == left->addrtype + left->addr
     * "leftnexthop="  == left->nexttype + left->nexthop
     */

    /* What kind of result we want to parse? */
    int parse_src = (left->addrtype == KH_DEFAULTROUTE);
    int parse_gateway = (left->nexttype == KH_DEFAULTROUTE);
    if (parse_src == 0 && parse_gateway == 0)
	return 0;

    /* Fill netlink request */
    char msgbuf[RTNL_BUFSIZE];
    int has_dst = 0;
    netlink_query_init(msgbuf, left->addr_family);
    if (left->nexttype == KH_IPADDR) { /* My nexthop is specified */
	netlink_query_add(msgbuf, RTA_DST, &left->nexthop);
	has_dst = 1;
    } else if (right->addrtype == KH_IPADDR) { /* Peer IP is specified */
	netlink_query_add(msgbuf, RTA_DST, &right->addr);
	has_dst = 1;
    }
    if (has_dst && left->addrtype == KH_IPADDR) /* SRC works only with DST */
	netlink_query_add(msgbuf, RTA_SRC, &left->addr);

    /* If we have for example left=%defaultroute + right=%any, the netlink
     * reply will be full routing table. We just want default gateway for the
     * first run.
     */
    if (has_dst == 0) {
	struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf;
	nlmsg->nlmsg_flags |= NLM_F_DUMP;
        if (parse_gateway)
	    parse_src = 0;
    }
    if (verbose)
	printf("\nparse_src = %d, parse_gateway = %d, has_dst = %d\n",
	       parse_src, parse_gateway, has_dst);

    /* Send netlink get_route request */
    int len = netlink_query(msgbuf);
    if (len < 0)
	return -1;

    /* Parse reply */
    struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf;
    for (; NLMSG_OK(nlmsg, len); nlmsg = NLMSG_NEXT(nlmsg, len)) {
	struct rtmsg *rtmsg;
	struct rtattr *rtattr;
	int rtlen;
	char r_interface[IF_NAMESIZE];
	char r_source[ADDRTOT_BUF];
	char r_gateway[ADDRTOT_BUF];
	char r_destination[ADDRTOT_BUF];

	/* Check for IPv4 / IPv6 */
	rtmsg = (struct rtmsg *) NLMSG_DATA(nlmsg);
	if (rtmsg->rtm_family != AF_INET &&
	    rtmsg->rtm_family != AF_INET6)
	    continue;

	/* Parse one route entry */
	*r_interface = *r_source = *r_gateway = *r_destination = 0;
	rtattr = (struct rtattr *) RTM_RTA(rtmsg);
	rtlen = RTM_PAYLOAD(nlmsg);
	for (; RTA_OK(rtattr, rtlen); rtattr = RTA_NEXT(rtattr, rtlen)) {
	    switch (rtattr->rta_type) {
	    case RTA_OIF:
		if_indextoname(*(int *)RTA_DATA(rtattr), r_interface);
		break;

	    case RTA_PREFSRC:
		inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr),
			  r_source, sizeof(r_source));
		break;

	    case RTA_GATEWAY:
		inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr),
			  r_gateway, sizeof(r_gateway));
		break;

	    case RTA_DST:
		inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr),
			  r_destination, sizeof(r_destination));
		break;
	    }
	}
	if (verbose)
	    printf("dst %s via %s dev %s src %s\n",
		   r_destination, r_gateway, r_interface, r_source);

	err_t err;
	if (parse_src && *r_source != 0) {
	    err = tnatoaddr(r_source, 0, rtmsg->rtm_family, &left->addr);
	    if (err == NULL) {
		left->addrtype = KH_IPADDR;
		parse_src = 0;
		if (verbose)
		    printf("set addr: %s\n", r_source);
	    } else if (verbose)
		printf("unknown source results from kernel: %s\n", err);
	}
	if (parse_gateway && *r_gateway != 0 && (has_dst || *r_source == 0)) {
	    err = tnatoaddr(r_gateway, 0, rtmsg->rtm_family, &left->nexthop);
	    if (err == NULL) {
		left->nexttype = KH_IPADDR;
		parse_gateway = 0; /* Use first if multiple */
		if (verbose)
		    printf("set nexthop: %s\n", r_gateway);
	    } else if (verbose)
		printf("unknown gateway results from kernel: %s\n", err);
	}
    }

    /* If we parsed and found default_gateway, we must do the request again
     * to find out the source IP for that gateway.
     */
    return has_dst == 0 && parse_gateway == 0;
}