Ejemplo n.º 1
0
static int teamd_ctl_method_port_config_dump(struct teamd_context *ctx,
					     const struct teamd_ctl_method_ops *ops,
					     void *ops_priv)
{
	const char *port_devname;
	uint32_t ifindex;
	char *cfg;
	int err;

	err = ops->get_args(ops_priv, "s", &port_devname);
	if (err)
		return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments.");
	teamd_log_dbgx(ctx, 2, "port_devname \"%s\"", port_devname);

	ifindex = team_ifname2ifindex(ctx->th, port_devname);
	if (!ifindex) {
		teamd_log_err("Device \"%s\" does not exist.", port_devname);
		return ops->reply_err(ops_priv, "NoSuchDev", "No such device.");
	}
	err = teamd_config_port_dump(ctx, port_devname, &cfg);
	if (err) {
		teamd_log_err("Failed to dump config for port \"%s\".",
			      port_devname);
		return ops->reply_err(ops_priv, "ConfigDumpFail", "Failed to dump config.");
	}
	err = ops->reply_succ(ops_priv, cfg);
	free(cfg);
	return err;
}
Ejemplo n.º 2
0
static int lw_ethtool_port_added(struct teamd_context *ctx,
				 struct teamd_port *tdport,
				 void *priv, void *creator_priv)
{
	int err;

	err = lw_ethtool_load_options(ctx, tdport, priv);
	if (err) {
		teamd_log_err("Failed to load options.");
		return err;
	}
	err = teamd_loop_callback_timer_add(ctx, LW_ETHTOOL_DELAY_CB_NAME,
					    priv, lw_ethtool_callback_delay);
	if (err) {
		teamd_log_err("Failed add delay callback timer");
		return err;
	}
	err = teamd_event_watch_register(ctx, &lw_ethtool_port_watch_ops, priv);
	if (err) {
		teamd_log_err("Failed to register event watch.");
		goto delay_callback_del;
	}
	return 0;

delay_callback_del:
	teamd_loop_callback_del(ctx, LW_ETHTOOL_DELAY_CB_NAME, priv);
	return err;
}
Ejemplo n.º 3
0
static int lw_ethtool_load_options(struct teamd_context *ctx,
				   struct teamd_port *tdport,
				   struct lw_ethtool_port_priv *ethtool_ppriv)
{
	struct teamd_config_path_cookie *cpcookie = ethtool_ppriv->common.cpcookie;
	int err;
	int tmp;

