/* Interface lookup by netlink socket. */ int interface_lookup_netlink (void) { int ret; /* Get interface information. */ ret = netlink_request (AF_PACKET, RTM_GETLINK, &netlink_cmd); if (ret < 0) return ret; ret = netlink_parse_info (netlink_interface, &netlink_cmd); if (ret < 0) return ret; /* Get IPv4 address of the interfaces. */ ret = netlink_request (AF_INET, RTM_GETADDR, &netlink_cmd); if (ret < 0) return ret; ret = netlink_parse_info (netlink_interface_addr, &netlink_cmd); if (ret < 0) return ret; #ifdef HAVE_IPV6 /* Get IPv6 address of the interfaces. */ ret = netlink_request (AF_INET6, RTM_GETADDR, &netlink_cmd); if (ret < 0) return ret; ret = netlink_parse_info (netlink_interface_addr, &netlink_cmd); if (ret < 0) return ret; #endif /* HAVE_IPV6 */ return 0; }
/* Adresses lookup bootstrap function */ static int netlink_address_lookup(void) { nl_handle_t nlh; int status = 0; if (netlink_socket(&nlh, 0, 0) < 0) return -1; /* IPv4 Address lookup */ if (netlink_request(&nlh, AF_INET, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); /* IPv6 Address lookup */ if (netlink_request(&nlh, AF_INET6, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); end_addr: netlink_close(&nlh); return status; }
/* Adresses lookup bootstrap function */ static int netlink_address_lookup(void) { nl_handle_t nlh; int status = 0; int ret, flags; if (netlink_socket(&nlh, 0) < 0) return -1; /* Set blocking flag */ ret = netlink_set_block(&nlh, &flags); if (ret < 0) log_message(LOG_INFO, "Netlink: Warning, couldn't set " "blocking flag to netlink socket..."); /* IPv4 Address lookup */ if (netlink_request(&nlh, AF_INET, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); /* IPv6 Address lookup */ if (netlink_request(&nlh, AF_INET6, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); end_addr: netlink_close(&nlh); return status; }
/* Routing table read function using netlink interface. Only called bootstrap time. */ int netlink_route_read () { int ret; /* Get IPv4 routing table. */ ret = netlink_request (AF_INET, RTM_GETROUTE, &netlink_cmd); if (ret < 0) return ret; ret = netlink_parse_info (netlink_routing_table, &netlink_cmd); if (ret < 0) return ret; #ifdef HAVE_IPV6 /* Get IPv6 routing table. */ ret = netlink_request (AF_INET6, RTM_GETROUTE, &netlink_cmd); if (ret < 0) return ret; ret = netlink_parse_info (netlink_routing_table, &netlink_cmd); if (ret < 0) return ret; #endif /* HAVE_IPV6 */ return 0; }
/* Interface lookup by netlink socket. */ int interface_lookup_netlink(struct zebra_ns *zns) { int ret; /* Get interface information. */ ret = netlink_request_intf_addr(zns, AF_PACKET, RTM_GETLINK, 0); if (ret < 0) return ret; ret = netlink_parse_info(netlink_interface, &zns->netlink_cmd, zns, 0, 1); if (ret < 0) return ret; /* Get interface information - for bridge interfaces. */ ret = netlink_request_intf_addr(zns, AF_BRIDGE, RTM_GETLINK, RTEXT_FILTER_BRVLAN); if (ret < 0) return ret; ret = netlink_parse_info(netlink_interface, &zns->netlink_cmd, zns, 0, 0); if (ret < 0) return ret; /* Get interface information - for bridge interfaces. */ ret = netlink_request_intf_addr(zns, AF_BRIDGE, RTM_GETLINK, RTEXT_FILTER_BRVLAN); if (ret < 0) return ret; ret = netlink_parse_info(netlink_interface, &zns->netlink_cmd, zns, 0, 0); if (ret < 0) return ret; /* Get IPv4 address of the interfaces. */ ret = netlink_request_intf_addr(zns, AF_INET, RTM_GETADDR, 0); if (ret < 0) return ret; ret = netlink_parse_info(netlink_interface_addr, &zns->netlink_cmd, zns, 0, 1); if (ret < 0) return ret; /* Get IPv6 address of the interfaces. */ ret = netlink_request_intf_addr(zns, AF_INET6, RTM_GETADDR, 0); if (ret < 0) return ret; ret = netlink_parse_info(netlink_interface_addr, &zns->netlink_cmd, zns, 0, 1); if (ret < 0) return ret; return 0; }
/* Interfaces lookup bootstrap function */ int netlink_interface_lookup(void) { nl_handle_t nlh; int status = 0; int ret, flags; if (netlink_socket(&nlh, 0) < 0) return -1; /* Set blocking flag */ ret = netlink_set_block(&nlh, &flags); if (ret < 0) log_message(LOG_INFO, "Netlink: Warning, couldn't set " "blocking flag to netlink socket..."); /* Interface lookup */ if (netlink_request(&nlh, AF_PACKET, RTM_GETLINK) < 0) { status = -1; goto end_int; } status = netlink_parse_info(netlink_if_link_filter, &nlh, NULL); end_int: netlink_close(&nlh); return status; }
/* sendmsg() to netlink socket then recvmsg(). */ int netlink_talk (struct nlmsghdr *n, struct nlsock *nl) { int status; 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 = ++netlink_cmd.seq; if (IS_ZEBRA_DEBUG_KERNEL) zlog_info ("netlink_talk: %s type %s(%u), seq=%u", netlink_cmd.name, lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, n->nlmsg_seq); /* Send message to netlink interface. */ status = sendmsg (nl->sock, &msg, 0); if (status < 0) { zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s", strerror (errno)); return -1; } status = netlink_parse_info (netlink_talk_filter, nl); return status; }
/* Kernel route reflection. */ static int kernel_read (struct thread *thread) { netlink_parse_info (netlink_information_fetch, &netlink); thread_add_read (hm->master, kernel_read, NULL, netlink.sock); return 0; }
int kernel_netlink(thread_t * thread) { if (thread->type != THREAD_READ_TIMEOUT) netlink_parse_info(netlink_broadcast_filter, &nl_kernel, NULL); thread_add_read(master, kernel_netlink, NULL, nl_kernel.fd, NETLINK_TIMER); return 0; }
static int kernel_netlink(thread_t * thread) { nl_handle_t *nl = THREAD_ARG(thread); if (thread->type != THREAD_READ_TIMEOUT) netlink_parse_info(netlink_broadcast_filter, nl, NULL); nl->thread = thread_add_read(master, kernel_netlink, nl, nl->fd, NETLINK_TIMER); return 0; }
/* Kernel route reflection. */ int kernel_read (struct thread *thread) { int ret; int sock; sock = THREAD_FD (thread); ret = netlink_parse_info (netlink_information_fetch, &netlink); thread_add_read (master, kernel_read, NULL, netlink.sock); return 0; }
/* Interfaces lookup bootstrap function */ int netlink_interface_lookup(void) { nl_handle_t nlh; int status = 0; if (netlink_socket(&nlh, 0, 0) < 0) return -1; /* Interface lookup */ if (netlink_request(&nlh, AF_PACKET, RTM_GETLINK) < 0) { status = -1; goto end_int; } status = netlink_parse_info(netlink_if_link_filter, &nlh, NULL); end_int: netlink_close(&nlh); return status; }
/* sendmsg() to netlink socket then recvmsg(). */ static int netlink_talk (struct nlmsghdr *n, struct nlsock *nl) { int status; struct sockaddr_nl snl; struct iovec iov = { (void *) n, n->nlmsg_len }; struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; int save_errno; memset (&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; n->nlmsg_seq = ++nl->seq; /* Request an acknowledgement by setting NLM_F_ACK */ n->nlmsg_flags |= NLM_F_ACK; if (IS_DEBUG_HA(kroute, KROUTE)) zlog_debug ("netlink_talk: %s type %s(%u), seq=%u", nl->name, lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, n->nlmsg_seq); /* Send message to netlink interface. */ status = sendmsg (nl->sock, &msg, 0); save_errno = errno; if (status < 0) { zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s", safe_strerror (save_errno)); return -1; } /* * Get reply from netlink socket. * The reply should either be an acknowlegement or an error. */ return netlink_parse_info (netlink_talk_filter, nl); }
/* 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; }
/* sendmsg() to netlink socket then recvmsg(). */ int netlink_talk (struct nlmsghdr *n, struct nlsock *nl, void *a, unsigned int size) { int status; struct sockaddr_nl snl; struct iovec iov = { (void *) n, n->nlmsg_len }; struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; int flags = 0; int snb_ret; int save_errno; memset (&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; n->nlmsg_seq = ++nl->seq; /* Request an acknowledgement by setting NLM_F_ACK */ if (!a) n->nlmsg_flags |= NLM_F_ACK; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("netlink_talk: %s type %s(%u), seq=%u", nl->name, lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, n->nlmsg_seq); /* Send message to netlink interface. */ if (zserv_privs.change (ZPRIVS_RAISE)) zlog (NULL, LOG_ERR, "Can't raise privileges"); status = sendmsg (nl->sock, &msg, 0); save_errno = errno; if (zserv_privs.change (ZPRIVS_LOWER)) zlog (NULL, LOG_ERR, "Can't lower privileges"); if (status < 0) { zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s", safe_strerror (save_errno)); return -1; } if (!a) { /* * Change socket flags for blocking I/O. * This ensures we wait for a reply in netlink_parse_info(). */ snb_ret = set_netlink_blocking (nl, &flags); if (snb_ret < 0) zlog (NULL, LOG_WARNING, "%s:%i Warning: Could not set netlink socket to blocking.", __FUNCTION__, __LINE__); } /* * Get reply from netlink socket. * The reply should either be an acknowlegement or an error. */ status = netlink_parse_info (netlink_talk_filter, nl, a, size); /* Restore socket flags for nonblocking I/O */ if (snb_ret == 0 && !a) set_netlink_nonblocking (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; }
/* sendmsg() to netlink socket then recvmsg(). */ int netlink_talk (struct nlmsghdr *n, struct nlsock *nl) { int status; struct sockaddr_nl snl; struct iovec iov = { (void*) n, n->nlmsg_len }; struct msghdr msg = {(void*) &snl, sizeof snl, &iov, 1, NULL, 0, 0}; int flags = 0; memset (&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; n->nlmsg_seq = ++netlink_cmd.seq; /* Request an acknowledgement by setting NLM_F_ACK */ n->nlmsg_flags |= NLM_F_ACK; if (IS_ZEBRA_DEBUG_KERNEL) zlog_info ("netlink_talk: %s type %s(%u), seq=%u", netlink_cmd.name, lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, n->nlmsg_seq); /* Send message to netlink interface. */ status = sendmsg (nl->sock, &msg, 0); if (status < 0) { zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s", strerror (errno)); return -1; } /* * Change socket flags for blocking I/O. * This ensures we wait for a reply in netlink_parse_info(). */ if((flags = fcntl(nl->sock, F_GETFL, 0)) < 0) { zlog (NULL, LOG_ERR, "%s:%i F_GETFL error: %s", __FUNCTION__, __LINE__, strerror (errno)); } flags &= ~O_NONBLOCK; if(fcntl(nl->sock, F_SETFL, flags) < 0) { zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s", __FUNCTION__, __LINE__, strerror (errno)); } /* * Get reply from netlink socket. * The reply should either be an acknowlegement or an error. */ status = netlink_parse_info (netlink_talk_filter, nl); /* Restore socket flags for nonblocking I/O */ flags |= O_NONBLOCK; if(fcntl(nl->sock, F_SETFL, flags) < 0) { zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s", __FUNCTION__, __LINE__, strerror (errno)); } return status; }