Example #1
0
static int fill_if_addr(struct if_entry *dest, struct nlmsg_entry *ainfo)
{
    struct if_addr *entry;
    struct nlmsghdr *n;
    struct ifaddrmsg *ifa;
    struct rtattr *rta_tb[IFA_MAX + 1];
    int len, err;

    for (; ainfo; ainfo = ainfo->next) {
        n = &ainfo->h;
        ifa = NLMSG_DATA(n);
        if (ifa->ifa_index != dest->if_index)
            continue;
        if (n->nlmsg_type != RTM_NEWADDR)
            continue;
        len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
        if (len < 0)
            continue;
        if (ifa->ifa_family != AF_INET &&
                ifa->ifa_family != AF_INET6)
            /* only IP addresses supported (at least for now) */
            continue;
        rtnl_parse(rta_tb, IFA_MAX, IFA_RTA(ifa), len);
        if (!rta_tb[IFA_LOCAL] && !rta_tb[IFA_ADDRESS])
            /* don't care about broadcast and anycast adresses */
            continue;

        entry = calloc(sizeof(struct if_addr), 1);
        if (!entry)
            return ENOMEM;

        if (!rta_tb[IFA_LOCAL]) {
            rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
            rta_tb[IFA_ADDRESS] = NULL;
        }
        if ((err = addr_init_netlink(&entry->addr, ifa, rta_tb[IFA_LOCAL])))
            return err;
        if (rta_tb[IFA_ADDRESS] &&
                memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]),
                       ifa->ifa_family == AF_INET ? 4 : 16)) {
            if ((err = addr_init_netlink(&entry->peer, ifa, rta_tb[IFA_ADDRESS])))
                return err;
        }

        list_append(&dest->addr, node(entry));
    }
    return 0;
}
Example #2
0
int nl_send(struct nl_handle *hnd, struct iovec *iov, int iovlen)
{
	struct sockaddr_nl sa = {
		.nl_family = AF_NETLINK,
	};
	struct msghdr msg = {
		.msg_name = &sa,
		.msg_namelen = sizeof(sa),
		.msg_iov = iov,
		.msg_iovlen = iovlen,
	};
	struct nlmsghdr *src = iov->iov_base;

	src->nlmsg_seq = ++hnd->seq;
	if (sendmsg(hnd->fd, &msg, 0) < 0)
		return errno;
	return 0;
}

int nl_recv(struct nl_handle *hnd, struct nlmsg_entry **dest, int is_dump)
{
	struct sockaddr_nl sa = {
		.nl_family = AF_NETLINK,
	};
	struct iovec iov;
	struct msghdr msg = {
		.msg_name = &sa,
		.msg_namelen = sizeof(sa),
		.msg_iov = &iov,
		.msg_iovlen = 1,
	};
	char buf[16384];
	int len, err;
	struct nlmsghdr *n;
	struct nlmsg_entry *ptr = NULL; /* GCC false positive */
	struct nlmsg_entry *entry;

	*dest = NULL;
	while (1) {
		iov.iov_base = buf;
		iov.iov_len = sizeof(buf);
		len = recvmsg(hnd->fd, &msg, 0);
		if (len < 0)
			return errno;
		if (!len)
			return EPIPE;
		if (sa.nl_pid) {
			/* not from the kernel */
			continue;
		}
		for (n = (struct nlmsghdr *)buf; NLMSG_OK(n, len); n = NLMSG_NEXT(n, len)) {
			if (n->nlmsg_pid != hnd->pid || n->nlmsg_seq != hnd->seq)
				continue;
			if (is_dump && n->nlmsg_type == NLMSG_DONE)
				return 0;
			if (n->nlmsg_type == NLMSG_ERROR) {
				struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(n);

				err = -nlerr->error;
				goto err_out;
			}
			entry = malloc(n->nlmsg_len + sizeof(void *));
			if (!entry) {
				err = ENOMEM;
				goto err_out;
			}
			entry->next = NULL;
			memcpy(&entry->h, n, n->nlmsg_len);
			if (!*dest)
				*dest = entry;
			else
				ptr->next = entry;
			ptr = entry;
			if (!is_dump)
				return 0;
		}
	}
err_out:
	nlmsg_free(*dest);
	*dest = NULL;
	return err;
}