	err = teamd_config_int_get(ctx, &tmp, "@.queue_id", cpcookie);
	if (!err) {
		if (tmp < 0) {
			teamd_log_err("\"delay_up\" must not be negative number.");
			return -EINVAL;
		}
		teamd_log_dbg("delay_up \"%d\".", tmp);
		ms_to_timespec(&ethtool_ppriv->delay_up, tmp);
	}
	err = teamd_config_int_get(ctx, &tmp, "@.delay_down", cpcookie);
	if (!err) {
		if (tmp < 0) {
			teamd_log_err("\"delay_down\" must not be negative number.");
			return -EINVAL;
		}
		teamd_log_dbg("delay_down \"%d\".", tmp);
		ms_to_timespec(&ethtool_ppriv->delay_down, tmp);
	}
	return 0;
}
Ejemplo n.º 4
0
static int teamd_ctl_method_state_item_value_set(struct teamd_context *ctx,
						 const struct teamd_ctl_method_ops *ops,
						 void *ops_priv)
{
	const char *item_path;
	const char *value;
	int err;

	err = ops->get_args(ops_priv, "ss", &item_path, &value);
	if (err)
		return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments.");
	teamd_log_dbgx(ctx, 2, "item_path \"%s\", value \"%s\"",
		       item_path, value);

	err = teamd_state_item_value_set(ctx, item_path, value);
	if (err == -ENOENT) {
		teamd_log_err("Failed to set state item \"%s\".", item_path);
		return ops->reply_err(ops_priv, "PathDoesNotExist", "Item path does not exist.");
	} else if (err == -EOPNOTSUPP) {
		teamd_log_err("Failed to set state item \"%s\".", item_path);
		return ops->reply_err(ops_priv, "OpNotSupp", "Operation not supported.");
	} else if (err) {
		teamd_log_err("Failed to set state item \"%s\".", item_path);
		return ops->reply_err(ops_priv, "ItemValueSetFail", "Failed to set item value.");
	}
	return ops->reply_succ(ops_priv, NULL);
}
Ejemplo n.º 5
0
static int teamd_ctl_method_port_add(struct teamd_context *ctx,
				     const struct teamd_ctl_method_ops *ops,
				     void *ops_priv)
{
	const char *port_devname;
	int err;

	err = ops->get_args(ops_priv, "s", &port_devname);
	if (err)
		return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments.");
	teamd_log_dbgx(ctx, 2, "port_devname \"%s\"", port_devname);

	err = teamd_port_add_ifname(ctx, port_devname);
	if (err) {
		teamd_log_err("%s: Failed to add port (%s).", port_devname,
			      strerror(-err));
	}

	switch (err) {
	case -ENODEV:
		return ops->reply_err(ops_priv, "NoSuchDev", "No such device.");
	case 0:
		break;
	default:
		return ops->reply_err(ops_priv, "PortAddFail", "Failed to add port.");
	}
	return ops->reply_succ(ops_priv, NULL);
}
Ejemplo n.º 6
0
int teamd_config_load(struct teamd_context *ctx)
{
	json_error_t jerror;
	size_t jflags = JSON_REJECT_DUPLICATES;

	if (!ctx->config_text && !ctx->config_file) {
		ctx->config_text = strdup(TEAMD_IMPLICIT_CONFIG);
		if (!ctx->config_text)
			return -ENOMEM;
	}
	if (ctx->config_text) {
		if (ctx->config_file)
			teamd_log_warn("Command line config string is present, ignoring given config file.");
		ctx->config_json = json_loads(ctx->config_text, jflags,
					      &jerror);
	} else if (ctx->config_file) {
		ctx->config_json = json_load_file(ctx->config_file, jflags,
						  &jerror);
	}
	if (!ctx->config_json) {
		teamd_log_err("Failed to parse config: %s on line %d, column %d",
			      jerror.text, jerror.line, jerror.column);
		return -EIO;
	}

	return 0;
}
Ejemplo n.º 7
0
static int acc_conn_create(struct teamd_context *ctx, int sock)
{
	struct usock_acc_conn *acc_conn;
	int err;

	acc_conn = myzalloc(sizeof(*acc_conn));
	if (!acc_conn) {
		teamd_log_err("usock: No memory to allocate new connection structure.");
		return -ENOMEM;
	}
	acc_conn->sock = sock;
	err = teamd_loop_callback_fd_add(ctx, USOCK_ACC_CONN_CB_NAME, acc_conn,
					 callback_usock_acc_conn,
					 acc_conn->sock,
					 TEAMD_LOOP_FD_EVENT_READ);
	if (err)
		goto free_acc_conn;
	teamd_loop_callback_enable(ctx, USOCK_ACC_CONN_CB_NAME, acc_conn);
	list_add(&ctx->usock.acc_conn_list, &acc_conn->list);
	return 0;

free_acc_conn:
	free(acc_conn);
	return err;
}
Ejemplo n.º 8
0
static int teamd_usock_sock_open(struct teamd_context *ctx)
{
	struct sockaddr_un addr;
	int sock;
	int err;

	err = teamd_make_rundir();
	if (err)
		return err;

	sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
	if (sock == -1) {
		teamd_log_err("usock: Failed to create socket.");
		return -errno;
	}

	addr.sun_family = AF_UNIX;
	teamd_usock_get_sockpath(addr.sun_path, sizeof(addr.sun_path),
				 ctx->team_devname);

	teamd_log_dbg("usock: Using sockpath \"%s\"", addr.sun_path);
	err = unlink(addr.sun_path);
	if (err == -1 && errno != ENOENT) {
		teamd_log_err("usock: Failed to remove socket file.");
		err = -errno;
		goto close_sock;
	}

	err = bind(sock, (struct sockaddr *) &addr,
	           strlen(addr.sun_path) + sizeof(addr.sun_family));
	if (err == -1) {
		teamd_log_err("usock: Failed to bind socket.");
		err = -errno;
		goto close_sock;
	}
	listen(sock, USOCK_MAX_CLIENT_COUNT);

	ctx->usock.sock = sock;
	ctx->usock.addr = addr;
	return 0;

close_sock:
	close(sock);
	return err;
}
Ejemplo n.º 9
0
int teamd_sriov_init(struct teamd_context *ctx)
{
	int err;

	err = teamd_event_watch_register(ctx, &teamd_sriov_event_watch_ops,
					 NULL);
	if (err) {
		teamd_log_err("Failed to register event watch.");
		return err;
	}
	return 0;
}
Ejemplo n.º 10
0
static int handle_period_fd(int fd)
{
	ssize_t ret;
	uint64_t exp;

	ret = read(fd, &exp, sizeof(uint64_t));
	if (ret == -1) {
		if (errno == EINTR || errno == EAGAIN)
			return 0;
		teamd_log_err("read() failed.");
		return -errno;
	}
	if (ret != sizeof(uint64_t)) {
		teamd_log_err("read() returned unexpected number of bytes.");
		return -EINVAL;
	}
	if (exp > 1)
		teamd_log_warn("some periodic function calls missed (%" PRIu64 ")",
			       exp - 1);
	return 0;
}
Ejemplo n.º 11
0
static int lw_psr_load_options(struct teamd_context *ctx,
			       struct teamd_port *tdport,
			       struct lw_psr_port_priv *psr_ppriv)
{
	struct teamd_config_path_cookie *cpcookie = psr_ppriv->common.cpcookie;
	int err;
	int tmp;

