Ejemplo n.º 1
0
int netlink_request_simple(void *request, __u32 request_len)
{
	struct nl_msg *msg;
	int error;

	msg = nlmsg_alloc();
	if (!msg) {
		log_err("Could not allocate the request; it seems we're out of memory.");
		return -ENOMEM;
	}

	if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family, 0, 0,
			JOOL_COMMAND, 1)) {
		log_err("Unknown error building the packet to the kernel.");
		nlmsg_free(msg);
		return -EINVAL;
	}

	error = nla_put(msg, ATTR_DATA, request_len, request);
	if (error) {
		log_err("Could not write on the packet to kernelspace.");
		nlmsg_free(msg);
		return netlink_print_error(error);
	}

	error = nl_send_auto(sk, msg);
	nlmsg_free(msg);
	if (error < 0) {
		log_err("Could not dispatch the request to kernelspace.");
		return netlink_print_error(error);
	}

	return 0;
}
Ejemplo n.º 2
0
Archivo: ae.c Proyecto: Domikk/libnl
int xfrmnl_ae_get_kernel(struct nl_sock* sock, struct nl_addr* daddr, unsigned int spi, unsigned int protocol,
                         unsigned int mark_mask, unsigned int mark_value, struct xfrmnl_ae** result)
{
	struct nl_msg *msg = NULL;
	struct nl_object *obj;
	int err;

	if ((err = xfrmnl_ae_build_get_request(daddr, spi, protocol, mark_mask, mark_value, &msg)) < 0)
		return err;

	err = nl_send_auto(sock, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	if ((err = nl_pickup(sock, &xfrm_ae_msg_parser, &obj)) < 0)
		return err;

	/* We have used xfrm_ae_msg_parser(), object is definitely a xfrm ae */
	*result = (struct xfrmnl_ae *) obj;

	/* If an object has been returned, we also need to wait for the ACK */
	 if (err == 0 && obj)
		 nl_wait_for_ack(sock);

	return 0;
}
Ejemplo n.º 3
0
int match_nl_create_update_destroy_table(struct nl_sock *nsd, uint32_t pid,
				unsigned int ifindex, int family,
				struct net_mat_tbl *table, uint8_t cmd)
{
	struct nlattr *tb[NET_MAT_MAX+1];
	struct nlattr *nest, *nest1;
	struct nlmsghdr *nlh;
	struct match_msg *msg;
	sigset_t bs;
	int err = 0;

	msg = match_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
	if (!msg) {
		MAT_LOG(ERR, "Error: Allocation failure\n");
		return -ENOMSG;
	}

	if (nla_put_u32(msg->nlbuf,
			NET_MAT_IDENTIFIER_TYPE,
			NET_MAT_IDENTIFIER_IFINDEX) ||
	    nla_put_u32(msg->nlbuf, NET_MAT_IDENTIFIER, ifindex)) {
		MAT_LOG(ERR, "Error: Identifier put failed\n");
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}

	nest = nla_nest_start(msg->nlbuf, NET_MAT_TABLES);
	if (!nest) {
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}
	nest1 = nla_nest_start(msg->nlbuf, NET_MAT_TABLE);
	match_put_table(msg->nlbuf, table);
	nla_nest_end(msg->nlbuf, nest1);
	nla_nest_end(msg->nlbuf, nest);
	nl_send_auto(nsd, msg->nlbuf);
	match_nl_free_msg(msg);

	sigemptyset(&bs);
	sigaddset(&bs, SIGINT);
	sigprocmask(SIG_UNBLOCK, &bs, NULL);

	msg = match_nl_recv_msg(nsd, &err);
	sigprocmask(SIG_BLOCK, &bs, NULL);

	if (!msg)
		return -EINVAL;

	nlh = msg->msg;
	err = genlmsg_parse(nlh, 0, tb, NET_MAT_MAX, match_get_tables_policy);
	if (err < 0) {
		MAT_LOG(ERR, "Warning: unable to parse create table msg\n");
		match_nl_free_msg(msg);
		return err;
	}
	match_nl_free_msg(msg);
	return 0;
}
Ejemplo n.º 4
0
indigo_error_t
indigo_port_stats_get(
    of_port_stats_request_t *port_stats_request,
    of_port_stats_reply_t **port_stats_reply_ptr)
{
    of_port_no_t req_of_port_num;
    of_port_stats_reply_t *port_stats_reply;
    indigo_error_t err = INDIGO_ERROR_NONE;

    port_stats_reply = of_port_stats_reply_new(port_stats_request->version);
    if (port_stats_reply == NULL) {
        err = INDIGO_ERROR_RESOURCE;
        goto out;
    }

    of_list_port_stats_entry_t list;
    of_port_stats_reply_entries_bind(port_stats_reply, &list);

    of_port_stats_request_port_no_get(port_stats_request, &req_of_port_num);
    int dump_all = req_of_port_num == OF_PORT_DEST_NONE_BY_VERSION(port_stats_request->version);

    /* Refresh statistics */
    nl_cache_refill(route_cache_sock, link_cache);

    struct nl_msg *msg = ind_ovs_create_nlmsg(ovs_vport_family, OVS_VPORT_CMD_GET);
    if (dump_all) {
        nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;
    } else {
        nla_put_u32(msg, OVS_VPORT_ATTR_PORT_NO, req_of_port_num);
    }

    /* Ask kernel to send us one or more OVS_VPORT_CMD_NEW messages */
    if (nl_send_auto(ind_ovs_socket, msg) < 0) {
        err = INDIGO_ERROR_UNKNOWN;
        goto out;
    }
    ind_ovs_nlmsg_freelist_free(msg);

    /* Handle OVS_VPORT_CMD_NEW messages */
    nl_cb_set(netlink_callbacks, NL_CB_VALID, NL_CB_CUSTOM,
              port_stats_iterator, &list);
    if (nl_recvmsgs(ind_ovs_socket, netlink_callbacks) < 0) {
        err = INDIGO_ERROR_UNKNOWN;
        goto out;
    }

out:
    if (err != INDIGO_ERROR_NONE) {
        of_port_stats_reply_delete(port_stats_reply);
        port_stats_reply = NULL;
    }

    *port_stats_reply_ptr = port_stats_reply;
    return err;
}
Ejemplo n.º 5
0
int netlink_request(void *request, __u32 request_len,
		jool_response_cb cb, void *cb_arg)
{
	struct nl_msg *msg;
	struct response_cb callback = { .cb = cb, .arg = cb_arg };
	int error;

	error = nl_socket_modify_cb(sk, NL_CB_MSG_IN, NL_CB_CUSTOM,
			response_handler, &callback);
	if (error < 0) {
		log_err("Could not register response handler.");
		log_err("I will not be able to parse Jool's response, so I won't send the request.");
		return netlink_print_error(error);
	}

	msg = nlmsg_alloc();
	if (!msg) {
		log_err("Could not allocate the message to the kernel; it seems we're out of memory.");
		return -ENOMEM;
	}

	if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family, 0, 0,
			JOOL_COMMAND, 1)) {
		log_err("Unknown error building the packet to the kernel.");
		nlmsg_free(msg);
		return -EINVAL;
	}

	error = nla_put(msg, ATTR_DATA, request_len, request);
	if (error) {
		log_err("Could not write on the packet to kernelspace.");
		nlmsg_free(msg);
		return netlink_print_error(error);
	}

	error = nl_send_auto(sk, msg);
	nlmsg_free(msg);
	if (error < 0) {
		log_err("Could not dispatch the request to kernelspace.");
		return netlink_print_error(error);
	}

	error = nl_recvmsgs_default(sk);
	if (error < 0) {
		if (error_handler_called) {
			error_handler_called = false;
			return error;
		}
		log_err("Error receiving the kernel module's response.");
		return netlink_print_error(error);
	}

	return 0;
}
Ejemplo n.º 6
0
int main()
{
  struct nl_sock * sk;
  int cbarg;

  // nl_debug = 4;

  // setup netlink socket
  sk = nl_socket_alloc();
  nl_socket_disable_seq_check(sk);	// disable sequence number check
  genl_connect(sk);

  int id = genl_ctrl_resolve(sk, DEMO_FAMILY_NAME);

  struct nl_msg * msg;


  // create a messgae
  msg = nlmsg_alloc();
  genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, id, 0,	// hdrlen
                        0,	// flags
                        DEMO_CMD,	// numeric command identifier
                        DEMO_VERSION	// interface version
                       );

  nla_put_string(msg, DEMO_ATTR1_STRING, "hola");
  nla_put_u16(msg, DEMO_ATTR2_UINT16, 0xf1);

  // send it
  nl_send_auto(sk, msg);

  // handle reply
  struct nl_cb * cb = NULL;
  cb = nl_cb_alloc(NL_CB_CUSTOM);

  //nl_cb_set_all(cb, NL_CB_DEBUG, NULL, NULL);
  nl_cb_set_all(cb, NL_CB_CUSTOM, cb_handler, &cbarg);
  nl_cb_err(cb, NL_CB_DEBUG, NULL, NULL);

  int nrecv = nl_recvmsgs_report(sk, cb);

  printf("cbarg %d nrecv %d\n", cbarg, nrecv);

  // cleanup
  nlmsg_free(msg);
  nl_close(sk);
  nl_socket_free(sk);

  return 0;
}
Ejemplo n.º 7
0
Archivo: main.c Proyecto: nbastin/ivs
static void
show(void)
{
    struct nl_msg *msg = nlmsg_alloc();
    struct ovs_header *hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ,
                                         ovs_datapath_family, sizeof(*hdr),
                                         NLM_F_DUMP, OVS_DP_CMD_GET, OVS_DATAPATH_VERSION);
    hdr->dp_ifindex = 0;
    if (nl_send_auto(sk, msg) < 0) {
        abort();
    }

    nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, show_datapath__, NULL);
    nl_recvmsgs_default(sk);
}
int mod_subflow(
  struct mptcp_state *mp_state,
  uint32_t src_ip,
  uint32_t src_locid,
  struct connection *conn,
  int command)
{
    int rc = 0;
    struct nl_sock_data *nlsd;
    struct nl_sock *sock;
    struct nl_msg *msg;
    msg = nlmsg_alloc();

    nlsd = (struct nl_sock_data *)mptcp_state_get_comms(mp_state);

    sock = nlsd->sock;

    genlmsg_put(msg,
        0,
        0,
        nlsd->family_id,
        CA_PM_GENL_HDRLEN,
        0,
        command,
        CA_PM_GENL_VERSION);

    //print_debug("Subflow: %s -> %s:%d\n", src->name, ip_to_str(htonl(conn->daddr)), conn->dport);

    rc = nla_put_u32 (msg, CONTEXT_MPTCP_TOKEN, conn->token);
    if(rc < 0) print_error("NLA PUT ERROR CONTEXT_MPTCP_TOKEN\n");

    rc = nla_put_u32 (msg, CONTEXT_SRC_ADDR, src_ip);
    if(rc < 0) print_error("NLA PUT ERROR CONTEXT_SRC_ADDR\n");

    rc = nla_put_u32 (msg, CONTEXT_DST_ADDR, conn->daddr);
    if(rc < 0) print_error("NLA PUT ERROR CONTEXT_DST_ADDR\n");

    rc = nla_put_u16 (msg, CONTEXT_DST_PORT, conn->dport);
    if(rc < 0) print_error("NLA PUT ERROR CONTEXT_DST_PORT\n");

    rc = nla_put_u16 (msg, CONTEXT_LOC_ID, src_locid);
    if(rc < 0) print_error("NLA PUT ERROR CONTEXT_LOC_ID\n");

    nl_send_auto(sock, msg);
    nlmsg_free(msg);

    return 0;
}
Ejemplo n.º 9
0
Archivo: main.c Proyecto: nbastin/ivs
static void
show_vports__(int dp_ifindex)
{
    struct nl_msg *msg = nlmsg_alloc();
    struct ovs_header *hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ,
                                         ovs_vport_family, sizeof(*hdr),
                                         NLM_F_DUMP, OVS_VPORT_CMD_GET, OVS_VPORT_VERSION);
    hdr->dp_ifindex = dp_ifindex;
    if (nl_send_auto(sk2, msg) < 0) {
        abort();
    }

    nl_socket_modify_cb(sk2, NL_CB_VALID, NL_CB_CUSTOM, show_vport__, NULL);
    nl_recvmsgs_default(sk2);

    nlmsg_free(msg);
}
Ejemplo n.º 10
0
int nf10_reg_wr(uint32_t addr, uint32_t val)
{
    struct nl_msg   *msg;
    int             err;

    err = driver_connect();
    if(err)
        return err;

    msg = nlmsg_alloc();
    if(msg == NULL) {
        driver_disconnect();
        return -NLE_NOMEM;
    }

    /* genlmsg_put will fill in the fields of the nlmsghdr and the genlmsghdr. */
    genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nf10_genl_family, 0, 0,
            NF10_GENL_C_REG_WR, NF10_GENL_FAMILY_VERSION);

    nla_put_u32(msg, NF10_GENL_A_ADDR32, addr);
    nla_put_u32(msg, NF10_GENL_A_REGVAL32, val);

    /* nl_send_auto will automatically fill in the PID and the sequence number,
     * and also add an NLM_F_REQUEST flag. It will also add an NLM_F_ACK
     * flag unless the netlink socket has the NL_NO_AUTO_ACK flag set. */
    err = nl_send_auto(nf10_genl_sock, msg);
    if(err < 0) {
        nlmsg_free(msg);
        driver_disconnect();
        return err;
    }

    nlmsg_free(msg);

    nl_socket_modify_cb(nf10_genl_sock, NL_CB_ACK, NL_CB_CUSTOM, nf10_reg_wr_recv_ack_cb, NULL);

    /* FIXME: this function will return even if there's no ACK in the buffer. I.E. it doesn't
     * seem to wait for the ACK to be received... Ideally we'd have the behavior that getting an
     * ACK tells us everything is OK, otherwise we time out on waiting for an ACK and tell this
     * to the user. */
    err = nl_recvmsgs_default(nf10_genl_sock);

    driver_disconnect();

    return err;
}
Ejemplo n.º 11
0
int nlt_get_ifinfo(struct nl_sock *sk, struct nlt_ifinfo *ifinfo)
{

	struct nl_msg *msg = nlmsg_alloc();
	if (!msg)
		return -1;

	int flags = NLM_F_DUMP;

	int family_id = genl_ctrl_resolve(sk, "nl80211");

	genlmsg_put(msg, 0, NL_AUTO_SEQ, family_id, 0, flags, NL80211_CMD_GET_INTERFACE, 0);

//      NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, 0);
	nl_send_auto(sk, msg);




	int err;

	struct nl_cb *nl_cb = nl_cb_alloc(NL_CB_CUSTOM);
	nl_cb_set(nl_cb, NL_CB_VALID, NL_CB_CUSTOM, get_ifinfo_cb, ifinfo);
	nl_cb_err(nl_cb, NL_CB_CUSTOM, error_handler, &err);


	int nlr;
//      do {
	nlr = nl_recvmsgs(sk, nl_cb);
//              printf("round %d\n",nlr);


//      }while(1);;

	//int nlr = nl_recvmsgs_default(sk);
//	cw_log(LOG_ERR, "iGet if index: Make if %d - %s", nlr, nl_geterror(nlr));




//      nla_put_failure:
	nlmsg_free(msg);
	return 0;


}
Ejemplo n.º 12
0
Archivo: main.c Proyecto: nbastin/ivs
/* Replacement for nl_send_sync that returns the real error code */
static int
transact(struct nl_sock *sk, struct nl_msg *msg)
{
    if (nl_send_auto(sk, msg) < 0) {
        return -EBADE;
    }
    nlmsg_free(msg);

    struct nlmsghdr *reply;
    struct sockaddr_nl nla;
    if (nl_recv(sk, &nla, (unsigned char **)&reply, NULL) < 0) {
        return -EBADE;
    }

    assert(reply->nlmsg_type == NLMSG_ERROR);
    int err = ((struct nlmsgerr *)nlmsg_data(reply))->error;
    free(reply);

    return err;
}
Ejemplo n.º 13
0
/*
 * See the OF_PORT_DEST_USE_TABLE comment in ind_ovs_translate_actions.
 * This is mostly the same as ind_ovs_handle_packet_miss but does
 * not reuse the original message for the execute command since
 * the incoming message has a userdata attribute.
 * It also doesn't install a kflow or update flow stats.
 */