int nl_exchange(struct nl_handle *hnd,
		struct nlmsghdr *src, struct nlmsg_entry **dest)
{
	struct iovec iov = {
		.iov_base = src,
		.iov_len = src->nlmsg_len,
	};
	int is_dump;
	int err;

	is_dump = !!(src->nlmsg_flags & NLM_F_DUMP);
	err = nl_send(hnd, &iov, 1);
	if (err)
		return err;
	return nl_recv(hnd, dest, is_dump);
}

/* The original payload is not freed. Returns 0 in case of error, length
 * of *dest otherwise. *dest is newly allocated. */
int nla_add_str(void *orig, int orig_len, int nla_type, const char *str,
		void **dest)
{
	struct nlattr *nla;
	int len = strlen(str) + 1;
	int size;

	size = NLA_ALIGN(orig_len) + NLA_HDRLEN + NLA_ALIGN(len);
	*dest = calloc(size, 1);
	if (!*dest)
		return 0;
	if (orig_len)
		memcpy(*dest, orig, orig_len);
	nla = *dest + NLA_ALIGN(orig_len);
	nla->nla_len = NLA_HDRLEN + len;
	nla->nla_type = nla_type;
	memcpy(nla + 1, str, len);
	return size;
}

int rtnl_open(struct nl_handle *hnd)
{
	return nl_open(hnd, NETLINK_ROUTE);
}

int rtnl_dump(struct nl_handle *hnd, int family, int type, struct nlmsg_entry **dest)
{
	struct {
		struct nlmsghdr n;
		struct ifinfomsg i;
	} req;

	memset(&req, 0, sizeof(req));
	req.n.nlmsg_len = sizeof(req);
	req.n.nlmsg_type = type;
	req.n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
	req.i.ifi_family = family;
	return nl_exchange(hnd, &req.n, dest);
}

void rtnl_parse(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
	memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
	while (RTA_OK(rta, len)) {
		if (rta->rta_type <= max)
			tb[rta->rta_type] = rta;
		rta = RTA_NEXT(rta, len);
	}
}

void rtnl_parse_nested(struct rtattr *tb[], int max, struct rtattr *rta)
{
	rtnl_parse(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta));
}

int genl_open(struct nl_handle *hnd)
{
	return nl_open(hnd, NETLINK_GENERIC);
}

int genl_request(struct nl_handle *hnd,
		 int type, int cmd, void *payload, int payload_len,
		 struct nlmsg_entry **dest)
{
	struct {
		struct nlmsghdr n;
		struct genlmsghdr g;
	} req;
	struct iovec iov[2];
	int err;

	memset(&req, 0, sizeof(req));
	req.n.nlmsg_len = sizeof(req) + payload_len;
	req.n.nlmsg_type = type;
	req.n.nlmsg_flags = NLM_F_REQUEST;
	req.g.cmd = cmd;
	req.g.version = 1;

	iov[0].iov_base = &req;
	iov[0].iov_len = sizeof(req);
	iov[1].iov_base = payload;
	iov[1].iov_len = payload_len;
	err = nl_send(hnd, iov, 2);
	if (err)
		return err;
	return nl_recv(hnd, dest, 0);
}