	err = teamd_config_int_get(ctx, &tmp, "@.interval", cpcookie);
	if (err) {
		teamd_log_err("Failed to get \"interval\" link-watch option.");
		return -EINVAL;
	}
	teamd_log_dbg("interval \"%d\".", tmp);
	ms_to_timespec(&psr_ppriv->interval, tmp);

	err = teamd_config_int_get(ctx, &tmp, "@.init_wait", cpcookie);
	if (!err)
		ms_to_timespec(&psr_ppriv->init_wait, tmp);
	/* if init_wait is set to 0, use default_init_wait */
	if (err || !tmp)
		psr_ppriv->init_wait = lw_psr_default_init_wait;
	teamd_log_dbg("init_wait \"%d\".", timespec_to_ms(&psr_ppriv->init_wait));

	err = teamd_config_int_get(ctx, &tmp, "@.missed_max", cpcookie);
	if (err) {
		teamd_log_err("Failed to get \"missed_max\" link-watch option.");
		return -EINVAL;
	}
	if (tmp < 0) {
		teamd_log_err("\"missed_max\" must not be negative number.");
		return -EINVAL;
	}
	teamd_log_dbg("missed_max \"%d\".", tmp);
	psr_ppriv->missed_max = tmp;

	return 0;
}
Ejemplo n.º 12
0
static int __set_sockaddr(struct sockaddr *sa, socklen_t sa_len,
			  sa_family_t family, const char *hostname)
{
	struct addrinfo *result;
	struct addrinfo hints;
	int err;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = family;
	err = getaddrinfo(hostname, NULL, &hints, &result);
	if (err) {
		teamd_log_err("getaddrinfo failed: %s", gai_strerror(err));
		return -EINVAL;
	}
	if (sa_len != result->ai_addrlen) {
		/* This should not happen, so just to be safe */
		teamd_log_err("Wrong address length in result.");
		return -EINVAL;
	}
	memcpy(sa, result->ai_addr, sa_len);
	freeaddrinfo(result);
	return 0;
}
Ejemplo n.º 13
0
static int usock_op_get_args(void *ops_priv, const char *fmt, ...)
{
	va_list ap;
	struct usock_ops_priv *usock_ops_priv = ops_priv;
	char **pstr;
	char *str;
	char *rest = usock_ops_priv->rcv_msg_args;
	int err = 0;

	va_start(ap, fmt);
	while (*fmt) {
		switch (*fmt++) {
		case 's': /* string */
			pstr = va_arg(ap, char **);
			str = teamd_usock_msg_getline(&rest);
			if (!str) {
				teamd_log_err("Insufficient number of arguments in message.");
				err = -EINVAL;
				goto out;
			}
			err = __strdecode(str);
			if (err) {
				teamd_log_err("Corrupted argument in message.");
				goto out;
			}
			*pstr = str;
			break;
		default:
			teamd_log_err("Unknown argument type requested");
			err = -EINVAL;
			goto out;
		}
	}
out:
	va_end(ap);
	return err;
}
Ejemplo n.º 14
0
static int icmp6_sock_open(int *sock_p)
{
	int sock;
	struct icmp6_filter flt;
	int ret;
	int err;
	int val;

	sock = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
	if (sock == -1) {
		teamd_log_err("Failed to create ICMP6 socket.");
		return -errno;
	}

	ICMP6_FILTER_SETBLOCKALL(&flt);
	ret = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &flt, sizeof(flt));
	if (ret == -1) {
		teamd_log_err("Failed to setsockopt ICMP6_FILTER.");
		err = -errno;
		goto close_sock;
	}

	val = 255;
	ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
			 &val, sizeof(val));
	if (ret == -1) {
		teamd_log_err("Failed to setsockopt IPV6_MULTICAST_HOPS.");
		err = -errno;
		goto close_sock;
	}

	*sock_p = sock;
	return 0;