static void
ind_ovs_handle_packet_table(struct ind_ovs_upcall_thread *thread,
                            struct ind_ovs_port *port,
                            struct nl_msg *msg, struct nlattr **attrs)
{
    struct nlattr *key = attrs[OVS_PACKET_ATTR_KEY];
    struct nlattr *packet = attrs[OVS_PACKET_ATTR_PACKET];
    assert(key && packet);

    struct ind_ovs_parsed_key pkey;
    ind_ovs_parse_key(key, &pkey);

    /* Lookup the flow in the userspace flowtable. */
    struct ind_ovs_flow *flow;
    if (ind_ovs_lookup_flow(&pkey, &flow) != 0) {
        ind_ovs_upcall_request_pktin(pkey.in_port, port, packet, key, OF_PACKET_IN_REASON_NO_MATCH);
        return;
    }

    /* Send OVS_PACKET_CMD_EXECUTE. */
    /* TODO ensure the message is large enough */
    struct nl_msg *reply = ind_ovs_create_nlmsg(ovs_packet_family,
                                                OVS_PACKET_CMD_EXECUTE);

    ind_ovs_translate_actions(&pkey, flow->of_list_action,
                              reply, OVS_PACKET_ATTR_ACTIONS);

    nla_put(reply, OVS_PACKET_ATTR_KEY, nla_len(key), nla_data(key));
    nla_put(reply, OVS_PACKET_ATTR_PACKET, nla_len(packet), nla_data(packet));