unsigned int genl_family_id(struct nl_handle *hnd, const char *name)
{
	unsigned int res = 0;
	struct nlattr *nla;
	int len;
	struct nlmsg_entry *dest;
	void *ptr;

	len = nla_add_str(NULL, 0, CTRL_ATTR_FAMILY_NAME, name, &ptr);
	if (!len)
		return 0;
	if (genl_request(hnd, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
			 ptr, len, &dest)) {
		free(ptr);
		return 0;
	}
	free(ptr);

	len = dest->h.nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
	ptr = (void *)&dest->h + NLMSG_HDRLEN + GENL_HDRLEN;

	while (len > NLA_HDRLEN) {
		nla = ptr;
		if (nla->nla_type == CTRL_ATTR_FAMILY_ID &&
		    nla->nla_len >= NLA_HDRLEN + 2) {
			res = *(uint16_t *)(nla + 1);
			break;
		}

		ptr += NLMSG_ALIGN(nla->nla_len);
		len -= NLMSG_ALIGN(nla->nla_len);
	}

	nlmsg_free(dest);
	return res;

}
Example #3
0
static int fill_if_link(struct if_entry *dest, struct nlmsghdr *n)
{
    struct ifinfomsg *ifi = NLMSG_DATA(n);
    struct rtattr *tb[IFLA_MAX + 1];
    struct rtattr *linkinfo[IFLA_INFO_MAX + 1];
    int len = n->nlmsg_len;
    int err;

    if (n->nlmsg_type != RTM_NEWLINK)
        return ENOENT;
    len -= NLMSG_LENGTH(sizeof(*ifi));
    if (len < 0)
        return ENOENT;
    rtnl_parse(tb, IFLA_MAX, IFLA_RTA(ifi), len);
    if (tb[IFLA_IFNAME] == NULL)
        return ENOENT;
    dest->if_index = ifi->ifi_index;
    dest->if_name = strdup(RTA_DATA(tb[IFLA_IFNAME]));
    if (!dest->if_name)
        return ENOMEM;
    if (ifi->ifi_flags & IFF_UP) {
        dest->flags |= IF_UP;
        if (ifi->ifi_flags & IFF_RUNNING)
            dest->flags |= IF_HAS_LINK;
    }
    if (tb[IFLA_MASTER])
        dest->master_index = NLA_GET_U32(tb[IFLA_MASTER]);
    if (tb[IFLA_LINK]) {
        dest->link_index = NLA_GET_U32(tb[IFLA_LINK]);
        if (tb[IFLA_LINK_NETNSID])
            dest->link_netnsid = NLA_GET_S32(tb[IFLA_LINK_NETNSID]);
    }
    if (tb[IFLA_MTU])
        dest->mtu = NLA_GET_U32(tb[IFLA_MTU]);
    if (tb[IFLA_LINKINFO])
        rtnl_parse_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);

    if(tb[IFLA_ADDRESS])
        if ((err = mac_addr_fill_netlink(&dest->mac_addr,
                                         RTA_DATA(tb[IFLA_ADDRESS]),
                                         RTA_PAYLOAD(tb[IFLA_ADDRESS]))))
            return err;

    if (ifi->ifi_flags & IFF_LOOPBACK) {
        dest->driver = strdup("loopback");
        dest->flags |= IF_LOOPBACK;
    } else
        dest->driver = ethtool_driver(dest->if_name);
    if (!dest->driver) {
        /* No ethtool ops available, try IFLA_INFO_KIND */
        if (tb[IFLA_LINKINFO] && linkinfo[IFLA_INFO_KIND])
            dest->driver = strdup(RTA_DATA(linkinfo[IFLA_INFO_KIND]));
    }
    if (!dest->driver) {
        /* Allow the program to continue at least with generic stuff
         * as there may be interfaces that do not implement any of
         * the mechanisms for driver detection that we use */
        dest->driver = strdup("unknown driver, please report a bug");
    }

    if ((err = if_handler_init(dest)))
        goto err_driver;

    if ((err = if_handler_netlink(dest, tb[IFLA_LINKINFO] ? linkinfo : NULL)))
        if (err != ENOENT)
            goto err_driver;

    return 0;

err_driver:
    free(dest->driver);
    dest->driver = NULL;
    free(dest->if_name);
    dest->if_name = NULL;
    return err;
}