close_sock:
	close(sock);
	return err;
}
static int ab_hwaddr_policy_same_all_hwaddr_changed(struct teamd_context *ctx,
						    struct ab *ab)
{
	struct teamd_port *tdport;
	int err;

	teamd_for_each_tdport(tdport, ctx) {
		err = team_hwaddr_set(ctx->th, tdport->ifindex, ctx->hwaddr,
				      ctx->hwaddr_len);
		if (err) {
			teamd_log_err("%s: Failed to set port hardware address.",
				      tdport->ifname);
			return err;
		}
	}
Ejemplo n.º 16
0
static char *__str_sockaddr(struct sockaddr *sa, socklen_t sa_len,
			    sa_family_t family)
{
	static char buf[NI_MAXHOST];
	int err;

	sa->sa_family = family;
	err = getnameinfo(sa, sa_len, buf, sizeof(buf),
			  NULL, 0, NI_NUMERICHOST);
	if (err) {
		teamd_log_err("getnameinfo failed: %s", gai_strerror(err));
		return NULL;
	}
	return buf;
}
Ejemplo n.º 17
0
static int teamd_ctl_method_state_dump(struct teamd_context *ctx,
				       const struct teamd_ctl_method_ops *ops,
				       void *ops_priv)
{
	char *state;
	int err;

	err = teamd_state_dump(ctx, &state);
	if (err) {
		teamd_log_err("Failed to dump state.");
		return ops->reply_err(ops_priv, "StateDumpFail", "Failed to dump state.");
	}
	err = ops->reply_succ(ops_priv, state);
	free(state);
	return err;
}
Ejemplo n.º 18
0
static int teamd_ctl_method_config_dump_actual(struct teamd_context *ctx,
					       const struct teamd_ctl_method_ops *ops,
					       void *ops_priv)
{
	char *cfg;
	int err;

	err = teamd_config_actual_dump(ctx, &cfg);
	if (err) {
		teamd_log_err("Failed to dump actual config.");
		return ops->reply_err(ops_priv, "ConfigDumpActualFail", "Failed to dump actual config.");
	}
	err = ops->reply_succ(ops_priv, cfg);
	free(cfg);
	return err;
}
Ejemplo n.º 19
0
static int lw_ethtool_event_watch_port_changed(struct teamd_context *ctx,
					       struct teamd_port *tdport,
					       void *priv)
{
	struct lw_common_port_priv *common_ppriv = priv;
	struct lw_ethtool_port_priv *ethtool_ppriv = priv;
	bool link_up;
	struct timespec *delay;
	int err;

	if (common_ppriv->tdport != tdport ||
	    !team_is_port_changed(tdport->team_port))
		return 0;

	/*
	 * Link changed for sure, so if there is some delay in progress,
	 * cancel it before proceeding.
	 */
	teamd_loop_callback_disable(ctx, LW_ETHTOOL_DELAY_CB_NAME, priv);
	link_up = team_is_port_link_up(tdport->team_port);
	if (!teamd_link_watch_link_up_differs(common_ppriv, link_up))
		return 0;

	if (link_up) {
		if (timespec_is_zero(&ethtool_ppriv->delay_up))
			goto nodelay;
		delay = &ethtool_ppriv->delay_up;
	} else {
		if (timespec_is_zero(&ethtool_ppriv->delay_down))
			goto nodelay;
		delay = &ethtool_ppriv->delay_down;
	}

	err = teamd_loop_callback_timer_set(ctx, LW_ETHTOOL_DELAY_CB_NAME,
					    priv, NULL, delay);
	if (err) {
		teamd_log_err("Failed to set delay timer.");
		return err;
	}
	teamd_loop_callback_enable(ctx, LW_ETHTOOL_DELAY_CB_NAME, priv);
	return 0;

nodelay:
	return teamd_link_watch_check_link_up(ctx, tdport, common_ppriv,
					      link_up);
}
Ejemplo n.º 20
0
static int callback_usock_acc_conn(struct teamd_context *ctx, int events,
				   void *priv)
{
	struct usock_acc_conn *acc_conn = priv;
	char *msg = NULL; /* gcc needs this initialized */
	int err;

	err = teamd_usock_recv_msg(acc_conn->sock, &msg);
	if (err == -EPIPE || err == -ECONNRESET) {
		acc_conn_destroy(ctx, acc_conn);
		return 0;
	} else if (err) {
		teamd_log_err("usock: Failed to receive data from connection.");
		return err;
	}
	err = process_rcv_msg(ctx, acc_conn->sock, msg);
	free(msg);
	return err;
}
Ejemplo n.º 21
0
static int __get_port_curr_hwaddr(struct lw_psr_port_priv *psr_ppriv,
				  struct sockaddr_ll *addr, size_t expected_len)
{
	struct team_ifinfo *ifinfo = psr_ppriv->common.tdport->team_ifinfo;
	size_t port_hwaddr_len = team_get_ifinfo_hwaddr_len(ifinfo);
	char *port_hwaddr = team_get_ifinfo_hwaddr(ifinfo);
	int err;