    struct nlattr *actions = nlmsg_find_attr(nlmsg_hdr(reply),
        sizeof(struct genlmsghdr) + sizeof(struct ovs_header),
        OVS_PACKET_ATTR_ACTIONS);

    /* Don't send the packet back out if it would be dropped. */
    if (nla_len(actions) > 0) {
        nl_send_auto(port->notify_socket, reply);
    }

    ind_ovs_nlmsg_freelist_free(reply);
}
Ejemplo n.º 14
0
int nf10_reg_rd(uint32_t addr, uint32_t *val_ptr)
{
    struct nl_msg   *msg;
    int             err;

    err = driver_connect();
    if(err)
        return err;

    msg = nlmsg_alloc();
    if(msg == NULL) {
        driver_disconnect();
        return -NLE_NOMEM;
    }

    /* genlmsg_put will fill in the fields of the nlmsghdr and the genlmsghdr. */
    genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nf10_genl_family, 0, 0,
            NF10_GENL_C_REG_RD, NF10_GENL_FAMILY_VERSION);

    nla_put_u32(msg, NF10_GENL_A_ADDR32, addr);

    /* nl_send_auto will automatically fill in the PID and the sequence number,
     * and also add an NLM_F_REQUEST flag. It will also add an NLM_F_ACK
     * flag unless the netlink socket has the NL_NO_AUTO_ACK flag set. */
    err = nl_send_auto(nf10_genl_sock, msg);
    if(err < 0) {
        nlmsg_free(msg);
        driver_disconnect();
        return err;
    }

    nlmsg_free(msg);

    nl_socket_modify_cb(nf10_genl_sock, NL_CB_VALID, NL_CB_CUSTOM, nf10_reg_rd_recv_msg_cb, (void*)val_ptr);

    err = nl_recvmsgs_default(nf10_genl_sock);

    driver_disconnect();

    return err;
}
Ejemplo n.º 15
0
Archivo: nl.c Proyecto: rinrinne/libnl
/**
 * 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;
}
Ejemplo n.º 16
0
Archivo: main.c Proyecto: Juankc125/ivs
static void
dump_flows(const char *datapath)
{
    unsigned int dp_ifindex = if_nametoindex(datapath);
    if (dp_ifindex == 0) {
        fprintf(stderr, "Failed: no such datapath '%s'\n", datapath);
        exit(1);
    }

    struct nl_msg *msg = nlmsg_alloc();
    struct ovs_header *hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ,
                                         ovs_flow_family, sizeof(*hdr),
                                         NLM_F_DUMP, OVS_FLOW_CMD_GET, OVS_FLOW_VERSION);
    hdr->dp_ifindex = dp_ifindex;
    if (nl_send_auto(sk2, msg) < 0) {
        abort();
    }

    nl_socket_modify_cb(sk2, NL_CB_VALID, NL_CB_CUSTOM, show_kflow__, NULL);
    nl_recvmsgs_default(sk2);

    nlmsg_free(msg);
}
Ejemplo n.º 17
0
struct match_msg *match_nl_alloc_msg(uint8_t type, uint32_t pid,
				   int flags, int size, int family)
{
	struct match_msg *msg;
	static uint32_t seq = 0;

	msg = (struct match_msg *) malloc(sizeof(struct match_msg));
	if (!msg)
		return NULL;

	msg->nlbuf = nlmsg_alloc();

	msg->msg = genlmsg_put(msg->nlbuf, 0, seq, family, (int)size, flags,
			       type, NET_MAT_GENL_VERSION);

	msg->seq = seq++;

	if (pid) {
		struct nl_msg *nl_msg = msg->nlbuf;
		struct sockaddr_nl nladdr = {
			.nl_family = AF_NETLINK,
			.nl_pid = pid,
			.nl_groups = 0,
		};

		nlmsg_set_dst(nl_msg, &nladdr);
	}
	return msg;
}

struct match_msg *match_nl_get_msg(struct nl_sock *nsd, uint8_t cmd, uint32_t pid,
				 unsigned int ifindex, int family)
{
	struct match_msg *msg;
	sigset_t bs;
	int err;

	msg = match_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
	if (!msg) {
		MAT_LOG(ERR, "Error: Allocation failure\n");
		return NULL;
	}

	nla_put_u32(msg->nlbuf,
		    NET_MAT_IDENTIFIER_TYPE,
		    NET_MAT_IDENTIFIER_IFINDEX);
	nla_put_u32(msg->nlbuf, NET_MAT_IDENTIFIER, ifindex);

	nl_send_auto(nsd, msg->nlbuf);
	match_nl_free_msg(msg);

	sigemptyset(&bs);
	sigaddset(&bs, SIGINT);
	sigprocmask(SIG_UNBLOCK, &bs, NULL);

	msg = match_nl_recv_msg(nsd, &err);
	sigprocmask(SIG_BLOCK, &bs, NULL);
	return msg;
}

static int match_nl_get_port(struct nl_sock *nsd, uint32_t pid,
			unsigned int ifindex, int family, uint8_t cmd,
			struct net_mat_port *ports,
			uint32_t *port_id, uint32_t *glort)
{
	struct net_mat_port *port_query = NULL;
	struct match_msg *msg;
	sigset_t bs;
	int err;

	msg = match_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
	if (!msg) {
		MAT_LOG(ERR, "Error: Allocation failure\n");
		return -ENOMEM;
	}

	if (nla_put_u32(msg->nlbuf,
			NET_MAT_IDENTIFIER_TYPE,
			NET_MAT_IDENTIFIER_IFINDEX) ||
	    nla_put_u32(msg->nlbuf, NET_MAT_IDENTIFIER, ifindex)) {
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}

	err = match_put_ports(msg->nlbuf, ports);
	if (err) {
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}

	nl_send_auto(nsd, msg->nlbuf);
	match_nl_free_msg(msg);

	sigemptyset(&bs);
	sigaddset(&bs, SIGINT);
	sigprocmask(SIG_UNBLOCK, &bs, NULL);

	msg = match_nl_recv_msg(nsd, &err);

	if (msg) {
		struct nlmsghdr *nlh = msg->msg;
		struct nlattr *tb[NET_MAT_MAX+1];
		int err;

		err = genlmsg_parse(nlh, 0, tb,
				    NET_MAT_MAX, match_get_tables_policy);
		if (err < 0) {
			MAT_LOG(ERR, "Warning: unable to parse pci to lport msg\n");
			match_nl_free_msg(msg);
			return -EINVAL;
		}

		if (match_nl_table_cmd_to_type(stdout, true, NET_MAT_PORTS, tb)) {
			match_nl_free_msg(msg);
			return -EINVAL;
		}

		if (tb[NET_MAT_PORTS]) {
			err = match_get_ports(stdout, verbose,
					     tb[NET_MAT_PORTS], &port_query);
			if (err) {
				match_nl_free_msg(msg);
				return -EINVAL;
			}
		}

		if (!port_query) {
			match_nl_free_msg(msg);
			return -EINVAL;
		}

		if (cmd == NET_MAT_PORT_CMD_GET_LPORT)
			*port_id = port_query[0].port_id;
		else if (cmd == NET_MAT_PORT_CMD_GET_PHYS_PORT)
			*port_id = port_query[0].port_phys_id;

		if (glort)
			*glort = port_query[0].glort;
	}

	match_nl_free_msg(msg);
	free(port_query);
	return 0;
}

int match_nl_pci_lport(struct nl_sock *nsd, uint32_t pid,
		      unsigned int ifindex, int family,
		      uint8_t bus, uint8_t device, uint8_t function,
		      uint32_t *lport, uint32_t *glort)
{
	struct net_mat_port port = {.pci = {0},
	                            .port_id = NET_MAT_PORT_ID_UNSPEC,
				    .mac_addr = 0, .port_phys_id = 0};
	struct net_mat_port ports[2] = {{0}, {0}};
	int err;

	ports[0] = ports[1] = port;

	ports[0].pci.bus = bus;
	ports[0].pci.device = device;
	ports[0].pci.function = function;

	err = match_nl_get_port(nsd, pid, ifindex, family,
			NET_MAT_PORT_CMD_GET_LPORT, ports, lport, glort);

	return err;
}

int match_nl_mac_lport(struct nl_sock *nsd, uint32_t pid,
		     unsigned int ifindex, int family,
		     uint64_t mac, uint32_t *lport,
		     uint32_t *glort)
{
	struct net_mat_port port = {.pci = {0},
	                            .port_id = NET_MAT_PORT_ID_UNSPEC,
				    .mac_addr = 0, .port_phys_id = 0};
	struct net_mat_port ports[2] = {{0}, {0}};
	int err;

	ports[0] = ports[1] = port;

	ports[0].mac_addr = mac;

	err = match_nl_get_port(nsd, pid, ifindex, family,
			NET_MAT_PORT_CMD_GET_LPORT, ports, lport, glort);

	return err;
}

int match_nl_lport_to_phys_port(struct nl_sock *nsd, uint32_t pid,
				unsigned int ifindex, int family,
				uint32_t lport, uint32_t *phys_port,
				uint32_t *glort)
{
	struct net_mat_port port = {.pci = {0},
	                            .port_id = NET_MAT_PORT_ID_UNSPEC,
				    .mac_addr = 0, .port_phys_id = 0};
	struct net_mat_port ports[2] = {{0}, {0}};
	int err;

	ports[0] = ports[1] = port;

	ports[0].port_id = lport;

	err = match_nl_get_port(nsd, pid, ifindex, family,
			NET_MAT_PORT_CMD_GET_PHYS_PORT,
			ports, phys_port, glort);

	return err;
}
Ejemplo n.º 18
0
/**
 * 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_complete_msg(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;
}

void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
{
	nl_complete_msg(sk, msg);
}

/**
 * Automatically complete and send a netlink message
 * @arg sk		Netlink socket.
 * @arg msg		Netlink message to be sent.
 *
 * This function takes a netlink message and passes it on to
 * nl_auto_complete() for completion.
 *
 * 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(struct nl_sock *sk, struct nl_msg *msg)
{
	struct nl_cb *cb = sk->s_cb;

	nl_complete_msg(sk, msg);

	if (cb->cb_send_ow)
		return cb->cb_send_ow(sk, msg);
	else
		return nl_send(sk, msg);
}

int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
{
	return nl_send_auto(sk, msg);
}

/**
 * Send netlink message and wait for response (sync request-response)
 * @arg sk		Netlink socket
 * @arg msg		Netlink message to be sent
 *
 * This function takes a netlink message and sends it using nl_send_auto().
 * It will then wait for the response (ACK or error message) to be
 * received. Threfore this function will block until the operation has
 * been completed.
 *
 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
 *       this function to return immediately after sending. In this case,
 *       it is the responsibility of the caller to handle any eventual
 *       error messages returned.
 *
 * @see nl_send_auto().
 *
 * @return 0 on success or a negative error code.
 */
