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; }
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; }
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(ðtool_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(ðtool_ppriv->delay_down, tmp); } return 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); }
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); }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; } }
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; }
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; }
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; }
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(ðtool_ppriv->delay_up)) goto nodelay; delay = ðtool_ppriv->delay_up; } else { if (timespec_is_zero(ðtool_ppriv->delay_down)) goto nodelay; delay = ðtool_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); }
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; }
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; }
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; }
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; }
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; }
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; }