int tca_build_msg(struct rtnl_tc *tca, int type, int flags, struct nl_msg **result) { struct nl_msg *msg; struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = tca->tc_ifindex, .tcm_handle = tca->tc_handle, .tcm_parent = tca->tc_parent, }; msg = nlmsg_alloc_simple(type, flags); if (!msg) return -NLE_NOMEM; if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) goto nla_put_failure; if (tca->ce_mask & TCA_ATTR_KIND) NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind); *result = msg; return 0; nla_put_failure: nlmsg_free(msg); return -NLE_MSGSIZE; } void tca_set_kind(struct rtnl_tc *t, const char *kind) { strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1); t->ce_mask |= TCA_ATTR_KIND; }
static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link) { struct rtnl_link *peer = link->l_info; struct ifinfomsg ifi; struct nlattr *data, *info_peer; memset(&ifi, 0, sizeof ifi); ifi.ifi_family = peer->l_family; ifi.ifi_type = peer->l_arptype; ifi.ifi_index = peer->l_index; ifi.ifi_flags = peer->l_flags; ifi.ifi_change = peer->l_change; if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) return -NLE_MSGSIZE; if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER))) return -NLE_MSGSIZE; if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) return -NLE_MSGSIZE; rtnl_link_fill_info(msg, peer); nla_nest_end(msg, info_peer); nla_nest_end(msg, data); return 0; }
static struct nl_msg *sfq_get_opts(struct rtnl_qdisc *qdisc) { struct rtnl_sfq *sfq; struct tc_sfq_qopt opts; struct nl_msg *msg; sfq = sfq_qdisc(qdisc); if (!sfq) return NULL; msg = nlmsg_alloc(); if (!msg) goto errout; memset(&opts, 0, sizeof(opts)); opts.quantum = sfq->qs_quantum; opts.perturb_period = sfq->qs_perturb; opts.limit = sfq->qs_limit; if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) goto errout; return msg; errout: nlmsg_free(msg); return NULL; }
static struct nl_msg *fifo_get_opts(struct rtnl_qdisc *qdisc) { struct rtnl_fifo *fifo; struct tc_fifo_qopt opts; struct nl_msg *msg; fifo = fifo_qdisc(qdisc); if (!fifo || !(fifo->qf_mask & SCH_FIFO_ATTR_LIMIT)) return NULL; msg = nlmsg_build_no_hdr(); if (!msg) goto errout; memset(&opts, 0, sizeof(opts)); opts.limit = fifo->qf_limit; if (nlmsg_append(msg, &opts, sizeof(opts), 0) < 0) goto errout; return msg; errout: nlmsg_free(msg); return NULL; }
static struct nl_msg *build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags) { struct nl_msg *msg; struct rtmsg rtm = { .rtm_type = RTN_UNSPEC }; if (cmd == RTM_NEWRULE) rtm.rtm_type = RTN_UNICAST; if (tmpl->ce_mask & RULE_ATTR_FAMILY) rtm.rtm_family = tmpl->r_family; if (tmpl->ce_mask & RULE_ATTR_TABLE) rtm.rtm_table = tmpl->r_table; if (tmpl->ce_mask & RULE_ATTR_DSFIELD) rtm.rtm_tos = tmpl->r_dsfield; if (tmpl->ce_mask & RULE_ATTR_TYPE) rtm.rtm_type = tmpl->r_type; if (tmpl->ce_mask & RULE_ATTR_SRC_LEN) rtm.rtm_src_len = tmpl->r_src_len; if (tmpl->ce_mask & RULE_ATTR_DST_LEN) rtm.rtm_dst_len = tmpl->r_dst_len; msg = nlmsg_alloc_simple(cmd, flags); if (!msg) goto nla_put_failure; if (nlmsg_append(msg, &rtm, sizeof(rtm), NLMSG_ALIGNTO) < 0) goto nla_put_failure; if (tmpl->ce_mask & RULE_ATTR_SRC) NLA_PUT_ADDR(msg, RTA_SRC, tmpl->r_src); if (tmpl->ce_mask & RULE_ATTR_DST) NLA_PUT_ADDR(msg, RTA_DST, tmpl->r_dst); if (tmpl->ce_mask & RULE_ATTR_PRIO) NLA_PUT_U32(msg, RTA_PRIORITY, tmpl->r_prio); if (tmpl->ce_mask & RULE_ATTR_MARK) NLA_PUT_U32(msg, RTA_PROTOINFO, tmpl->r_mark); if (tmpl->ce_mask & RULE_ATTR_REALMS) NLA_PUT_U32(msg, RTA_FLOW, tmpl->r_realms); if (tmpl->ce_mask & RULE_ATTR_IIF) NLA_PUT_STRING(msg, RTA_IIF, tmpl->r_iif); return msg; nla_put_failure: nlmsg_free(msg); return NULL; }
/* return after DAD finishes for all known IPv6 addresses or an error */ int virNetDevIPWaitDadFinish(virSocketAddrPtr *addrs, size_t count) { struct nl_msg *nlmsg = NULL; struct ifaddrmsg ifa; struct nlmsghdr *resp = NULL; unsigned int recvbuflen; int ret = -1; bool dad = true; time_t max_time = time(NULL) + VIR_DAD_WAIT_TIMEOUT; if (!(nlmsg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP))) { virReportOOMError(); return -1; } memset(&ifa, 0, sizeof(ifa)); /* DAD is for IPv6 adresses only. */ ifa.ifa_family = AF_INET6; if (nlmsg_append(nlmsg, &ifa, sizeof(ifa), NLMSG_ALIGNTO) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; } /* Periodically query netlink until DAD finishes on all known addresses. */ while (dad && time(NULL) < max_time) { if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, NETLINK_ROUTE, 0) < 0) goto cleanup; if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, "%s", _("error reading DAD state information")); goto cleanup; } /* Parse response. */ dad = virNetDevIPParseDadStatus(resp, recvbuflen, addrs, count); if (dad) usleep(1000 * 10); VIR_FREE(resp); } /* Check timeout. */ if (dad) { virReportError(VIR_ERR_SYSTEM_ERROR, _("Duplicate Address Detection " "not finished in %d seconds"), VIR_DAD_WAIT_TIMEOUT); } else { ret = 0; } cleanup: VIR_FREE(resp); nlmsg_free(nlmsg); return ret; }
int rtnl_tc_msg_build(struct rtnl_tc *tc, int type, int flags, struct nl_msg **result) { struct nl_msg *msg; struct rtnl_tc_ops *ops; struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = tc->tc_ifindex, .tcm_handle = tc->tc_handle, .tcm_parent = tc->tc_parent, }; int err = -NLE_MSGSIZE; msg = nlmsg_alloc_simple(type, flags); if (!msg) return -NLE_NOMEM; if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) goto nla_put_failure; if (tc->ce_mask & TCA_ATTR_KIND) NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind); ops = rtnl_tc_get_ops(tc); if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) { struct nlattr *opts; void *data = rtnl_tc_data(tc); if (ops->to_msg_fill) { if (!(opts = nla_nest_start(msg, TCA_OPTIONS))) goto nla_put_failure; if ((err = ops->to_msg_fill(tc, data, msg)) < 0) goto nla_put_failure; nla_nest_end(msg, opts); } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0) goto nla_put_failure; } *result = msg; return 0; nla_put_failure: nlmsg_free(msg); return err; } void tca_set_kind(struct rtnl_tc *t, const char *kind) { strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1); t->ce_mask |= TCA_ATTR_KIND; }
TError TNlCgFilter::Create(const TNlLink &link) { TError error = TError::Success(); struct nl_msg *msg; int ret; struct tcmsg tchdr; tchdr.tcm_family = AF_UNSPEC; tchdr.tcm_ifindex = link.GetIndex(); tchdr.tcm_handle = Handle; tchdr.tcm_parent = Parent; tchdr.tcm_info = TC_H_MAKE(FilterPrio << 16, htons(ETH_P_ALL)); msg = nlmsg_alloc_simple(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE); if (!msg) return TError(EError::Unknown, "Unable to add filter: no memory"); ret = nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to add filter: ") + nl_geterror(ret)); goto free_msg; } ret = nla_put(msg, TCA_KIND, strlen(FilterType) + 1, FilterType); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to add filter: ") + nl_geterror(ret)); goto free_msg; } ret = nla_put(msg, TCA_OPTIONS, 0, NULL); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to add filter: ") + nl_geterror(ret)); goto free_msg; } L() << "netlink " << link.GetDesc() << ": add tfilter id 0x" << std::hex << Handle << " parent 0x" << Parent << std::dec << std::endl; ret = nl_send_sync(link.GetSock(), msg); if (ret) error = TError(EError::Unknown, std::string("Unable to add filter: ") + nl_geterror(ret)); if (!Exists(link)) error = TError(EError::Unknown, "BUG: created filter doesn't exist"); return error; free_msg: nlmsg_free(msg); return error; }
int nl_dect_cell_build_msg(struct nl_msg *msg, struct nl_dect_cell *cell) { struct dectmsg dm = { .dm_index = cell->c_index, }; if (nlmsg_append(msg, &dm, sizeof(dm), NLMSG_ALIGNTO) < 0) goto nla_put_failure; if (cell->ce_mask & CELL_ATTR_NAME) NLA_PUT_STRING(msg, DECTA_CELL_NAME, cell->c_name); if (cell->ce_mask & CELL_ATTR_FLAGS) NLA_PUT_U32(msg, DECTA_CELL_FLAGS, cell->c_flags); if (cell->ce_mask & CELL_ATTR_LINK) NLA_PUT_U8(msg, DECTA_CELL_CLUSTER, cell->c_link); return 0; nla_put_failure: return -NLE_MSGSIZE; } static struct trans_tbl cell_flags[] = { __ADD(DECT_CELL_CCP, ccp) __ADD(DECT_CELL_SLAVE, slave) __ADD(DECT_CELL_MONITOR, monitor) }; char *nl_dect_cell_flags2str(uint32_t flags, char *buf, size_t len) { return __flags2str(flags, buf, len, cell_flags, ARRAY_SIZE(cell_flags)); } uint32_t nl_dect_cell_str2flags(const char *str) { return __str2flags(str, cell_flags, ARRAY_SIZE(cell_flags)); } /** @cond SKIP */ struct nl_object_ops nl_dect_cell_obj_ops = { .oo_name = "nl_dect/cell", .oo_size = sizeof(struct nl_dect_cell), .oo_free_data = cell_free_data, .oo_dump = { [NL_DUMP_LINE] = cell_dump, }, .oo_id_attrs = CELL_ATTR_NAME,
static struct nl_msg * prepare_tcmsg(int type, int flags, uint32_t parent, uint32_t handle, uint32_t info) { struct nl_msg *msg = nlmsg_alloc_simple(type, flags); if (!msg) exit_errno("nlmsg_alloc_simple"); struct tcmsg tcmsg; memset(&tcmsg, 0, sizeof(tcmsg)); tcmsg.tcm_family = AF_UNSPEC; tcmsg.tcm_ifindex = ifindex; tcmsg.tcm_parent = parent; tcmsg.tcm_handle = handle; tcmsg.tcm_info = info; nlmsg_append(msg, &tcmsg, sizeof(tcmsg), NLMSG_ALIGNTO); return msg; }
static int red_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) { struct rtnl_red *red = data; if (!red) BUG(); #if 0 memset(&opts, 0, sizeof(opts)); opts.quantum = sfq->qs_quantum; opts.perturb_period = sfq->qs_perturb; opts.limit = sfq->qs_limit; if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) goto errout; #endif return -NLE_OPNOTSUPP; }
/** * Builds a netlink request message to do a lookup * @arg req Requested match. * @arg flags additional netlink message flags * @arg result Result pointer * * Builds a new netlink message requesting a change of link attributes. * The netlink message header isn't fully equipped with all relevant * fields and must be sent out via nl_send_auto_complete() or * supplemented as needed. * \a old must point to a link currently configured in the kernel * and \a tmpl must contain the attributes to be changed set via * \c rtnl_link_set_* functions. * * @return 0 on success or a negative error code. */ int flnl_lookup_build_request(struct flnl_request *req, int flags, struct nl_msg **result) { struct nl_msg *msg; struct nl_addr *addr; uint64_t fwmark; int tos, scope, table; struct fib_result_nl fr = {0}; fwmark = flnl_request_get_fwmark(req); tos = flnl_request_get_tos(req); scope = flnl_request_get_scope(req); table = flnl_request_get_table(req); fr.fl_fwmark = fwmark != UINT_LEAST64_MAX ? fwmark : 0; fr.fl_tos = tos >= 0 ? tos : 0; fr.fl_scope = scope >= 0 ? scope : RT_SCOPE_UNIVERSE; fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC; addr = flnl_request_get_addr(req); if (!addr) return -NLE_MISSING_ATTR; fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr); msg = nlmsg_alloc_simple(0, flags); if (!msg) return -NLE_NOMEM; if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0) goto errout; *result = msg; return 0; errout: nlmsg_free(msg); return -NLE_MSGSIZE; }
/** * Builds a netlink request message to do a lookup * @arg req Requested match. * @arg flags additional netlink message flags * * Builds a new netlink message requesting a change of link attributes. * The netlink message header isn't fully equipped with all relevant * fields and must be sent out via nl_send_auto_complete() or * supplemented as needed. * \a old must point to a link currently configured in the kernel * and \a tmpl must contain the attributes to be changed set via * \c rtnl_link_set_* functions. * * @return New netlink message * @note Not all attributes can be changed, see * \ref link_changeable "Changeable Attributes" for more details. */ struct nl_msg *flnl_lookup_build_request(struct flnl_request *req, int flags) { struct nl_msg *msg; struct nl_addr *addr; uint64_t fwmark; int tos, scope, table; struct fib_result_nl fr = {0}; fwmark = flnl_request_get_fwmark(req); tos = flnl_request_get_tos(req); scope = flnl_request_get_scope(req); table = flnl_request_get_table(req); fr.fl_fwmark = fwmark != UINT_LEAST64_MAX ? fwmark : 0; fr.fl_tos = tos >= 0 ? tos : 0; fr.fl_scope = scope >= 0 ? scope : RT_SCOPE_UNIVERSE; fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC; addr = flnl_request_get_addr(req); if (!addr) { nl_error(EINVAL, "Request must specify the address"); return NULL; } fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr); msg = nlmsg_alloc_simple(0, flags); if (!msg) goto errout; if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0) goto errout; return msg; errout: nlmsg_free(msg); return NULL; }
/** * Construct and transmit a Netlink message * @arg sk Netlink socket (required) * @arg type Netlink message type (required) * @arg flags Netlink message flags (optional) * @arg buf Data buffer (optional) * @arg size Size of data buffer (optional) * * Allocates a new Netlink message based on `type` and `flags`. If `buf` * points to payload of length `size` that payload will be appended to the * message. * * Sends out the message using `nl_send_auto()` and frees the message * afterwards. * * @see nl_send_auto() * * @return Number of characters sent on success or a negative error code. * @retval -NLE_NOMEM Unable to allocate Netlink message */ int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, size_t size) { int err; struct nl_msg *msg; msg = nlmsg_alloc_simple(type, flags); if (!msg) return -NLE_NOMEM; if (buf && size) { err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO); if (err < 0) goto errout; } err = nl_send_auto(sk, msg); errout: nlmsg_free(msg); return err; }
int xfrmnl_ae_build_get_request(struct nl_addr* daddr, unsigned int spi, unsigned int protocol, unsigned int mark_mask, unsigned int mark_value, struct nl_msg **result) { struct nl_msg *msg; struct xfrm_aevent_id ae_id; struct xfrmnl_mark mark; if (!daddr || !spi) { fprintf(stderr, "APPLICATION BUG: %s:%d:%s: A valid destination address, spi must be specified\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); assert(0); return -NLE_MISSING_ATTR; } memset(&ae_id, 0, sizeof(ae_id)); memcpy (&ae_id.sa_id.daddr, nl_addr_get_binary_addr (daddr), sizeof (uint8_t) * nl_addr_get_len (daddr)); ae_id.sa_id.spi = htonl(spi); ae_id.sa_id.family = nl_addr_get_family (daddr); ae_id.sa_id.proto = protocol; if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETAE, 0))) return -NLE_NOMEM; if (nlmsg_append(msg, &ae_id, sizeof(ae_id), NLMSG_ALIGNTO) < 0) goto nla_put_failure; mark.m = mark_mask; mark.v = mark_value; NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrmnl_mark), &mark); *result = msg; return 0; nla_put_failure: nlmsg_free(msg); return -NLE_MSGSIZE; }
static struct nl_msg *build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags) { struct nl_msg *msg; struct ifaddrmsg am = { .ifa_family = tmpl->a_family, .ifa_index = tmpl->a_ifindex, .ifa_prefixlen = tmpl->a_prefixlen, }; if (tmpl->a_mask & ADDR_ATTR_FLAGS) am.ifa_flags = tmpl->a_flags; if (tmpl->a_mask & ADDR_ATTR_SCOPE) am.ifa_scope = tmpl->a_scope; else { /* compatibility hack */ if (tmpl->a_family == AF_INET && tmpl->a_mask & ADDR_ATTR_LOCAL && *((char *) nl_addr_get_binary_addr(tmpl->a_local)) == 127) am.ifa_scope = RT_SCOPE_HOST; else am.ifa_scope = RT_SCOPE_UNIVERSE; } msg = nlmsg_build_simple(cmd, flags); if (!msg) goto nla_put_failure; if (nlmsg_append(msg, &am, sizeof(am), 1) < 0) goto nla_put_failure; if (tmpl->a_mask & ADDR_ATTR_LOCAL) NLA_PUT_ADDR(msg, IFA_LOCAL, tmpl->a_local); if (tmpl->a_mask & ADDR_ATTR_PEER) NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_peer); else NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_local); if (tmpl->a_mask & ADDR_ATTR_LABEL) NLA_PUT_STRING(msg, IFA_LABEL, tmpl->a_label); if (tmpl->a_mask & ADDR_ATTR_BROADCAST) NLA_PUT_ADDR(msg, IFA_BROADCAST, tmpl->a_bcast); if (tmpl->a_mask & ADDR_ATTR_ANYCAST) NLA_PUT_ADDR(msg, IFA_ANYCAST, tmpl->a_anycast); return msg; nla_put_failure: nlmsg_free(msg); return NULL; } /** * @name Address Addition * @{ */ /** * Build netlink request message to request addition of new address * @arg addr Address object representing the new address. * @arg flags Additional netlink message flags. * * Builds a new netlink message requesting the addition of a new * address. The netlink message header isn't fully equipped with * all relevant fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. * * Minimal required attributes: * - interface index (rtnl_addr_set_ifindex()) * - local address (rtnl_addr_set_local()) * * The scope will default to universe except for loopback addresses in * which case a host scope is used if not specified otherwise. * * @note Free the memory after usage using nlmsg_free(). * @return Newly allocated netlink message or NULL if an error occured. */ struct nl_msg *rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags) { int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY | ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL; if ((addr->a_mask & required) != required) { nl_error(EINVAL, "Missing mandatory attributes, required are: " "ifindex, family, prefixlen, local address."); return NULL; } return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags); }
/** * virNetlinkCommand: * @nlmsg: pointer to netlink message * @respbuf: pointer to pointer where response buffer will be allocated * @respbuflen: pointer to integer holding the size of the response buffer * on return of the function. * @src_pid: the pid of the process to send a message * @dst_pid: the pid of the process to talk to, i.e., pid = 0 for kernel * @protocol: netlink protocol * @groups: the group identifier * * Send the given message to the netlink layer and receive response. * Returns 0 on success, -1 on error. In case of error, no response * buffer will be returned. */ int virNetlinkCommand(struct nl_msg *nl_msg, struct nlmsghdr **resp, unsigned int *respbuflen, uint32_t src_pid, uint32_t dst_pid, unsigned int protocol, unsigned int groups) { int ret = -1; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = dst_pid, .nl_groups = 0, }; ssize_t nbytes; struct pollfd fds[1]; int fd; int n; struct nlmsghdr *nlmsg = nlmsg_hdr(nl_msg); virNetlinkHandle *nlhandle = NULL; int len = 0; if (protocol >= MAX_LINKS) { virReportSystemError(EINVAL, _("invalid protocol argument: %d"), protocol); goto cleanup; } if (!(nlhandle = virNetlinkCreateSocket(protocol))) goto cleanup; fd = nl_socket_get_fd(nlhandle); if (fd < 0) { virReportSystemError(errno, "%s", _("cannot get netlink socket fd")); goto cleanup; } if (groups && nl_socket_add_membership(nlhandle, groups) < 0) { virReportSystemError(errno, "%s", _("cannot add netlink membership")); goto cleanup; } nlmsg_set_dst(nl_msg, &nladdr); nlmsg->nlmsg_pid = src_pid ? src_pid : getpid(); nbytes = nl_send_auto_complete(nlhandle, nl_msg); if (nbytes < 0) { virReportSystemError(errno, "%s", _("cannot send to netlink socket")); goto cleanup; } memset(fds, 0, sizeof(fds)); fds[0].fd = fd; fds[0].events = POLLIN; n = poll(fds, ARRAY_CARDINALITY(fds), NETLINK_ACK_TIMEOUT_S); if (n <= 0) { if (n < 0) virReportSystemError(errno, "%s", _("error in poll call")); if (n == 0) virReportSystemError(ETIMEDOUT, "%s", _("no valid netlink response was received")); goto cleanup; } len = nl_recv(nlhandle, &nladdr, (unsigned char **)resp, NULL); if (len == 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nl_recv failed - returned 0 bytes")); goto cleanup; } if (len < 0) { virReportSystemError(errno, "%s", _("nl_recv failed")); goto cleanup; } ret = 0; *respbuflen = len; cleanup: if (ret < 0) { *resp = NULL; *respbuflen = 0; } virNetlinkFree(nlhandle); return ret; } /** * virNetlinkDumpLink: * * @ifname: The name of the interface; only use if ifindex <= 0 * @ifindex: The interface index; may be <= 0 if ifname is given * @data: Gets a pointer to the raw data from netlink. MUST BE FREED BY CALLER! * @nlattr: Pointer to a pointer of netlink attributes that will contain * the results * @src_pid: pid used for nl_pid of the local end of the netlink message * (0 == "use getpid()") * @dst_pid: pid of destination nl_pid if the kernel * is not the target of the netlink message but it is to be * sent to another process (0 if sending to the kernel) * * Get information from netlink about an interface given its name or index. * * Returns 0 on success, -1 on fatal error. */ int virNetlinkDumpLink(const char *ifname, int ifindex, void **nlData, struct nlattr **tb, uint32_t src_pid, uint32_t dst_pid) { int rc = -1; struct nlmsghdr *resp = NULL; struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC, .ifi_index = ifindex }; unsigned int recvbuflen; struct nl_msg *nl_msg; if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0) return -1; ifinfo.ifi_index = ifindex; nl_msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST); if (!nl_msg) { virReportOOMError(); return -1; } if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (ifname) { if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0) goto buffer_too_small; } # ifdef RTEXT_FILTER_VF /* if this filter exists in the kernel's netlink implementation, * we need to set it, otherwise the response message will not * contain the IFLA_VFINFO_LIST that we're looking for. */ { uint32_t ifla_ext_mask = RTEXT_FILTER_VF; if (nla_put(nl_msg, IFLA_EXT_MASK, sizeof(ifla_ext_mask), &ifla_ext_mask) < 0) { goto buffer_too_small; } } # endif if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, src_pid, dst_pid, NETLINK_ROUTE, 0) < 0) goto cleanup; if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL) goto malformed_resp; switch (resp->nlmsg_type) { case NLMSG_ERROR: err = (struct nlmsgerr *)NLMSG_DATA(resp); if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) goto malformed_resp; if (err->error) { virReportSystemError(-err->error, _("error dumping %s (%d) interface"), ifname, ifindex); goto cleanup; } break; case GENL_ID_CTRL: case NLMSG_DONE: rc = nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL); if (rc < 0) goto malformed_resp; break; default: goto malformed_resp; } rc = 0; cleanup: nlmsg_free(nl_msg); if (rc < 0) VIR_FREE(resp); *nlData = resp; return rc; malformed_resp: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed netlink response message")); goto cleanup; buffer_too_small: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }
int ompi_btl_usnic_nl_ip_rt_lookup(struct usnic_rtnl_sk *unlsk, const char *src_ifname, uint32_t src_addr, uint32_t dst_addr, int *metric) { struct nl_msg *nlm; struct rtmsg rmsg; struct nl_lookup_arg arg; int msg_cnt; int err; int oif; oif = if_nametoindex(src_ifname); if (0 == oif) { return errno; } arg.nh_addr = 0; arg.oif = oif; arg.found = 0; arg.replied = 0; arg.unlsk = unlsk; arg.msg_count = msg_cnt = 0; memset(&rmsg, 0, sizeof(rmsg)); rmsg.rtm_family = AF_INET; rmsg.rtm_dst_len = sizeof(dst_addr)*8; rmsg.rtm_src_len = sizeof(src_addr)*8; nlm = nlmsg_alloc_simple(RTM_GETROUTE, 0); nlmsg_append(nlm, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO); nla_put_u32(nlm, RTA_DST, dst_addr); nla_put_u32(nlm, RTA_SRC, src_addr); err = rtnl_send_ack_disable(unlsk, nlm); nlmsg_free(nlm); if (err < 0) { usnic_err("Failed to send rtnl query %s\n", nl_geterror(err)); return err; } err = nl_socket_modify_cb(unlsk->sock, NL_CB_MSG_IN, NL_CB_CUSTOM, rtnl_raw_parse_cb, &arg); if (err != 0) { usnic_err("Failed to setup callback function, error %s\n", nl_geterror(err)); return err; } while (!arg.replied) { err = nl_recvmsgs_default(unlsk->sock); if (err < 0) { /* err will be returned as -NLE_AGAIN if the socket times out */ usnic_err("Failed to receive rtnl query results %s\n", nl_geterror(err)); return err; } } if (arg.found) { if (metric != NULL) { *metric = arg.metric; } return 0; } else { return -1; } }
static struct nl_msg * virNetDevCreateNetlinkAddressMessage(int messageType, const char *ifname, virSocketAddr *addr, unsigned int prefix, virSocketAddr *broadcast, virSocketAddr *peer) { struct nl_msg *nlmsg = NULL; struct ifaddrmsg ifa; unsigned int ifindex; void *addrData = NULL; void *peerData = NULL; void *broadcastData = NULL; size_t addrDataLen; if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0) return NULL; if (peer && VIR_SOCKET_ADDR_VALID(peer)) { if (virNetDevGetIPAddressBinary(peer, &peerData, &addrDataLen) < 0) return NULL; } else if (broadcast) { if (virNetDevGetIPAddressBinary(broadcast, &broadcastData, &addrDataLen) < 0) return NULL; } /* Get the interface index */ if ((ifindex = if_nametoindex(ifname)) == 0) return NULL; if (!(nlmsg = nlmsg_alloc_simple(messageType, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL))) { virReportOOMError(); return NULL; } memset(&ifa, 0, sizeof(ifa)); ifa.ifa_prefixlen = prefix; ifa.ifa_family = VIR_SOCKET_ADDR_FAMILY(addr); ifa.ifa_index = ifindex; ifa.ifa_scope = 0; if (nlmsg_append(nlmsg, &ifa, sizeof(ifa), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (nla_put(nlmsg, IFA_LOCAL, addrDataLen, addrData) < 0) goto buffer_too_small; if (peerData) { if (nla_put(nlmsg, IFA_ADDRESS, addrDataLen, peerData) < 0) goto buffer_too_small; } if (broadcastData) { if (nla_put(nlmsg, IFA_BROADCAST, addrDataLen, broadcastData) < 0) goto buffer_too_small; } return nlmsg; buffer_too_small: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); nlmsg_free(nlmsg); return NULL; }
/** * virNetDevIPRouteAdd: * @ifname: the interface name * @addr: the IP network address (IPv4 or IPv6) * @prefix: number of 1 bits in the netmask * @gateway: via address for route (same as @addr) * * Add a route for a network IP address to an interface. This function * *does not* remove any previously added IP static routes. * * Returns 0 in case of success or -1 in case of error. */ int virNetDevIPRouteAdd(const char *ifname, virSocketAddrPtr addr, unsigned int prefix, virSocketAddrPtr gateway, unsigned int metric) { int ret = -1; struct nl_msg *nlmsg = NULL; struct nlmsghdr *resp = NULL; unsigned int recvbuflen; unsigned int ifindex; struct rtmsg rtmsg; void *gatewayData = NULL; void *addrData = NULL; size_t addrDataLen; int errCode; virSocketAddr defaultAddr; virSocketAddrPtr actualAddr; char *toStr = NULL; char *viaStr = NULL; actualAddr = addr; /* If we have no valid network address, then use the default one */ if (!addr || !VIR_SOCKET_ADDR_VALID(addr)) { VIR_DEBUG("computing default address"); int family = VIR_SOCKET_ADDR_FAMILY(gateway); if (family == AF_INET) { if (virSocketAddrParseIPv4(&defaultAddr, VIR_SOCKET_ADDR_IPV4_ALL) < 0) goto cleanup; } else { if (virSocketAddrParseIPv6(&defaultAddr, VIR_SOCKET_ADDR_IPV6_ALL) < 0) goto cleanup; } actualAddr = &defaultAddr; } toStr = virSocketAddrFormat(actualAddr); viaStr = virSocketAddrFormat(gateway); VIR_DEBUG("Adding route %s/%d via %s", toStr, prefix, viaStr); if (virNetDevGetIPAddressBinary(actualAddr, &addrData, &addrDataLen) < 0 || virNetDevGetIPAddressBinary(gateway, &gatewayData, &addrDataLen) < 0) goto cleanup; /* Get the interface index */ if ((ifindex = if_nametoindex(ifname)) == 0) goto cleanup; if (!(nlmsg = nlmsg_alloc_simple(RTM_NEWROUTE, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL))) { virReportOOMError(); goto cleanup; } memset(&rtmsg, 0, sizeof(rtmsg)); rtmsg.rtm_family = VIR_SOCKET_ADDR_FAMILY(gateway); rtmsg.rtm_table = RT_TABLE_MAIN; rtmsg.rtm_scope = RT_SCOPE_UNIVERSE; rtmsg.rtm_protocol = RTPROT_BOOT; rtmsg.rtm_type = RTN_UNICAST; rtmsg.rtm_dst_len = prefix; if (nlmsg_append(nlmsg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (prefix > 0 && nla_put(nlmsg, RTA_DST, addrDataLen, addrData) < 0) goto buffer_too_small; if (nla_put(nlmsg, RTA_GATEWAY, addrDataLen, gatewayData) < 0) goto buffer_too_small; if (nla_put_u32(nlmsg, RTA_OIF, ifindex) < 0) goto buffer_too_small; if (metric > 0 && nla_put_u32(nlmsg, RTA_PRIORITY, metric) < 0) goto buffer_too_small; if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, NETLINK_ROUTE, 0) < 0) goto cleanup; if ((errCode = virNetlinkGetErrorCode(resp, recvbuflen)) < 0) { virReportSystemError(errCode, _("Error adding route to %s"), ifname); goto cleanup; } ret = 0; cleanup: VIR_FREE(toStr); VIR_FREE(viaStr); nlmsg_free(nlmsg); return ret; buffer_too_small: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }
static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci, struct rtnl_route *route) { struct rtnl_rtcacheinfo nci = { .rtci_clntref = ci->rta_clntref, .rtci_last_use = ci->rta_lastuse, .rtci_expires = ci->rta_expires, .rtci_error = ci->rta_error, .rtci_used = ci->rta_used, .rtci_id = ci->rta_id, .rtci_ts = ci->rta_ts, .rtci_tsage = ci->rta_tsage, }; rtnl_route_set_cacheinfo(route, &nci); } static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *nlh, struct nl_parser_param *pp) { struct rtmsg *rtm; struct rtnl_route *route; struct nlattr *tb[RTA_MAX + 1]; struct nl_addr *src = NULL, *dst = NULL, *addr; int err; route = rtnl_route_alloc(); if (!route) { err = nl_errno(ENOMEM); goto errout; } route->ce_msgtype = nlh->nlmsg_type; err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy); if (err < 0) goto errout; rtm = nlmsg_data(nlh); rtnl_route_set_family(route, rtm->rtm_family); rtnl_route_set_tos(route, rtm->rtm_tos); rtnl_route_set_table(route, rtm->rtm_table); rtnl_route_set_type(route, rtm->rtm_type); rtnl_route_set_scope(route, rtm->rtm_scope); rtnl_route_set_protocol(route, rtm->rtm_protocol); rtnl_route_set_flags(route, rtm->rtm_flags); if (tb[RTA_DST]) { dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family); if (dst == NULL) goto errout_errno; } else { dst = nl_addr_alloc(0); nl_addr_set_family(dst, rtm->rtm_family); } nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); err = rtnl_route_set_dst(route, dst); if (err < 0) goto errout; nl_addr_put(dst); if (tb[RTA_SRC]) { src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family); if (src == NULL) goto errout_errno; } else if (rtm->rtm_src_len) src = nl_addr_alloc(0); if (src) { nl_addr_set_prefixlen(src, rtm->rtm_src_len); rtnl_route_set_src(route, src); nl_addr_put(src); } if (tb[RTA_IIF]) rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF])); if (tb[RTA_OIF]) rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF])); if (tb[RTA_GATEWAY]) { addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family); if (addr == NULL) goto errout_errno; rtnl_route_set_gateway(route, addr); nl_addr_put(addr); } if (tb[RTA_PRIORITY]) rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY])); if (tb[RTA_PREFSRC]) { addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family); if (addr == NULL) goto errout_errno; rtnl_route_set_pref_src(route, addr); nl_addr_put(addr); } if (tb[RTA_METRICS]) { struct nlattr *mtb[RTAX_MAX + 1]; int i; err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); if (err < 0) goto errout; for (i = 1; i <= RTAX_MAX; i++) { if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { uint32_t m = nla_get_u32(mtb[i]); if (rtnl_route_set_metric(route, i, m) < 0) goto errout_errno; } } } if (tb[RTA_MULTIPATH]) { struct rtnl_nexthop *nh; struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]); size_t tlen = nla_len(tb[RTA_MULTIPATH]); while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { nh = rtnl_route_nh_alloc(); if (!nh) goto errout; rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); if (rtnh->rtnh_len > sizeof(*rtnh)) { struct nlattr *ntb[RTA_MAX + 1]; nla_parse(ntb, RTA_MAX, (struct nlattr *) RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh), route_policy); if (ntb[RTA_GATEWAY]) { nh->rtnh_gateway = nla_get_addr( ntb[RTA_GATEWAY], route->rt_family); nh->rtnh_mask = NEXTHOP_HAS_GATEWAY; } } rtnl_route_add_nexthop(route, nh); tlen -= RTNH_ALIGN(rtnh->rtnh_len); rtnh = RTNH_NEXT(rtnh); } } if (tb[RTA_FLOW]) rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW])); if (tb[RTA_CACHEINFO]) copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route); if (tb[RTA_MP_ALGO]) rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_MP_ALGO])); err = pp->pp_cb((struct nl_object *) route, pp); if (err < 0) goto errout; err = P_ACCEPT; errout: rtnl_route_put(route); return err; errout_errno: err = nl_get_errno(); goto errout; } static int route_request_update(struct nl_cache *c, struct nl_handle *h) { return nl_rtgen_request(h, RTM_GETROUTE, AF_UNSPEC, NLM_F_DUMP); } /** * @name Cache Management * @{ */ /** * Build a route cache holding all routes currently configured in the kernel * @arg handle netlink handle * * Allocates a new cache, initializes it properly and updates it to * contain all routes currently configured in the kernel. * * @note The caller is responsible for destroying and freeing the * cache after using it. * @return The cache or NULL if an error has occured. */ struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle) { struct nl_cache *cache; cache = nl_cache_alloc(&rtnl_route_ops); if (!cache) return NULL; if (handle && nl_cache_refill(handle, cache) < 0) { free(cache); return NULL; } return cache; } /** @} */ /** * @name Route Addition * @{ */ static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd, int flags) { struct nl_msg *msg; struct nl_addr *addr; int scope, i, oif, nmetrics = 0; struct nlattr *metrics; struct rtmsg rtmsg = { .rtm_family = rtnl_route_get_family(tmpl), .rtm_dst_len = rtnl_route_get_dst_len(tmpl), .rtm_src_len = rtnl_route_get_src_len(tmpl), .rtm_tos = rtnl_route_get_tos(tmpl), .rtm_table = rtnl_route_get_table(tmpl), .rtm_type = rtnl_route_get_type(tmpl), .rtm_protocol = rtnl_route_get_protocol(tmpl), .rtm_flags = rtnl_route_get_flags(tmpl), }; if (rtmsg.rtm_family == AF_UNSPEC) { nl_error(EINVAL, "Cannot build route message, address " \ "family is unknown."); return NULL; } scope = rtnl_route_get_scope(tmpl); if (scope == RT_SCOPE_NOWHERE) { if (rtmsg.rtm_type == RTN_LOCAL) scope = RT_SCOPE_HOST; else { /* XXX Change to UNIVERSE if gw || nexthops */ scope = RT_SCOPE_LINK; } } rtmsg.rtm_scope = scope; msg = nlmsg_alloc_simple(cmd, flags); if (msg == NULL) return NULL; if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; addr = rtnl_route_get_dst(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_DST, addr); addr = rtnl_route_get_src(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_SRC, addr); addr = rtnl_route_get_gateway(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_GATEWAY, addr); addr = rtnl_route_get_pref_src(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_PREFSRC, addr); NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl)); oif = rtnl_route_get_oif(tmpl); if (oif != RTNL_LINK_NOT_FOUND) NLA_PUT_U32(msg, RTA_OIF, oif); for (i = 1; i <= RTAX_MAX; i++) if (rtnl_route_get_metric(tmpl, i) != UINT_MAX) nmetrics++; if (nmetrics > 0) { unsigned int val; metrics = nla_nest_start(msg, RTA_METRICS); if (metrics == NULL) goto nla_put_failure; for (i = 1; i <= RTAX_MAX; i++) { val = rtnl_route_get_metric(tmpl, i); if (val != UINT_MAX) NLA_PUT_U32(msg, i, val); } nla_nest_end(msg, metrics); } #if 0 RTA_IIF, RTA_MULTIPATH, RTA_PROTOINFO, RTA_FLOW, RTA_CACHEINFO, RTA_SESSION, RTA_MP_ALGO, #endif return msg; nla_put_failure: nlmsg_free(msg); return NULL; } struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags) { return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags); } int rtnl_route_add(struct nl_handle *handle, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; msg = rtnl_route_build_add_request(route, flags); if (!msg) return nl_get_errno(); err = nl_send_auto_complete(handle, msg); nlmsg_free(msg); if (err < 0) return err; return nl_wait_for_ack(handle); } struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags) { return build_route_msg(tmpl, RTM_DELROUTE, flags); } int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; msg = rtnl_route_build_del_request(route, flags); if (!msg) return nl_get_errno(); err = nl_send_auto_complete(handle, msg); nlmsg_free(msg); if (err < 0) return err; return nl_wait_for_ack(handle); } /** @} */ static struct nl_af_group route_groups[] = { { AF_INET, RTNLGRP_IPV4_ROUTE }, { AF_INET6, RTNLGRP_IPV6_ROUTE }, { AF_DECnet, RTNLGRP_DECnet_ROUTE }, { END_OF_GROUP_LIST }, }; static struct nl_cache_ops rtnl_route_ops = { .co_name = "route/route", .co_hdrsize = sizeof(struct rtmsg), .co_msgtypes = { { RTM_NEWROUTE, NL_ACT_NEW, "new" }, { RTM_DELROUTE, NL_ACT_DEL, "del" }, { RTM_GETROUTE, NL_ACT_GET, "get" }, END_OF_MSGTYPES_LIST, }, .co_protocol = NETLINK_ROUTE, .co_groups = route_groups, .co_request_update = route_request_update, .co_msg_parser = route_msg_parser, .co_obj_ops = &route_obj_ops, }; static void __init route_init(void) { nl_cache_mngt_register(&rtnl_route_ops); } static void __exit route_exit(void) { nl_cache_mngt_unregister(&rtnl_route_ops); }
/** * virNetDevMacVLanDelete: * * @ifname: Name of the interface * * Tear down an interface with the given name. * * Returns 0 on success, -1 on fatal error. */ int virNetDevMacVLanDelete(const char *ifname) { int rc = -1; struct nlmsghdr *resp; struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC }; unsigned char *recvbuf = NULL; unsigned int recvbuflen; struct nl_msg *nl_msg; nl_msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); if (!nl_msg) { virReportOOMError(); return -1; } if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0) goto buffer_too_small; if (virNetlinkCommand(nl_msg, &recvbuf, &recvbuflen, 0) < 0) { goto cleanup; } if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL) goto malformed_resp; resp = (struct nlmsghdr *)recvbuf; switch (resp->nlmsg_type) { case NLMSG_ERROR: err = (struct nlmsgerr *)NLMSG_DATA(resp); if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) goto malformed_resp; if (err->error) { virReportSystemError(-err->error, _("error destroying %s interface"), ifname); goto cleanup; } break; case NLMSG_DONE: break; default: goto malformed_resp; } rc = 0; cleanup: nlmsg_free(nl_msg); VIR_FREE(recvbuf); return rc; malformed_resp: virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed netlink response message")); goto cleanup; buffer_too_small: virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }
static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags, struct nl_msg **result) { struct nl_msg *msg; struct fib_rule_hdr frh = { .family = tmpl->r_family, .table = tmpl->r_table, .action = tmpl->r_action, .flags = tmpl->r_flags, .tos = tmpl->r_dsfield, }; if (!(tmpl->ce_mask & RULE_ATTR_FAMILY)) return -NLE_MISSING_ATTR; msg = nlmsg_alloc_simple(cmd, flags); if (!msg) return -NLE_NOMEM; if (nlmsg_append(msg, &frh, sizeof(frh), NLMSG_ALIGNTO) < 0) goto nla_put_failure; if (tmpl->ce_mask & RULE_ATTR_SRC) { frh.src_len = nl_addr_get_prefixlen(tmpl->r_src); NLA_PUT_ADDR(msg, FRA_SRC, tmpl->r_src); } if (tmpl->ce_mask & RULE_ATTR_DST) { frh.dst_len = nl_addr_get_prefixlen(tmpl->r_dst); NLA_PUT_ADDR(msg, FRA_DST, tmpl->r_dst); } if (tmpl->ce_mask & RULE_ATTR_IIFNAME) NLA_PUT_STRING(msg, FRA_IIFNAME, tmpl->r_iifname); if (tmpl->ce_mask & RULE_ATTR_OIFNAME) NLA_PUT_STRING(msg, FRA_OIFNAME, tmpl->r_oifname); if (tmpl->ce_mask & RULE_ATTR_PRIO) NLA_PUT_U32(msg, FRA_PRIORITY, tmpl->r_prio); if (tmpl->ce_mask & RULE_ATTR_MARK) NLA_PUT_U32(msg, FRA_FWMARK, tmpl->r_mark); if (tmpl->ce_mask & RULE_ATTR_MASK) NLA_PUT_U32(msg, FRA_FWMASK, tmpl->r_mask); if (tmpl->ce_mask & RULE_ATTR_GOTO) NLA_PUT_U32(msg, FRA_GOTO, tmpl->r_goto); if (tmpl->ce_mask & RULE_ATTR_FLOW) NLA_PUT_U32(msg, FRA_FLOW, tmpl->r_flow); *result = msg; return 0; nla_put_failure: nlmsg_free(msg); return -NLE_MSGSIZE; } /** * Build netlink request message to add a new rule * @arg tmpl template with data of new rule * @arg flags additional netlink message flags * * Builds a new netlink message requesting a addition of a new * rule. The netlink message header isn't fully equipped with * all relevant fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. \a tmpl must contain the attributes of the new * address set via \c rtnl_rule_set_* functions. * * @return The netlink message */ int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags, struct nl_msg **result) { return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags, result); } /** * Add a new rule * @arg sk Netlink socket. * @arg tmpl template with requested changes * @arg flags additional netlink message flags * * Builds a netlink message by calling rtnl_rule_build_add_request(), * sends the request to the kernel and waits for the next ACK to be * received and thus blocks until the request has been fullfilled. * * @return 0 on sucess or a negative error if an error occured. */ int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags) { struct nl_msg *msg; int err; if ((err = rtnl_rule_build_add_request(tmpl, flags, &msg)) < 0) return err; err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; return wait_for_ack(sk); } /** @} */ /** * @name Rule Deletion * @{ */ /** * Build a netlink request message to delete a rule * @arg rule rule to delete * @arg flags additional netlink message flags * * Builds a new netlink message requesting a deletion of a rule. * The netlink message header isn't fully equipped with all relevant * fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. \a rule must point to an existing * address. * * @return The netlink message */ int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags, struct nl_msg **result) { return build_rule_msg(rule, RTM_DELRULE, flags, result); } /** * Delete a rule * @arg sk Netlink socket. * @arg rule rule to delete * @arg flags additional netlink message flags * * Builds a netlink message by calling rtnl_rule_build_delete_request(), * sends the request to the kernel and waits for the next ACK to be * received and thus blocks until the request has been fullfilled. * * @return 0 on sucess or a negative error if an error occured. */ int rtnl_rule_delete(struct nl_sock *sk, struct rtnl_rule *rule, int flags) { struct nl_msg *msg; int err; if ((err = rtnl_rule_build_delete_request(rule, flags, &msg)) < 0) return err; err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; return wait_for_ack(sk); } /** @} */ /** * @name Attribute Modification * @{ */ void rtnl_rule_set_family(struct rtnl_rule *rule, int family) { rule->r_family = family; rule->ce_mask |= RULE_ATTR_FAMILY; }
TError TNlLink::AddXVlan(const std::string &vlantype, const std::string &master, uint32_t type, const std::string &hw, int mtu) { TError error = TError::Success(); int ret; uint32_t masterIdx; struct nl_msg *msg; struct nlattr *linkinfo, *infodata; struct ifinfomsg ifi = { 0 }; struct ether_addr *ea = nullptr; auto Name = GetName(); if (hw.length()) { // FIXME THREADS ea = ether_aton(hw.c_str()); if (!ea) return TError(EError::Unknown, "Invalid " + vlantype + " mac address " + hw); } TNlLink masterLink(Nl, master); error = masterLink.Load(); if (error) return error; masterIdx = masterLink.GetIndex(); msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_CREATE); if (!msg) return TError(EError::Unknown, "Unable to add " + vlantype + ": no memory"); ret = nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO); if (ret < 0) { error = TError(EError::Unknown, "Unable to add " + vlantype + ": " + nl_geterror(ret)); goto free_msg; } /* link configuration */ ret = nla_put(msg, IFLA_LINK, sizeof(uint32_t), &masterIdx); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to put IFLA_LINK: ") + nl_geterror(ret)); goto free_msg; } ret = nla_put(msg, IFLA_IFNAME, Name.length() + 1, Name.c_str()); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to put IFLA_IFNAME: ") + nl_geterror(ret)); goto free_msg; } if (mtu > 0) { ret = nla_put(msg, IFLA_MTU, sizeof(int), &mtu); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to put IFLA_MTU: ") + nl_geterror(ret)); goto free_msg; } } if (ea) { struct nl_addr *addr = nl_addr_build(AF_LLC, ea, ETH_ALEN); ret = nla_put(msg, IFLA_ADDRESS, nl_addr_get_len(addr), nl_addr_get_binary_addr(addr)); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to put IFLA_ADDRESS: ") + nl_geterror(ret)); goto free_msg; } nl_addr_put(addr); } /* link type */ linkinfo = nla_nest_start(msg, IFLA_LINKINFO); if (!linkinfo) { error = TError(EError::Unknown, "Unable to add " + vlantype + ": can't nest IFLA_LINKINFO"); goto free_msg; } ret = nla_put(msg, IFLA_INFO_KIND, vlantype.length() + 1, vlantype.c_str()); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to put IFLA_INFO_KIND: ") + nl_geterror(ret)); goto free_msg; } /* xvlan specific */ infodata = nla_nest_start(msg, IFLA_INFO_DATA); if (!infodata) { error = TError(EError::Unknown, "Unable to add " + vlantype + ": can't nest IFLA_INFO_DATA"); goto free_msg; } if (vlantype == "macvlan") { ret = nla_put(msg, IFLA_MACVLAN_MODE, sizeof(uint32_t), &type); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to put IFLA_MACVLAN_MODE: ") + nl_geterror(ret)); goto free_msg; } #ifdef IFLA_IPVLAN_MAX } else if (vlantype == "ipvlan") { uint16_t mode = type; ret = nla_put(msg, IFLA_IPVLAN_MODE, sizeof(uint16_t), &mode); if (ret < 0) { error = TError(EError::Unknown, std::string("Unable to put IFLA_IPVLAN_MODE: ") + nl_geterror(ret)); goto free_msg; } #endif } nla_nest_end(msg, infodata); nla_nest_end(msg, linkinfo); L() << "netlink: add " << vlantype << " " << Name << " master " << master << " type " << type << " hw " << hw << " mtu " << mtu << std::endl; ret = nl_send_sync(GetSock(), msg); if (ret) return Error(ret, "Cannot add " + vlantype); return Load(); free_msg: nlmsg_free(msg); return error; }
int opal_btl_usnic_nl_ip_rt_lookup(struct usnic_rtnl_sk *unlsk, const char *src_ifname, uint32_t src_addr, uint32_t dst_addr, int *metric) { struct nl_msg *nlm; struct rtmsg rmsg; struct nl_lookup_arg arg; int msg_cnt; int err; int oif; oif = if_nametoindex(src_ifname); if (0 == oif) { return errno; } arg.nh_addr = 0; arg.oif = oif; arg.found = 0; arg.replied = 0; arg.unlsk = unlsk; arg.msg_count = msg_cnt = 0; memset(&rmsg, 0, sizeof(rmsg)); rmsg.rtm_family = AF_INET; rmsg.rtm_dst_len = sizeof(dst_addr)*8; rmsg.rtm_src_len = sizeof(src_addr)*8; nlm = nlmsg_alloc_simple(RTM_GETROUTE, 0); nlmsg_append(nlm, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO); nla_put_u32(nlm, RTA_DST, dst_addr); nla_put_u32(nlm, RTA_SRC, src_addr); err = rtnl_send_ack_disable(unlsk, nlm); nlmsg_free(nlm); if (err < 0) { usnic_err("Failed to send nl route message to kernel, " "error %s\n", nl_geterror()); return err; } err = nl_socket_modify_cb(unlsk->nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, rtnl_raw_parse_cb, &arg); if (err != 0) { usnic_err("Failed to setup callback function, error %s\n", nl_geterror()); return err; } while (!arg.replied) { err = nl_recvmsgs_default(unlsk->nlh); if (err < 0) { usnic_err("Failed to receive nl route message from " "kernel, error %s\n", nl_geterror()); return err; } /* * the return value of nl_recvmsgs_default does not tell * whether it returns because of successful read or socket * timeout. So we compare msg count before and after the call * to decide if no new message arrives. In such case, * this function needs to terminate to prevent the caller from * blocking forever * NL_CB_MSG_IN traps every received message, so * there should be no premature exit */ if (msg_cnt != arg.msg_count) msg_cnt = arg.msg_count; else break; } if (arg.found) { if (metric != NULL) { *metric = arg.metric; } return 0; } else { return -1; } }
/** * Send netlink message. * @arg sk Netlink socket. * @arg msg Netlink message to be sent. * @arg iov iovec to be sent. * @arg iovlen number of struct iovec to be sent. * @see nl_sendmsg() * @return Number of characters sent on success or a negative error code. */ int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) { struct sockaddr_nl *dst; struct ucred *creds; struct msghdr hdr = { .msg_name = (void *) &sk->s_peer, .msg_namelen = sizeof(struct sockaddr_nl), .msg_iov = iov, .msg_iovlen = iovlen, }; /* Overwrite destination if specified in the message itself, defaults * to the peer address of the socket. */ dst = nlmsg_get_dst(msg); if (dst->nl_family == AF_NETLINK) hdr.msg_name = dst; /* Add credentials if present. */ creds = nlmsg_get_creds(msg); if (creds != NULL) { char buf[CMSG_SPACE(sizeof(struct ucred))]; struct cmsghdr *cmsg; hdr.msg_control = buf; hdr.msg_controllen = sizeof(buf); cmsg = CMSG_FIRSTHDR(&hdr); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); } return nl_sendmsg(sk, msg, &hdr); } /** * Send netlink message. * @arg sk Netlink socket. * @arg msg Netlink message to be sent. * @see nl_sendmsg() * @return Number of characters sent on success or a negative error code. */ int nl_send(struct nl_sock *sk, struct nl_msg *msg) { struct iovec iov = { .iov_base = (void *) nlmsg_hdr(msg), .iov_len = nlmsg_hdr(msg)->nlmsg_len, }; return nl_send_iovec(sk, msg, &iov, 1); } void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg) { struct nlmsghdr *nlh; nlh = nlmsg_hdr(msg); if (nlh->nlmsg_pid == 0) nlh->nlmsg_pid = sk->s_local.nl_pid; if (nlh->nlmsg_seq == 0) nlh->nlmsg_seq = sk->s_seq_next++; if (msg->nm_protocol == -1) msg->nm_protocol = sk->s_proto; nlh->nlmsg_flags |= NLM_F_REQUEST; if (!(sk->s_flags & NL_NO_AUTO_ACK)) nlh->nlmsg_flags |= NLM_F_ACK; } /** * Send netlink message and check & extend header values as needed. * @arg sk Netlink socket. * @arg msg Netlink message to be sent. * * Checks the netlink message \c nlh for completness and extends it * as required before sending it out. Checked fields include pid, * sequence nr, and flags. * * @see nl_send() * @return Number of characters sent or a negative error code. */ int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) { struct nl_cb *cb = sk->s_cb; nl_auto_complete(sk, msg); if (cb->cb_send_ow) return cb->cb_send_ow(sk, msg); else return nl_send(sk, msg); } /** * Send simple netlink message using nl_send_auto_complete() * @arg sk Netlink socket. * @arg type Netlink message type. * @arg flags Netlink message flags. * @arg buf Data buffer. * @arg size Size of data buffer. * * Builds a netlink message with the specified type and flags and * appends the specified data as payload to the message. * * @see nl_send_auto_complete() * @return Number of characters sent on success or a negative error code. */ int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, size_t size) { int err; struct nl_msg *msg; msg = nlmsg_alloc_simple(type, flags); if (!msg) return -NLE_NOMEM; if (buf && size) { err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO); if (err < 0) goto errout; } err = nl_send_auto_complete(sk, msg); errout: nlmsg_free(msg); return err; } /** @} */ /** * @name Receive * @{ */ /** * Receive data from netlink socket * @arg sk Netlink socket. * @arg nla Destination pointer for peer's netlink address. * @arg buf Destination pointer for message content. * @arg creds Destination pointer for credentials. * * Receives a netlink message, allocates a buffer in \c *buf and * stores the message content. The peer's netlink address is stored * in \c *nla. The caller is responsible for freeing the buffer allocated * in \c *buf if a positive value is returned. Interruped system calls * are handled by repeating the read. The input buffer size is determined * by peeking before the actual read is done. * * A non-blocking sockets causes the function to return immediately with * a return value of 0 if no data is available. * * @return Number of octets read, 0 on EOF or a negative error code. */ int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred **creds) { int n; int flags = 0; static int page_size = 0; struct iovec iov; struct msghdr msg = { .msg_name = (void *) nla, .msg_namelen = sizeof(struct sockaddr_nl), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0, }; struct cmsghdr *cmsg; if (sk->s_flags & NL_MSG_PEEK) flags |= MSG_PEEK; if (page_size == 0) page_size = getpagesize(); iov.iov_len = page_size; iov.iov_base = *buf = malloc(iov.iov_len); if (sk->s_flags & NL_SOCK_PASSCRED) { msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); msg.msg_control = calloc(1, msg.msg_controllen); } retry: n = recvmsg(sk->s_fd, &msg, flags); if (!n) goto abort; else if (n < 0) { if (errno == EINTR) { NL_DBG(3, "recvmsg() returned EINTR, retrying\n"); goto retry; } else if (errno == EAGAIN) { NL_DBG(3, "recvmsg() returned EAGAIN, aborting\n"); goto abort; } else { free(msg.msg_control); free(*buf); return -nl_syserr2nlerr(errno); } } if (iov.iov_len < n || msg.msg_flags & MSG_TRUNC) { /* Provided buffer is not long enough, enlarge it * and try again. */ iov.iov_len *= 2; iov.iov_base = *buf = realloc(*buf, iov.iov_len); goto retry; } else if (msg.msg_flags & MSG_CTRUNC) { msg.msg_controllen *= 2; msg.msg_control = realloc(msg.msg_control, msg.msg_controllen); goto retry; } else if (flags != 0) { /* Buffer is big enough, do the actual reading */ flags = 0; goto retry; } if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { free(msg.msg_control); free(*buf); return -NLE_NOADDR; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { *creds = calloc(1, sizeof(struct ucred)); memcpy(*creds, CMSG_DATA(cmsg), sizeof(struct ucred)); break; } } free(msg.msg_control); return n; abort: free(msg.msg_control); free(*buf); return 0; } #define NL_CB_CALL(cb, type, msg) \ do { \ err = nl_cb_call(cb, type, msg); \ switch (err) { \ case NL_OK: \ err = 0; \ break; \ case NL_SKIP: \ goto skip; \ case NL_STOP: \ goto stop; \ default: \ goto out; \ } \ } while (0) static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb) { int n, err = 0, multipart = 0; unsigned char *buf = NULL; struct nlmsghdr *hdr; struct sockaddr_nl nla = {0}; struct nl_msg *msg = NULL; struct ucred *creds = NULL; continue_reading: NL_DBG(3, "Attempting to read from %p\n", sk); if (cb->cb_recv_ow) n = cb->cb_recv_ow(sk, &nla, &buf, &creds); else n = nl_recv(sk, &nla, &buf, &creds); if (n <= 0) return n; NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", sk, n); hdr = (struct nlmsghdr *) buf; while (nlmsg_ok(hdr, n)) { NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", sk); nlmsg_free(msg); msg = nlmsg_convert(hdr); if (!msg) { err = -NLE_NOMEM; goto out; } nlmsg_set_proto(msg, sk->s_proto); nlmsg_set_src(msg, &nla); if (creds) nlmsg_set_creds(msg, creds); /* Raw callback is the first, it gives the most control * to the user and he can do his very own parsing. */ if (cb->cb_set[NL_CB_MSG_IN]) NL_CB_CALL(cb, NL_CB_MSG_IN, msg); /* Sequence number checking. The check may be done by * the user, otherwise a very simple check is applied * enforcing strict ordering */ if (cb->cb_set[NL_CB_SEQ_CHECK]) NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg); else if (hdr->nlmsg_seq != sk->s_seq_expect) { if (cb->cb_set[NL_CB_INVALID]) NL_CB_CALL(cb, NL_CB_INVALID, msg); else { err = -NLE_SEQ_MISMATCH; goto out; } } if (hdr->nlmsg_type == NLMSG_DONE || hdr->nlmsg_type == NLMSG_ERROR || hdr->nlmsg_type == NLMSG_NOOP || hdr->nlmsg_type == NLMSG_OVERRUN) { /* We can't check for !NLM_F_MULTI since some netlink * users in the kernel are broken. */ sk->s_seq_expect++; NL_DBG(3, "recvmsgs(%p): Increased expected " \ "sequence number to %d\n", sk, sk->s_seq_expect); } if (hdr->nlmsg_flags & NLM_F_MULTI) multipart = 1; /* Other side wishes to see an ack for this message */ if (hdr->nlmsg_flags & NLM_F_ACK) { if (cb->cb_set[NL_CB_SEND_ACK]) NL_CB_CALL(cb, NL_CB_SEND_ACK, msg); else { /* FIXME: implement */ } } /* messages terminates a multpart message, this is * usually the end of a message and therefore we slip * out of the loop by default. the user may overrule * this action by skipping this packet. */ if (hdr->nlmsg_type == NLMSG_DONE) { multipart = 0; if (cb->cb_set[NL_CB_FINISH]) NL_CB_CALL(cb, NL_CB_FINISH, msg); } /* Message to be ignored, the default action is to * skip this message if no callback is specified. The * user may overrule this action by returning * NL_PROCEED. */ else if (hdr->nlmsg_type == NLMSG_NOOP) { if (cb->cb_set[NL_CB_SKIPPED]) NL_CB_CALL(cb, NL_CB_SKIPPED, msg); else goto skip; } /* Data got lost, report back to user. The default action is to * quit parsing. The user may overrule this action by retuning * NL_SKIP or NL_PROCEED (dangerous) */ else if (hdr->nlmsg_type == NLMSG_OVERRUN) { if (cb->cb_set[NL_CB_OVERRUN]) NL_CB_CALL(cb, NL_CB_OVERRUN, msg); else { err = -NLE_MSG_OVERFLOW; goto out; } } /* Message carries a nlmsgerr */ else if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *e = nlmsg_data(hdr); if (hdr->nlmsg_len < nlmsg_msg_size(sizeof(*e))) { /* Truncated error message, the default action * is to stop parsing. The user may overrule * this action by returning NL_SKIP or * NL_PROCEED (dangerous) */ if (cb->cb_set[NL_CB_INVALID]) NL_CB_CALL(cb, NL_CB_INVALID, msg); else { err = -NLE_MSG_TRUNC; goto out; } } else if (e->error) { /* Error message reported back from kernel. */ if (cb->cb_err) { err = cb->cb_err(&nla, e, cb->cb_err_arg); if (err < 0) goto out; else if (err == NL_SKIP) goto skip; else if (err == NL_STOP) { err = -nl_syserr2nlerr(e->error); goto out; } } else { err = -nl_syserr2nlerr(e->error); goto out; } } else if (cb->cb_set[NL_CB_ACK]) NL_CB_CALL(cb, NL_CB_ACK, msg); } else { /* Valid message (not checking for MULTIPART bit to * get along with broken kernels. NL_SKIP has no * effect on this. */ if (cb->cb_set[NL_CB_VALID]) NL_CB_CALL(cb, NL_CB_VALID, msg); } skip: err = 0; hdr = nlmsg_next(hdr, &n); } nlmsg_free(msg); free(buf); free(creds); buf = NULL; msg = NULL; creds = NULL; if (multipart) { /* Multipart message not yet complete, continue reading */ goto continue_reading; } stop: err = 0; out: nlmsg_free(msg); free(buf); free(creds); return err; }
static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list) { struct rtnl_ematch *e; nl_list_for_each_entry(e, list, e_list) { struct tcf_ematch_hdr match = { .matchid = e->e_id, .kind = e->e_kind, .flags = e->e_flags, }; struct nlattr *attr; int err = 0; if (!(attr = nla_nest_start(msg, e->e_index + 1))) return -NLE_NOMEM; if (nlmsg_append(msg, &match, sizeof(match), 0) < 0) return -NLE_NOMEM; if (e->e_ops->eo_fill) err = e->e_ops->eo_fill(e, msg); else if (e->e_flags & TCF_EM_SIMPLE) err = nlmsg_append(msg, e->e_data, 4, 0); else if (e->e_datalen > 0) err = nlmsg_append(msg, e->e_data, e->e_datalen, 0); NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n", msg, e->e_index, match.matchid, match.kind, match.flags); if (err < 0) return -NLE_NOMEM; nla_nest_end(msg, attr); } nl_list_for_each_entry(e, list, e_list) { if (e->e_kind == TCF_EM_CONTAINER && fill_ematch_sequence(msg, &e->e_childs) < 0) return -NLE_NOMEM; } return 0; } int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid, struct rtnl_ematch_tree *tree) { struct tcf_ematch_tree_hdr thdr = { .progid = tree->et_progid, }; struct nlattr *list, *topattr; int err, index = 0; /* Assign index number to each ematch to allow for references * to be made while constructing the sequence of matches. */ err = update_container_index(&tree->et_list, &index); if (err < 0) return err; if (!(topattr = nla_nest_start(msg, attrid))) goto nla_put_failure; thdr.nmatches = index; NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr); if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST))) goto nla_put_failure; if (fill_ematch_sequence(msg, &tree->et_list) < 0) goto nla_put_failure; nla_nest_end(msg, list); nla_nest_end(msg, topattr); return 0; nla_put_failure: return -NLE_NOMEM; } /** @} */ extern int ematch_parse(void *, char **, struct nl_list_head *); int rtnl_ematch_parse_expr(const char *expr, char **errp, struct rtnl_ematch_tree **result) { struct rtnl_ematch_tree *tree; YY_BUFFER_STATE buf = NULL; yyscan_t scanner = NULL; int err; NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr); if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID))) return -NLE_FAILURE; if ((err = ematch_lex_init(&scanner)) < 0) { err = -NLE_FAILURE; goto errout; } buf = ematch__scan_string(expr, scanner); if ((err = ematch_parse(scanner, errp, &tree->et_list)) != 0) { ematch__delete_buffer(buf, scanner); err = -NLE_PARSE_ERR; goto errout; } ematch_lex_destroy(scanner); *result = tree; return 0; errout: if (scanner) ematch_lex_destroy(scanner); rtnl_ematch_tree_free(tree); return err; } static const char *layer_txt[] = { [TCF_LAYER_LINK] = "eth", [TCF_LAYER_NETWORK] = "ip", [TCF_LAYER_TRANSPORT] = "tcp", }; char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len) { snprintf(buf, len, "%s+%u", (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?", offset); return buf; } static const char *operand_txt[] = { [TCF_EM_OPND_EQ] = "=", [TCF_EM_OPND_LT] = "<", [TCF_EM_OPND_GT] = ">", }; char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len) { snprintf(buf, len, "%s", opnd < ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?"); return buf; }
int main(int argc, char *argv[]) { struct nl_handle *nlh; struct nl_cache *link_cache, *route_cache; struct nl_addr *dst; struct rtnl_route *route; struct ip_lookup_res res; struct nl_dump_params params = { .dp_fd = stdout, .dp_type = NL_DUMP_FULL }; int err = 1; if (argc < 2 || !strcmp(argv[1], "-h")) print_usage(); if (nltool_init(argc, argv) < 0) goto errout; nlh = nltool_alloc_handle(); if (!nlh) goto errout; route = rtnl_route_alloc(); if (!route) goto errout_free_handle; if (nltool_connect(nlh, NETLINK_ROUTE) < 0) goto errout_free_route; link_cache = nltool_alloc_link_cache(nlh); if (!link_cache) goto errout_close; dst = nltool_addr_parse(argv[1]); if (!dst) goto errout_link_cache; route_cache = nltool_alloc_route_cache(nlh); if (!route_cache) goto errout_addr_put; { struct nl_msg *m; struct rtmsg rmsg = { .rtm_family = nl_addr_get_family(dst), .rtm_dst_len = nl_addr_get_prefixlen(dst), }; m = nlmsg_alloc_simple(RTM_GETROUTE, 0); nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO); nla_put_addr(m, RTA_DST, dst); if ((err = nl_send_auto_complete(nlh, m)) < 0) { nlmsg_free(m); fprintf(stderr, "%s\n", nl_geterror()); goto errout_route_cache; } nlmsg_free(m); nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, cb, route_cache); if (nl_recvmsgs_default(nlh) < 0) { fprintf(stderr, "%s\n", nl_geterror()); goto errout_route_cache; } } rtnl_route_set_dst(route, dst); nl_cache_dump_filter(route_cache, ¶ms, (struct nl_object *) route); memset(&res, 0, sizeof(res)); nl_cache_foreach_filter(route_cache, (struct nl_object *) route, route_proc_cb, &res); printf("ip lookup result: oif idx: %d oif name %s ", res.oif, res.oifname); if (res.nh_addr) { char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &res.nh_addr, buf, sizeof(buf)); printf("via %s", buf); } printf ("\n"); err = 0; errout_route_cache: nl_cache_free(route_cache); errout_addr_put: nl_addr_put(dst); errout_link_cache: nl_cache_free(link_cache); errout_close: nl_close(nlh); errout_free_route: rtnl_route_put(route); errout_free_handle: nl_handle_destroy(nlh); errout: return err; }
/** * virNetDevMacVLanCreate: * * @ifname: The name the interface is supposed to have; optional parameter * @type: The type of device, i.e., "macvtap", "macvlan" * @macaddress: The MAC address of the device * @srcdev: The name of the 'link' device * @macvlan_mode: The macvlan mode to use * @retry: Pointer to integer that will be '1' upon return if an interface * with the same name already exists and it is worth to try * again with a different name * * Create a macvtap device with the given properties. * * Returns 0 on success, -1 on fatal error. */ int virNetDevMacVLanCreate(const char *ifname, const char *type, const unsigned char *macaddress, const char *srcdev, uint32_t macvlan_mode, int *retry) { int rc = -1; struct nlmsghdr *resp; struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC }; int ifindex; unsigned char *recvbuf = NULL; unsigned int recvbuflen; struct nl_msg *nl_msg; struct nlattr *linkinfo, *info_data; if (virNetDevGetIndex(srcdev, &ifindex) < 0) return -1; *retry = 0; nl_msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); if (!nl_msg) { virReportOOMError(); return -1; } if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (nla_put_u32(nl_msg, IFLA_LINK, ifindex) < 0) goto buffer_too_small; if (nla_put(nl_msg, IFLA_ADDRESS, VIR_MAC_BUFLEN, macaddress) < 0) goto buffer_too_small; if (ifname && nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0) goto buffer_too_small; if (!(linkinfo = nla_nest_start(nl_msg, IFLA_LINKINFO))) goto buffer_too_small; if (nla_put(nl_msg, IFLA_INFO_KIND, strlen(type), type) < 0) goto buffer_too_small; if (macvlan_mode > 0) { if (!(info_data = nla_nest_start(nl_msg, IFLA_INFO_DATA))) goto buffer_too_small; if (nla_put(nl_msg, IFLA_MACVLAN_MODE, sizeof(macvlan_mode), &macvlan_mode) < 0) goto buffer_too_small; nla_nest_end(nl_msg, info_data); } nla_nest_end(nl_msg, linkinfo); if (virNetlinkCommand(nl_msg, &recvbuf, &recvbuflen, 0) < 0) { goto cleanup; } if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL) goto malformed_resp; resp = (struct nlmsghdr *)recvbuf; switch (resp->nlmsg_type) { case NLMSG_ERROR: err = (struct nlmsgerr *)NLMSG_DATA(resp); if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) goto malformed_resp; switch (err->error) { case 0: break; case -EEXIST: *retry = 1; goto cleanup; default: virReportSystemError(-err->error, _("error creating %s type of interface"), type); goto cleanup; } break; case NLMSG_DONE: break; default: goto malformed_resp; } rc = 0; cleanup: nlmsg_free(nl_msg); VIR_FREE(recvbuf); return rc; malformed_resp: virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed netlink response message")); goto cleanup; buffer_too_small: virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }
/** * Send netlink message with control over sendmsg() message header. * @arg handle Netlink handle. * @arg msg Netlink message to be sent. * @arg hdr Sendmsg() message header. * @return Number of characters sent on sucess or a negative error code. */ int nl_sendmsg(struct nl_handle *handle, struct nl_msg *msg, struct msghdr *hdr) { struct nl_cb *cb; int ret; struct iovec iov = { .iov_base = (void *) nlmsg_hdr(msg), .iov_len = nlmsg_hdr(msg)->nlmsg_len, }; hdr->msg_iov = &iov; hdr->msg_iovlen = 1; nlmsg_set_src(msg, &handle->h_local); cb = nl_handle_get_cb(handle); if (cb->cb_set[NL_CB_MSG_OUT]) if (nl_cb_call(cb, NL_CB_MSG_OUT, msg) != NL_PROCEED) return 0; ret = sendmsg(handle->h_fd, hdr, 0); if (ret < 0) return nl_errno(errno); return ret; } /** * Send netlink message. * @arg handle Netlink handle * @arg msg Netlink message to be sent. * @see nl_sendmsg() * @return Number of characters sent on success or a negative error code. */ int nl_send(struct nl_handle *handle, struct nl_msg *msg) { struct sockaddr_nl *dst; struct ucred *creds; struct msghdr hdr = { .msg_name = (void *) &handle->h_peer, .msg_namelen = sizeof(struct sockaddr_nl), }; /* Overwrite destination if specified in the message itself, defaults * to the peer address of the handle. */ dst = nlmsg_get_dst(msg); if (dst->nl_family == AF_NETLINK) hdr.msg_name = dst; /* Add credentials if present. */ creds = nlmsg_get_creds(msg); if (creds != NULL) { char buf[CMSG_SPACE(sizeof(struct ucred))]; struct cmsghdr *cmsg; hdr.msg_control = buf; hdr.msg_controllen = sizeof(buf); cmsg = CMSG_FIRSTHDR(&hdr); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); } return nl_sendmsg(handle, msg, &hdr); } /** * Send netlink message and check & extend header values as needed. * @arg handle Netlink handle. * @arg msg Netlink message to be sent. * * Checks the netlink message \c nlh for completness and extends it * as required before sending it out. Checked fields include pid, * sequence nr, and flags. * * @see nl_send() * @return Number of characters sent or a negative error code. */ int nl_send_auto_complete(struct nl_handle *handle, struct nl_msg *msg) { struct nlmsghdr *nlh; nlh = nlmsg_hdr(msg); if (nlh->nlmsg_pid == 0) nlh->nlmsg_pid = handle->h_local.nl_pid; if (nlh->nlmsg_seq == 0) nlh->nlmsg_seq = handle->h_seq_next++; nlh->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); if (handle->h_cb->cb_send_ow) return handle->h_cb->cb_send_ow(handle, msg); else return nl_send(handle, msg); } /** * Send simple netlink message using nl_send_auto_complete() * @arg handle Netlink handle. * @arg type Netlink message type. * @arg flags Netlink message flags. * @arg buf Data buffer. * @arg size Size of data buffer. * * Builds a netlink message with the specified type and flags and * appends the specified data as payload to the message. * * @see nl_send_auto_complete() * @return Number of characters sent on success or a negative error code. */ int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf, size_t size) { int err; struct nl_msg *msg; struct nlmsghdr nlh = { .nlmsg_len = nlmsg_msg_size(0), .nlmsg_type = type, .nlmsg_flags = flags, }; msg = nlmsg_build(&nlh); if (!msg) return nl_errno(ENOMEM); if (buf && size) nlmsg_append(msg, buf, size, 1); err = nl_send_auto_complete(handle, msg); nlmsg_free(msg); return err; } /** @} */ /** * @name Receive * @{ */ /** * Receive netlink message from netlink socket. * @arg handle Netlink handle. * @arg nla Destination pointer for peer's netlink address. * @arg buf Destination pointer for message content. * @arg creds Destination pointer for credentials. * * Receives a netlink message, allocates a buffer in \c *buf and * stores the message content. The peer's netlink address is stored * in \c *nla. The caller is responsible for freeing the buffer allocated * in \c *buf if a positive value is returned. Interruped system calls * are handled by repeating the read. The input buffer size is determined * by peeking before the actual read is done. * * A non-blocking sockets causes the function to return immediately if * no data is available. * * @return Number of octets read, 0 on EOF or a negative error code. */ int nl_recv(struct nl_handle *handle, struct sockaddr_nl *nla, unsigned char **buf, struct ucred **creds) { int n; int flags = MSG_PEEK; struct iovec iov = { .iov_len = 4096, }; struct msghdr msg = { .msg_name = (void *) nla, .msg_namelen = sizeof(sizeof(struct sockaddr_nl)), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0, }; struct cmsghdr *cmsg; iov.iov_base = *buf = calloc(1, iov.iov_len); if (handle->h_flags & NL_SOCK_PASSCRED) { msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); msg.msg_control = calloc(1, msg.msg_controllen); } retry: if ((n = recvmsg(handle->h_fd, &msg, flags)) <= 0) { if (!n) goto abort; else if (n < 0) { if (errno == EINTR) goto retry; else if (errno == EAGAIN) goto abort; else { free(msg.msg_control); free(*buf); return nl_error(errno, "recvmsg failed"); } } } if (iov.iov_len < n) { /* Provided buffer is not long enough, enlarge it * and try again. */ iov.iov_len *= 2; iov.iov_base = *buf = realloc(*buf, iov.iov_len); goto retry; } else if (msg.msg_flags & MSG_CTRUNC) { msg.msg_controllen *= 2; msg.msg_control = realloc(msg.msg_control, msg.msg_controllen); goto retry; } else if (flags != 0) { /* Buffer is big enough, do the actual reading */ flags = 0; goto retry; } if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { free(msg.msg_control); free(*buf); return nl_error(EADDRNOTAVAIL, "socket address size mismatch"); } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { *creds = calloc(1, sizeof(struct ucred)); memcpy(*creds, CMSG_DATA(cmsg), sizeof(struct ucred)); break; } } free(msg.msg_control); return n; abort: free(msg.msg_control); free(*buf); return 0; } /** * Receive a set of messages from a netlink socket. * @arg handle netlink handle * @arg cb set of callbacks to control the behaviour. * * Repeatedly calls nl_recv() and parses the messages as netlink * messages. Stops reading if one of the callbacks returns * NL_EXIT or nl_recv returns either 0 or a negative error code. * * A non-blocking sockets causes the function to return immediately if * no data is available. * * @return 0 on success or a negative error code from nl_recv(). */ int nl_recvmsgs(struct nl_handle *handle, struct nl_cb *cb) { int n, err = 0; unsigned char *buf = NULL; struct nlmsghdr *hdr; struct sockaddr_nl nla = {0}; struct nl_msg *msg = NULL; struct ucred *creds = NULL; continue_reading: if (cb->cb_recv_ow) n = cb->cb_recv_ow(handle, &nla, &buf, &creds); else n = nl_recv(handle, &nla, &buf, &creds); if (n <= 0) return n; NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", handle, n); hdr = (struct nlmsghdr *) buf; while (nlmsg_ok(hdr, n)) { NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", handle); nlmsg_free(msg); msg = nlmsg_convert(hdr); if (!msg) { err = nl_errno(ENOMEM); goto out; } nlmsg_set_proto(msg, handle->h_proto); nlmsg_set_src(msg, &nla); if (creds) nlmsg_set_creds(msg, creds); /* Raw callback is the first, it gives the most control * to the user and he can do his very own parsing. */ if (cb->cb_set[NL_CB_MSG_IN]) { err = nl_cb_call(cb, NL_CB_MSG_IN, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } /* Sequence number checking. The check may be done by * the user, otherwise a very simple check is applied * enforcing strict ordering */ if (cb->cb_set[NL_CB_SEQ_CHECK]) { err = nl_cb_call(cb, NL_CB_SEQ_CHECK, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } else if (hdr->nlmsg_seq != handle->h_seq_expect) { if (cb->cb_set[NL_CB_INVALID]) { err = nl_cb_call(cb, NL_CB_INVALID, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } else goto out; } if (hdr->nlmsg_type == NLMSG_DONE || hdr->nlmsg_type == NLMSG_ERROR || hdr->nlmsg_type == NLMSG_NOOP || hdr->nlmsg_type == NLMSG_OVERRUN) { /* We can't check for !NLM_F_MULTI since some netlink * users in the kernel are broken. */ handle->h_seq_expect++; NL_DBG(3, "recvmsgs(%p): Increased expected " \ "sequence number to %d\n", handle, handle->h_seq_expect); } /* Other side wishes to see an ack for this message */ if (hdr->nlmsg_flags & NLM_F_ACK) { if (cb->cb_set[NL_CB_SEND_ACK]) { err = nl_cb_call(cb, NL_CB_SEND_ACK, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } else { /* FIXME: implement */ } } /* messages terminates a multpart message, this is * usually the end of a message and therefore we slip * out of the loop by default. the user may overrule * this action by skipping this packet. */ if (hdr->nlmsg_type == NLMSG_DONE) { if (cb->cb_set[NL_CB_FINISH]) { err = nl_cb_call(cb, NL_CB_FINISH, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } err = 0; goto out; } /* Message to be ignored, the default action is to * skip this message if no callback is specified. The * user may overrule this action by returning * NL_PROCEED. */ else if (hdr->nlmsg_type == NLMSG_NOOP) { if (cb->cb_set[NL_CB_SKIPPED]) { err = nl_cb_call(cb, NL_CB_SKIPPED, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } else goto skip; } /* Data got lost, report back to user. The default action is to * quit parsing. The user may overrule this action by retuning * NL_SKIP or NL_PROCEED (dangerous) */ else if (hdr->nlmsg_type == NLMSG_OVERRUN) { if (cb->cb_set[NL_CB_OVERRUN]) { err = nl_cb_call(cb, NL_CB_OVERRUN, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } else goto out; } /* Message carries a nlmsgerr */ else if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *e = nlmsg_data(hdr); if (hdr->nlmsg_len < nlmsg_msg_size(sizeof(*e))) { /* Truncated error message, the default action * is to stop parsing. The user may overrule * this action by returning NL_SKIP or * NL_PROCEED (dangerous) */ if (cb->cb_set[NL_CB_INVALID]) { err = nl_cb_call(cb, NL_CB_INVALID, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } else goto out; } else if (e->error) { /* Error message reported back from kernel. */ if (cb->cb_err) { err = cb->cb_err(&nla, e, cb->cb_err_arg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) { nl_error(-e->error, "Netlink Error"); err = e->error; goto out; } } else { nl_error(-e->error, "Netlink Error"); err = e->error; goto out; } } else if (cb->cb_set[NL_CB_ACK]) { /* ACK */ err = nl_cb_call(cb, NL_CB_ACK, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } } else { /* Valid message (not checking for MULTIPART bit to * get along with broken kernels. NL_SKIP has no * effect on this. */ if (cb->cb_set[NL_CB_VALID]) { err = nl_cb_call(cb, NL_CB_VALID, msg); if (err == NL_SKIP) goto skip; else if (err == NL_EXIT || err < 0) goto out; } } skip: hdr = nlmsg_next(hdr, &n); } nlmsg_free(msg); free(buf); free(creds); buf = NULL; msg = NULL; creds = NULL; /* Multipart message not yet complete, continue reading */ goto continue_reading; out: nlmsg_free(msg); free(buf); free(creds); return err; } /** * Receive a set of message from a netlink socket using handlers in nl_handle. * @arg handle netlink handle * * Calls nl_recvmsgs() with the handlers configured in the netlink handle. */ int nl_recvmsgs_def(struct nl_handle *handle) { if (handle->h_cb->cb_recvmsgs_ow) return handle->h_cb->cb_recvmsgs_ow(handle, handle->h_cb); else return nl_recvmsgs(handle, handle->h_cb); } static int ack_wait_handler(struct nl_msg *msg, void *arg) { return NL_EXIT; } /** * Wait for ACK. * @arg handle netlink handle * @pre The netlink socket must be in blocking state. * * Waits until an ACK is received for the latest not yet acknowledged * netlink message. */ int nl_wait_for_ack(struct nl_handle *handle) { int err; struct nl_cb *cb = nl_cb_clone(nl_handle_get_cb(handle)); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL); err = nl_recvmsgs(handle, cb); nl_cb_destroy(cb); return err; } /** @} */ /** * @name Netlink Family Translations * @{ */ static struct trans_tbl nlfamilies[] = { __ADD(NETLINK_ROUTE,route) __ADD(NETLINK_W1,w1) __ADD(NETLINK_USERSOCK,usersock) __ADD(NETLINK_FIREWALL,firewall) __ADD(NETLINK_INET_DIAG,inetdiag) __ADD(NETLINK_NFLOG,nflog) __ADD(NETLINK_XFRM,xfrm) __ADD(NETLINK_SELINUX,selinux) __ADD(NETLINK_ISCSI,iscsi) __ADD(NETLINK_AUDIT,audit) __ADD(NETLINK_FIB_LOOKUP,fib_lookup) __ADD(NETLINK_CONNECTOR,connector) __ADD(NETLINK_NETFILTER,netfilter) __ADD(NETLINK_IP6_FW,ip6_fw) __ADD(NETLINK_DNRTMSG,dnrtmsg) __ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent) __ADD(NETLINK_GENERIC,generic) }; /** * Convert netlink family to character string. * @arg family Netlink family. * @arg buf Destination buffer. * @arg size Size of destination buffer. * * Converts a netlink family to a character string and stores it in * the specified destination buffer. * * @return The destination buffer or the family encoded in hexidecimal * form if no match was found. */ char * nl_nlfamily2str(int family, char *buf, size_t size) { return __type2str(family, buf, size, nlfamilies, ARRAY_SIZE(nlfamilies)); } /** * Convert character string to netlink family. * @arg name Name of netlink family. * * Converts the provided character string specifying a netlink * family to the corresponding numeric value. * * @return Numeric netlink family or a negative value if no match was found. */ int nl_str2nlfamily(const char *name) { return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies)); }