int nl_send_sync(struct nl_sock *sk, struct nl_msg *msg)
{
	int err;

	err = nl_send_auto(sk, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return wait_for_ack(sk);
}
Ejemplo n.º 19
0
int match_nl_set_del_rules(struct nl_sock *nsd, uint32_t pid,
		      unsigned int ifindex, int family,
		      struct net_mat_rule *rule, uint8_t cmd)
{
	struct nlattr *tb[NET_MAT_MAX+1];
	struct match_msg *msg;
	struct nlmsghdr *nlh;
	struct nlattr *rules;
	sigset_t bs;
	int err = 0;

	pp_rule(stdout, true, rule);

	msg = match_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
	if (!msg) {
		MAT_LOG(ERR, "Error: Allocation failure\n");
		return -ENOMSG;
	}

	if (nla_put_u32(msg->nlbuf,
			NET_MAT_IDENTIFIER_TYPE,
			NET_MAT_IDENTIFIER_IFINDEX) ||
	    nla_put_u32(msg->nlbuf, NET_MAT_IDENTIFIER, ifindex)) {
		MAT_LOG(ERR, "Error: Identifier put failed\n");
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}

	err = match_put_rule_error(msg->nlbuf, NET_MAT_RULES_ERROR_CONT_LOG);
	if (err) {
		match_nl_free_msg(msg);
		return err;
	}

	rules = nla_nest_start(msg->nlbuf, NET_MAT_RULES);
	if (!rules) {
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}
	match_put_rule(msg->nlbuf, rule);
	nla_nest_end(msg->nlbuf, rules);

	nl_send_auto(nsd, msg->nlbuf);
	match_nl_free_msg(msg);

	/* message sent handle recv */
	sigemptyset(&bs);
	sigaddset(&bs, SIGINT);
	sigprocmask(SIG_UNBLOCK, &bs, NULL);

	msg = match_nl_recv_msg(nsd, &err);
	sigprocmask(SIG_BLOCK, &bs, NULL);

	if (!msg)
		return -EINVAL;

	nlh = msg->msg;
	err = genlmsg_parse(nlh, 0, tb, NET_MAT_MAX, match_get_tables_policy);
	if (err < 0) {
		MAT_LOG(ERR, "Warning: unable to parse set rules msg\n");
		match_nl_free_msg(msg);
		return err;
	}

	err = match_nl_table_cmd_to_type(stdout, true, 0, tb);
	if (err) {
		match_nl_free_msg(msg);
		return err;
	}

	if (tb[NET_MAT_RULES]) {
		MAT_LOG(ERR, "Failed to set:\n");
		match_get_rules(stdout, verbose, tb[NET_MAT_RULES], NULL);
		match_nl_free_msg(msg);
		return -EINVAL;
	}
	match_nl_free_msg(msg);
	return 0;
}
Ejemplo n.º 20
0
int match_nl_set_port(struct nl_sock *nsd, uint32_t pid,
				unsigned int ifindex, int family,
				struct net_mat_port *port)
{
	uint8_t cmd = NET_MAT_PORT_CMD_SET_PORTS;
	struct nlattr *tb[NET_MAT_MAX+1];
	struct nlattr *nest, *nest1;
	struct nlmsghdr *nlh;
	struct match_msg *msg;
	sigset_t bs;
	int err = 0;

	msg = match_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
	if (!msg) {
		MAT_LOG(ERR, "Error: Allocation failure\n");
		return -ENOMSG;
	}

	if (nla_put_u32(msg->nlbuf,
			NET_MAT_IDENTIFIER_TYPE,
			NET_MAT_IDENTIFIER_IFINDEX) ||
	    nla_put_u32(msg->nlbuf, NET_MAT_IDENTIFIER, ifindex)) {
		MAT_LOG(ERR, "Error: Identifier put failed\n");
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}

	nest = nla_nest_start(msg->nlbuf, NET_MAT_PORTS);
	if (!nest) {
		match_nl_free_msg(msg);
		return -EMSGSIZE;
	}
	nest1 = nla_nest_start(msg->nlbuf, NET_MAT_PORTS);
	match_put_port(msg->nlbuf, port);
	nla_nest_end(msg->nlbuf, nest1);
	nla_nest_end(msg->nlbuf, nest);
	nl_send_auto(nsd, msg->nlbuf);
	match_nl_free_msg(msg);

	sigemptyset(&bs);
	sigaddset(&bs, SIGINT);
	sigprocmask(SIG_UNBLOCK, &bs, NULL);

	msg = match_nl_recv_msg(nsd, &err);
	sigprocmask(SIG_BLOCK, &bs, NULL);

	if (!msg)
		return -EINVAL;

	nlh = msg->msg;
	err = genlmsg_parse(nlh, 0, tb, NET_MAT_MAX, match_get_tables_policy);
	if (err < 0) {
		MAT_LOG(ERR, "Warning: unable to parse set port msg\n");
		match_nl_free_msg(msg);
		return err;
	}

	err = match_nl_table_cmd_to_type(stdout, true, 0, tb);
	if (err) {
		match_nl_free_msg(msg);
		return err;
	}

	if (tb[NET_MAT_PORTS]) {
		MAT_LOG(ERR, "Failed to set:\n");
		match_get_ports(stdout, verbose, tb[NET_MAT_PORTS], NULL);
		match_nl_free_msg(msg);
		return -EINVAL;
	}
	match_nl_free_msg(msg);
	return 0;
}
Ejemplo n.º 21
0
struct net_mat_port *match_nl_get_ports(struct nl_sock *nsd, uint32_t pid,
                      unsigned int ifindex, int family, uint32_t min, uint32_t max)
{
	uint8_t cmd = NET_MAT_PORT_CMD_GET_PORTS;
	struct nlattr *tb[NET_MAT_MAX+1];
	struct net_mat_port *port = NULL;
	struct match_msg *msg;
	struct nlmsghdr *nlh;
	struct nlattr *ports;
	sigset_t bs;
	int err = 0;

	msg = match_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
	if (!msg) {
		MAT_LOG(ERR, "Error: Allocation failure\n");
		return NULL;
	}

	if (nla_put_u32(msg->nlbuf,
                        NET_MAT_IDENTIFIER_TYPE,
                        NET_MAT_IDENTIFIER_IFINDEX) ||
		nla_put_u32(msg->nlbuf, NET_MAT_IDENTIFIER, ifindex)) {
		MAT_LOG(ERR, "Error: Identifier put failed\n");
		goto out;
	}
	err = match_put_rule_error(msg->nlbuf, NET_MAT_RULES_ERROR_CONT_LOG);
	if (err)
		goto out;

	ports = nla_nest_start(msg->nlbuf, NET_MAT_PORTS);
	if (!ports) {
		MAT_LOG(ERR, "Error: get_port attributes failed\n");
		goto out;
	}
	if (min) {
		err = nla_put_u32(msg->nlbuf, NET_MAT_PORT_MIN_INDEX,
                                min);
		if (err)
			goto out;
	}
	if (max) {
		err = nla_put_u32(msg->nlbuf, NET_MAT_PORT_MAX_INDEX,
                                max);
		if (err)
			goto out;
	}
	nla_nest_end(msg->nlbuf, ports);
	nl_send_auto(nsd, msg->nlbuf);
	match_nl_free_msg(msg);

	/* message sent handle recv */
	sigemptyset(&bs);
	sigaddset(&bs, SIGINT);
	sigprocmask(SIG_UNBLOCK, &bs, NULL);

	msg = match_nl_recv_msg(nsd, &err);
	sigprocmask(SIG_BLOCK, &bs, NULL);
	if (msg) {
		nlh = msg->msg;
		err = genlmsg_parse(nlh, 0, tb, NET_MAT_MAX, match_get_tables_policy);
		if (err < 0) {
			MAT_LOG(ERR, "Warning: unable to parse get rules msg\n");
			goto out;
		}

		if (match_nl_table_cmd_to_type(stdout, true,
                                              NET_MAT_PORTS, tb))
                        goto out;

		if (tb[NET_MAT_PORTS]) {
			err = match_get_ports(stdout, verbose, tb[NET_MAT_PORTS], &port);
			if (err)
				goto out;
		}
	}
	match_nl_free_msg(msg);
	return port;
out:
	match_nl_free_msg(msg);
	return NULL;
}
Ejemplo n.º 22
0
Archivo: nl.c Proyecto: rinrinne/libnl
/**
 * @deprecated Please use nl_send_auto()
 */
int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
{
    return nl_send_auto(sk, msg);
}
Ejemplo n.º 23
0
Archivo: nl.c Proyecto: rinrinne/libnl
/**
 * Transmit Netlink message (taking IO vector)
 * @arg sk		Netlink socket (required)
 * @arg msg		Netlink message to be sent (required)
 * @arg iov		IO vector to be sent (required)
 * @arg iovlen		Number of struct iovec to be sent (required)
 *
 * This function is identical to nl_send() except that instead of taking a
 * `struct nl_msg` object it takes an IO vector. Please see the description
 * of `nl_send()`.
 *
 * @callback This function triggers the `NL_CB_MSG_OUT` callback.
 *
 * @see nl_send()
 *
 * @return Number of bytes sent on success or a negative error code.
 *
 * @lowlevel
 */
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);
}