	err = teamd_getsockname_hwaddr(psr_ppriv->sock, addr, expected_len);
	if (err)
		return err;
	if ((addr->sll_halen != port_hwaddr_len) ||
	    (expected_len && expected_len != port_hwaddr_len)) {
		teamd_log_err("Unexpected length of hw address.");
		return -ENOTSUP;
	}
	memcpy(addr->sll_addr, port_hwaddr, addr->sll_halen);
	return 0;
}
Ejemplo n.º 22
0
static int callback_usock(struct teamd_context *ctx, int events, void *priv)
{
	struct sockaddr_un addr;
	socklen_t alen;
	int sock;
	int err;

	alen = sizeof(addr);
	sock = accept(ctx->usock.sock, &addr, &alen);
	if (sock == -1) {
		teamd_log_err("usock: Failed to accept connection.");
		return -errno;
	}
	err = acc_conn_create(ctx, sock);
	if (err) {
		close(sock);
		return err;
	}
	return 0;
}
Ejemplo n.º 23
0
static int lw_nsnap_load_options(struct teamd_context *ctx,
				 struct teamd_port *tdport,
				 struct lw_psr_port_priv *psr_ppriv)
{
	struct lw_nsnap_port_priv *nsnap_ppriv = lw_nsnap_ppriv_get(psr_ppriv);
	struct teamd_config_path_cookie *cpcookie = psr_ppriv->common.cpcookie;
	const char *host;
	int err;

	err = teamd_config_string_get(ctx, &host, "@.target_host", cpcookie);
	if (err) {
		teamd_log_err("Failed to get \"target_host\" link-watch option.");
		return -EINVAL;
	}
	err = set_sockaddr_in6(&nsnap_ppriv->dst, host);
	if (err)
		return err;
	teamd_log_dbg("target address \"%s\".",
		      str_sockaddr_in6(&nsnap_ppriv->dst));

