/* send message to netlink kernel socket, then receive response */ int netlink_talk(nl_handle_t *nl, struct nlmsghdr *n) { int status; int ret, flags; struct sockaddr_nl snl; struct iovec iov = { (void *) n, n->nlmsg_len }; struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; memset(&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; n->nlmsg_seq = ++nl->seq; /* Request Netlink acknowledgement */ n->nlmsg_flags |= NLM_F_ACK; /* Send message to netlink interface. */ status = sendmsg(nl->fd, &msg, 0); if (status < 0) { log_message(LOG_INFO, "Netlink: sendmsg() error: %s", strerror(errno)); return -1; } /* Set blocking flag */ ret = netlink_set_block(nl, &flags); if (ret < 0) log_message(LOG_INFO, "Netlink: Warning, couldn't set " "blocking flag to netlink socket..."); status = netlink_parse_info(netlink_talk_filter, nl, n); /* Restore previous flags */ if (ret == 0) netlink_set_nonblock(nl, &flags); return status; }
/* Our netlink parser */ static int netlink_parse_info(int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), nl_handle_t *nl, struct nlmsghdr *n) { int status; int ret = 0; int error; while (1) { char buf[4096]; struct iovec iov = { .iov_base = buf, .iov_len = sizeof buf }; struct sockaddr_nl snl; struct msghdr msg = { .msg_name = &snl, .msg_namelen = sizeof(snl), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0 }; struct nlmsghdr *h; status = recvmsg(nl->fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK || errno == EAGAIN) break; log_message(LOG_INFO, "Netlink: Received message overrun (%m)"); continue; } if (status == 0) { log_message(LOG_INFO, "Netlink: EOF"); return -1; } if (msg.msg_namelen != sizeof snl) { log_message(LOG_INFO, "Netlink: Sender address length error: length %d", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) { /* Finish of reading. */ if (h->nlmsg_type == NLMSG_DONE) return ret; /* Error handling. */ if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(h); /* * If error == 0 then this is a netlink ACK. * return if not related to multipart message. */ if (err->error == 0) { if (!(h->nlmsg_flags & NLM_F_MULTI)) return 0; continue; } if (h->nlmsg_len < NLMSG_LENGTH(sizeof (struct nlmsgerr))) { log_message(LOG_INFO, "Netlink: error: message truncated"); return -1; } if (n && (err->error == -EEXIST) && ((n->nlmsg_type == RTM_NEWROUTE) || (n->nlmsg_type == RTM_NEWADDR))) return 0; /* If have more than one IPv4 address in the same CIDR * and the "primary" address is removed, unless promote_secondaries * is configured on the interface, all the "secondary" addresses * in the same CIDR are deleted */ if (n && err->error == -EADDRNOTAVAIL && n->nlmsg_type == RTM_DELADDR) { netlink_if_address_filter(NULL, n); if (!(h->nlmsg_flags & NLM_F_MULTI)) return 0; continue; } if (netlink_error_ignore != -err->error) log_message(LOG_INFO, "Netlink: error: %s, type=(%u), seq=%u, pid=%d", strerror(-err->error), err->msg.nlmsg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); return -1; } /* Skip unsolicited messages from cmd channel */ if (nl != &nl_cmd && h->nlmsg_pid == nl_cmd.nl_pid) continue; error = (*filter) (&snl, h); if (error < 0) { log_message(LOG_INFO, "Netlink: filter function error"); ret = error; } } /* After error care. */ if (msg.msg_flags & MSG_TRUNC) { log_message(LOG_INFO, "Netlink: error: message truncated"); continue; } if (status) { log_message(LOG_INFO, "Netlink: error: data remnant size %d", status); return -1; } } return ret; } /* Out talk filter */ static int netlink_talk_filter(struct sockaddr_nl *snl, struct nlmsghdr *h) { log_message(LOG_INFO, "Netlink: ignoring message type 0x%04x", h->nlmsg_type); return 0; } /* send message to netlink kernel socket, then receive response */ int netlink_talk(nl_handle_t *nl, struct nlmsghdr *n) { int status; int ret, flags; struct sockaddr_nl snl; struct iovec iov = { .iov_base = n, .iov_len = n->nlmsg_len }; struct msghdr msg = { .msg_name = &snl, .msg_namelen = sizeof(snl), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0 }; memset(&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; n->nlmsg_seq = ++nl->seq; /* Request Netlink acknowledgement */ n->nlmsg_flags |= NLM_F_ACK; /* Send message to netlink interface. */ status = sendmsg(nl->fd, &msg, 0); if (status < 0) { log_message(LOG_INFO, "Netlink: sendmsg() error: %s", strerror(errno)); return -1; } /* Set blocking flag */ ret = netlink_set_block(nl, &flags); if (ret < 0) log_message(LOG_INFO, "Netlink: Warning, couldn't set " "blocking flag to netlink socket..."); status = netlink_parse_info(netlink_talk_filter, nl, n); /* Restore previous flags */ if (ret == 0) netlink_set_nonblock(nl, &flags); return status; } /* Fetch a specific type information from netlink kernel */ static int netlink_request(nl_handle_t *nl, int family, int type) { int status; struct sockaddr_nl snl; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; /* Cleanup the room */ memset(&snl, 0, sizeof (snl)); snl.nl_family = AF_NETLINK; req.nlh.nlmsg_len = sizeof (req); req.nlh.nlmsg_type = type; req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = ++nl->seq; req.g.rtgen_family = family; status = sendto(nl->fd, (void *) &req, sizeof (req) , 0, (struct sockaddr *) &snl, sizeof (snl)); if (status < 0) { log_message(LOG_INFO, "Netlink: sendto() failed: %s", strerror(errno)); return -1; } return 0; } static int netlink_if_link_populate(interface_t *ifp, struct rtattr *tb[], struct ifinfomsg *ifi) { char *name; int i; #ifdef _HAVE_VRRP_VMAC_ struct rtattr* linkinfo[IFLA_INFO_MAX+1]; struct rtattr* linkattr[IFLA_MACVLAN_MAX+1]; interface_t *ifp_base; #endif name = (char *) RTA_DATA(tb[IFLA_IFNAME]); /* Fill the interface structure */ memcpy(ifp->ifname, name, strlen(name)); ifp->ifindex = ifi->ifi_index; ifp->mtu = *(int *) RTA_DATA(tb[IFLA_MTU]); ifp->hw_type = ifi->ifi_type; if (tb[IFLA_ADDRESS]) { int hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); if (hw_addr_len > IF_HWADDR_MAX) { log_message(LOG_ERR, "MAC address for %s is too large: %d", name, hw_addr_len); return -1; } else { ifp->hw_addr_len = hw_addr_len; memcpy(ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), hw_addr_len); for (i = 0; i < hw_addr_len; i++) if (ifp->hw_addr[i] != 0) break; if (i == hw_addr_len) ifp->hw_addr_len = 0; else ifp->hw_addr_len = hw_addr_len; } } #ifdef _HAVE_VRRP_VMAC_ /* See if this interface is a MACVLAN of ours */ if (tb[IFLA_LINKINFO] && tb[IFLA_LINK]){ /* If appears that the value of *(int*)RTA_DATA(tb[IFLA_LINKINFO]) is 0x1000c * for macvlan. 0x10000 for nested data, or'ed with 0x0c for macvlan; * other values are 0x09 for vlan, 0x0b for bridge, 0x08 for tun, -1 for no * underlying interface. * * I can't find where in the kernel these values are set or defined, so use * the string as below. */ parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); if (linkinfo[IFLA_INFO_KIND] && RTA_PAYLOAD(linkinfo[IFLA_INFO_KIND]) >= strlen(macvlan_ll_kind) && !strncmp(macvlan_ll_kind, RTA_DATA(linkinfo[IFLA_INFO_KIND]), strlen(macvlan_ll_kind)) && linkinfo[IFLA_INFO_DATA]) { parse_rtattr_nested(linkattr, IFLA_MACVLAN_MAX, linkinfo[IFLA_INFO_DATA]); if (linkattr[IFLA_MACVLAN_MODE] && *(int*)RTA_DATA(linkattr[IFLA_MACVLAN_MODE]) == MACVLAN_MODE_PRIVATE) { ifp->base_ifindex = *(int*)RTA_DATA(tb[IFLA_LINK]); ifp->vmac = true; } } } if (!ifp->vmac) #endif { #ifdef _HAVE_VRRP_VMAC_ if_vmac_reflect_flags(ifi->ifi_index, ifi->ifi_flags); #endif ifp->flags = ifi->ifi_flags; #ifdef _HAVE_VRRP_VMAC_ log_message(LOG_INFO, "Setting base index for %s to ifi_index %d", name, ifi->ifi_index); ifp->base_ifindex = ifi->ifi_index; #endif } #ifdef _HAVE_VRRP_VMAC_ else { if ((ifp_base = if_get_by_ifindex(ifp->base_ifindex))) ifp->flags = ifp_base->flags; } #endif return 1; }