/**
 * Transmit Netlink message
 * @arg sk		Netlink socket (required)
 * @arg msg		Netlink message (required)
 *
 * Transmits the Netlink message `msg` over the Netlink socket using the
 * `sendmsg()` system call. This function is based on `nl_send_iovec()` but
 * takes care of initializing a `struct iovec` based on the `msg` object.
 *
 * The message is addressed to the peer as specified in the socket by either
 * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function.
 * The peer address can be overwritten by specifying an address in the `msg`
 * object using nlmsg_set_dst().
 *
 * If present in the `msg`, credentials set by the nlmsg_set_creds() function
 * are added to the control buffer of the message.
 *
 * @par Overwriting Capability:
 * Calls to this function can be overwritten by providing an alternative using
 * the nl_cb_overwrite_send() function.
 *
 * @callback This function triggers the `NL_CB_MSG_OUT` callback.
 *
 * @attention
 * Unlike `nl_send_auto()`, this function does *not* finalize the message in
 * terms of automatically adding needed flags or filling out port numbers.
 *
 * @see nl_send_auto()
 * @see nl_send_iovec()
 * @see nl_socket_set_peer_port()
 * @see nl_socket_set_peer_groups()
 * @see nlmsg_set_dst()
 * @see nlmsg_set_creds()
 * @see nl_cb_overwrite_send()
 *
 * @return Number of bytes sent on success or a negative error code.
*/
int nl_send(struct nl_sock *sk, struct nl_msg *msg)
{
    struct nl_cb *cb = sk->s_cb;

    if (cb->cb_send_ow)
        return cb->cb_send_ow(sk, msg);
    else {
        struct iovec iov = {
            .iov_base = (void *) nlmsg_hdr(msg),
            .iov_len = nlmsg_hdr(msg)->nlmsg_len,
        };

        return nl_send_iovec(sk, msg, &iov, 1);
    }
}