	return 0;
}
Ejemplo n.º 24
0
static int lw_psr_port_added(struct teamd_context *ctx,
			     struct teamd_port *tdport,
			     void *priv, void *creator_priv)
{
	struct lw_psr_port_priv *psr_ppriv = priv;
	int err;

	err = lw_psr_load_options(ctx, tdport, psr_ppriv);
	if (err) {
		teamd_log_err("Failed to load options.");
		return err;
	}

	err = psr_ppriv->ops->load_options(ctx, tdport, psr_ppriv);
	if (err) {
		teamd_log_err("Failed to load options.");
		return err;
	}

	err = psr_ppriv->ops->sock_open(psr_ppriv);
	if (err) {
		teamd_log_err("Failed to create socket.");
		return err;
	}

	err = teamd_loop_callback_fd_add(ctx, LW_SOCKET_CB_NAME, psr_ppriv,
					 lw_psr_callback_socket,
					 psr_ppriv->sock,
					 TEAMD_LOOP_FD_EVENT_READ);
	if (err) {
		teamd_log_err("Failed add socket callback.");
		goto close_sock;
	}

	err = teamd_loop_callback_timer_add_set(ctx, LW_PERIODIC_CB_NAME,
						psr_ppriv,
						lw_psr_callback_periodic,
						&psr_ppriv->interval,
						&psr_ppriv->init_wait);
	if (err) {
		teamd_log_err("Failed add callback timer");
		goto socket_callback_del;
	}

	err = team_set_port_user_linkup_enabled(ctx->th, tdport->ifindex, true);
	if (err) {
		teamd_log_err("%s: Failed to enable user linkup.",
			      tdport->ifname);
		goto periodic_callback_del;
	}

	teamd_loop_callback_enable(ctx, LW_SOCKET_CB_NAME, psr_ppriv);
	teamd_loop_callback_enable(ctx, LW_PERIODIC_CB_NAME, psr_ppriv);
	return 0;

periodic_callback_del:
	teamd_loop_callback_del(ctx, LW_PERIODIC_CB_NAME, psr_ppriv);
socket_callback_del:
	teamd_loop_callback_del(ctx, LW_SOCKET_CB_NAME, psr_ppriv);
close_sock:
	psr_ppriv->ops->sock_close(psr_ppriv);
	return err;
}
Ejemplo n.º 25
0
static int lw_ap_load_options(struct teamd_context *ctx,
			      struct teamd_port *tdport,
			      struct lw_psr_port_priv *psr_ppriv)
{
	struct lw_ap_port_priv *ap_ppriv = lw_ap_ppriv_get(psr_ppriv);
	struct teamd_config_path_cookie *cpcookie = psr_ppriv->common.cpcookie;
	const char *host;
	int tmp;
	int err;

	err = teamd_config_string_get(ctx, &host, "@.source_host", cpcookie);
	if (err) {
		teamd_log_err("Failed to get \"source_host\" link-watch option.");
		return -EINVAL;
	}
	err = set_in_addr(&ap_ppriv->src, host);
	if (err)
		return err;
	teamd_log_dbg("source address \"%s\".",
		      str_in_addr(&ap_ppriv->src));

	err = teamd_config_string_get(ctx, &host, "@.target_host", cpcookie);
	if (err) {
		teamd_log_err("Failed to get \"target_host\" link-watch option.");
		return -EINVAL;
	}
	err = set_in_addr(&ap_ppriv->dst, host);
	if (err)
		return err;
	teamd_log_dbg("target address \"%s\".", str_in_addr(&ap_ppriv->dst));

	err = teamd_config_bool_get(ctx, &ap_ppriv->validate_active,
				    "@.validate_active", cpcookie);
	if (err)
		ap_ppriv->validate_active = false;
	teamd_log_dbg("validate_active \"%d\".", ap_ppriv->validate_active);

	err = teamd_config_bool_get(ctx, &ap_ppriv->validate_inactive,
				    "@.validate_inactive", cpcookie);
	if (err)
		ap_ppriv->validate_inactive = false;
	teamd_log_dbg("validate_inactive \"%d\".", ap_ppriv->validate_inactive);

	err = teamd_config_bool_get(ctx, &ap_ppriv->send_always,
				    "@.send_always", cpcookie);
	if (err)
		ap_ppriv->send_always = false;
	teamd_log_dbg("send_always \"%d\".", ap_ppriv->send_always);

	err = teamd_config_int_get(ctx, &tmp, "@.vlanid", cpcookie);
	if (!err) {
		if (tmp < 0 || tmp >= 4096) {
			teamd_log_err("Wrong \"vlanid\" option value.");
			return -EINVAL;
		}
		ap_ppriv->vlanid_in_use = true;
		ap_ppriv->vlanid = tmp;
		teamd_log_dbg("vlan id \"%u\".", ap_ppriv->vlanid);
	}

	return 0;
}