/**
 * Finalize Netlink message
 * @arg sk		Netlink socket (required)
 * @arg msg		Netlink message (required)
 *
 * This function finalizes a Netlink message by completing the message with
 * desirable flags and values depending on the socket configuration.
 *
 *  - If not yet filled out, the source address of the message (`nlmsg_pid`)
 *    will be set to the local port number of the socket.
 *  - If not yet specified, the next available sequence number is assigned
 *    to the message (`nlmsg_seq`).
 *  - If not yet specified, the protocol field of the message will be set to
 *    the protocol field of the socket.
 *  - The `NLM_F_REQUEST` Netlink message flag will be set.
 *  - The `NLM_F_ACK` flag will be set if Auto-ACK mode is enabled on the
 *    socket.
 */
void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg)
{
    struct nlmsghdr *nlh;

    nlh = nlmsg_hdr(msg);
    if (nlh->nlmsg_pid == NL_AUTO_PORT)
        nlh->nlmsg_pid = nl_socket_get_local_port(sk);

    if (nlh->nlmsg_seq == NL_AUTO_SEQ)
        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;
}

/**
 * Finalize and transmit Netlink message
 * @arg sk		Netlink socket (required)
 * @arg msg		Netlink message (required)
 *
 * Finalizes the message by passing it to `nl_complete_msg()` and transmits it
 * by passing it to `nl_send()`.
 *
 * @callback This function triggers the `NL_CB_MSG_OUT` callback.
 *
 * @see nl_complete_msg()
 * @see nl_send()
 *
 * @return Number of bytes sent or a negative error code.
 */
int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg)
{
    nl_complete_msg(sk, msg);

    return nl_send(sk, msg);
}

/**
 * Finalize and transmit Netlink message and wait for ACK or error message
 * @arg sk		Netlink socket (required)
 * @arg msg		Netlink message (required)
 *
 * Passes the `msg` to `nl_send_auto()` to finalize and transmit it. Frees the
 * message and waits (sleeps) for the ACK or error message to be received.
 *
 * @attention
 * Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this function
 * to return immediately after transmitting the message. However, the peer may
 * still be returning an error message in response to the request. It is the
 * responsibility of the caller to handle such messages.
 *
 * @callback This function triggers the `NL_CB_MSG_OUT` callback.
 *
 * @attention
 * This function frees the `msg` object after transmitting it by calling
 * `nlmsg_free()`.
 *
 * @see nl_send_auto().
 * @see nl_wait_for_ack()
 *
 * @return 0 on success or a negative error code.
 */
int nl_send_sync(struct nl_sock *sk, struct nl_msg *msg)
{
    int err;

    err = nl_send_auto(sk, msg);
    nlmsg_free(msg);
    if (err < 0)
        return err;

    return wait_for_ack(sk);
}