return;
}
+static void
+nl_udebug_cb(void *priv, struct nl_msg *msg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+
+ udebug_netlink_msg(priv, nlmsg_get_proto(msg), nlh, nlh->nlmsg_len);
+}
+
static struct nl_sock *
create_socket(int protocol, int groups)
{
return NULL;
}
+ nl_socket_set_tx_debug_cb(sock, nl_udebug_cb, &udb_nl);
+ nl_socket_set_rx_debug_cb(sock, nl_udebug_cb, &udb_nl);
+
return sock;
}
system_device_update_state(struct device *dev, unsigned int flags, unsigned int ifindex)
{
if (dev->type == &simple_device_type) {
- bool present = ifindex > 0;
-
if (dev->external)
- present = present && (flags & IFF_UP);
+ device_set_disabled(dev, !(flags & IFF_UP));
- device_set_present(dev, present);
+ device_set_present(dev, ifindex > 0);
}
device_set_link(dev, flags & IFF_LOWER_UP ? true : false);
}
int tries = 0;
int ret;
-retry:
- ret = 0;
- oldbr = system_get_bridge(dev->ifname, dev_buf, sizeof(dev_buf));
- if (!oldbr || strcmp(oldbr, bridge->ifname) != 0) {
+
+ for (tries = 0; tries < 3; tries++) {
+ ret = 0;
+ oldbr = system_get_bridge(dev->ifname, dev_buf, sizeof(dev_buf));
+ if (oldbr && !strcmp(oldbr, bridge->ifname))
+ break;
+
ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL);
- tries++;
- D(SYSTEM, "Failed to add device '%s' to bridge '%s' (tries=%d): %s\n",
+ if (!ret)
+ break;
+
+ D(SYSTEM, "Failed to add device '%s' to bridge '%s' (tries=%d): %s",
dev->ifname, bridge->ifname, tries, strerror(errno));
- if (tries <= 3)
- goto retry;
}
if (dev->wireless)
return NL_SKIP;
if (type == RTM_DELRULE)
- D(SYSTEM, "Remove a rule\n");
+ D(SYSTEM, "Remove a rule");
else
- D(SYSTEM, "Remove %s from device %s\n",
+ D(SYSTEM, "Remove %s from device %s",
type == RTM_DELADDR ? "an address" : "a route",
clr->dev->ifname);
ret = nl_send_auto_complete(sock_rtnl, clr->msg);
if (ret < 0) {
if (type == RTM_DELRULE)
- D(SYSTEM, "Error deleting a rule: %d\n", ret);
+ D(SYSTEM, "Error deleting a rule: %d", ret);
else
- D(SYSTEM, "Error deleting %s from device '%s': %d\n",
+ D(SYSTEM, "Error deleting %s from device '%s': %d",
type == RTM_DELADDR ? "an address" : "a route",
clr->dev->ifname, ret);
}
system_if_flags(dev->ifname, 0, IFF_UP);
if (system_is_bridge(dev->ifname)) {
- D(SYSTEM, "Delete existing bridge named '%s'\n", dev->ifname);
+ D(SYSTEM, "Delete existing bridge named '%s'", dev->ifname);
system_bridge_delbr(dev);
return;
}
bridge = system_get_bridge(dev->ifname, buf, sizeof(buf));
if (bridge) {
- D(SYSTEM, "Remove device '%s' from bridge '%s'\n", dev->ifname, bridge);
+ D(SYSTEM, "Remove device '%s' from bridge '%s'", dev->ifname, bridge);
system_bridge_if(bridge, dev, SIOCBRDELIF, NULL);
}
rv = system_rtnl_call(msg);
if (rv)
- D(SYSTEM, "Error adding bridge '%s': %d\n", bridge->ifname, rv);
+ D(SYSTEM, "Error adding bridge '%s': %d", bridge->ifname, rv);
return rv;
rv = system_rtnl_call(msg);
if (rv)
- D(SYSTEM, "Error adding macvlan '%s' over '%s': %d\n", macvlan->ifname, dev->ifname, rv);
+ D(SYSTEM, "Error adding macvlan '%s' over '%s': %d", macvlan->ifname, dev->ifname, rv);
return rv;
rv = system_rtnl_call(msg);
if (rv) {
if (cfg->flags & VETH_OPT_PEER_NAME)
- D(SYSTEM, "Error adding veth '%s' with peer '%s': %d\n", veth->ifname, cfg->peer_name, rv);
+ D(SYSTEM, "Error adding veth '%s' with peer '%s': %d", veth->ifname, cfg->peer_name, rv);
else
- D(SYSTEM, "Error adding veth '%s': %d\n", veth->ifname, rv);
+ D(SYSTEM, "Error adding veth '%s': %d", veth->ifname, rv);
}
return rv;
rv = system_rtnl_call(msg);
if (rv)
- D(SYSTEM, "Error adding vlandev '%s' over '%s': %d\n", vlandev->ifname, dev->ifname, rv);
+ D(SYSTEM, "Error adding vlandev '%s' over '%s': %d", vlandev->ifname, dev->ifname, rv);
return rv;
return system_link_del(vlandev->ifname);
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
+struct if_get_master_data {
+ int ifindex;
+ int master_ifindex;
+ bool pending;
+};
+
+static void if_get_master_dsa_linkinfo_attr(struct if_get_master_data *data,
+ struct rtattr *attr)
+{
+ struct rtattr *cur;
+ int rem = RTA_PAYLOAD(attr);
+
+ for (cur = RTA_DATA(attr); RTA_OK(cur, rem); cur = RTA_NEXT(cur, rem)) {
+ if (cur->rta_type != IFLA_DSA_MASTER)
+ continue;
+
+ data->master_ifindex = *(__u32 *)RTA_DATA(cur);
+ return;
+ }
+}
+
+static void if_get_master_linkinfo_attr(struct if_get_master_data *data,
+ struct rtattr *attr)
+{
+ struct rtattr *cur;
+ int rem = RTA_PAYLOAD(attr);
+
+ for (cur = RTA_DATA(attr); RTA_OK(cur, rem); cur = RTA_NEXT(cur, rem)) {
+ if (cur->rta_type != IFLA_INFO_KIND && cur->rta_type != IFLA_INFO_DATA)
+ continue;
+
+ if (cur->rta_type == IFLA_INFO_KIND && strcmp("dsa", (char *)RTA_DATA(cur)))
+ return;
+
+ if (cur->rta_type == IFLA_INFO_DATA)
+ if_get_master_dsa_linkinfo_attr(data, cur);
+ }
+}
+
+static int cb_if_get_master_valid(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nh = nlmsg_hdr(msg);
+ struct ifinfomsg *ifi = NLMSG_DATA(nh);
+ struct if_get_master_data *data = (struct if_get_master_data *)arg;
+ struct rtattr *attr;
+ int rem;
+
+ if (nh->nlmsg_type != RTM_NEWLINK)
+ return NL_SKIP;
+
+ if (ifi->ifi_family != AF_UNSPEC)
+ return NL_SKIP;
+
+ if (ifi->ifi_index != data->ifindex)
+ return NL_SKIP;
+
+ attr = IFLA_RTA(ifi);
+ rem = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
+
+ while (RTA_OK(attr, rem)) {
+ if (attr->rta_type == IFLA_LINKINFO) {
+ if_get_master_linkinfo_attr(data, attr);
+ data->pending = false;
+ break;
+ }
+
+ attr = RTA_NEXT(attr, rem);
+ }
+
+ return NL_OK;
+}
+
+static int cb_if_get_master_ack(struct nl_msg *msg, void *arg)
+{
+ struct if_get_master_data *data = (struct if_get_master_data *)arg;
+ data->pending = false;
+
+ return NL_STOP;
+}
+
+static int cb_if_get_master_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
+{
+ struct if_get_master_data *data = (struct if_get_master_data *)arg;
+ data->pending = false;
+
+ return NL_STOP;
+}
+
+static int system_if_get_master_ifindex(struct device *dev)
+{
+ struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+ struct nl_msg *msg;
+ struct ifinfomsg ifi = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = 0,
+ };
+ struct if_get_master_data data = {
+ .ifindex = if_nametoindex(dev->ifname),
+ .master_ifindex = -1,
+ .pending = true,
+ };
+ int ret = -1;
+
+ if (!cb)
+ return ret;
+
+ msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST);
+ if (!msg)
+ goto out;
+
+ if (nlmsg_append(msg, &ifi, sizeof(ifi), 0) ||
+ nla_put_string(msg, IFLA_IFNAME, dev->ifname))
+ goto free;
+
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_if_get_master_valid, &data);
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_if_get_master_ack, &data);
+ nl_cb_err(cb, NL_CB_CUSTOM, cb_if_get_master_error, &data);
+
+ ret = nl_send_auto_complete(sock_rtnl, msg);
+ if (ret < 0)
+ goto free;
+
+ while (data.pending)
+ nl_recvmsgs(sock_rtnl, cb);
+
+ if (data.master_ifindex >= 0)
+ ret = data.master_ifindex;
+
+free:
+ nlmsg_free(msg);
+out:
+ nl_cb_put(cb);
+ return ret;
+}
+
+static void system_refresh_orig_macaddr(struct device *dev, struct device_settings *s)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
+
+ if (ioctl(sock_ioctl, SIOCGIFHWADDR, &ifr) == 0)
+ memcpy(s->macaddr, &ifr.ifr_hwaddr.sa_data, sizeof(s->macaddr));
+}
+
+static void system_set_master(struct device *dev, int master_ifindex)
+{
+ struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC, };
+ struct nl_msg *nlm;
+
+ nlm = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST);
+ if (!nlm)
+ return;
+
+ nlmsg_append(nlm, &ifi, sizeof(ifi), 0);
+ nla_put_string(nlm, IFLA_IFNAME, dev->ifname);
+
+ struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO);
+ if (!linkinfo)
+ goto failure;
+
+ nla_put_string(nlm, IFLA_INFO_KIND, "dsa");
+ struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA);
+ if (!infodata)
+ goto failure;
+
+ nla_put_u32(nlm, IFLA_DSA_MASTER, master_ifindex);
+
+ nla_nest_end(nlm, infodata);
+ nla_nest_end(nlm, linkinfo);
+
+ system_rtnl_call(nlm);
+
+ return;
+
+failure:
+ nlmsg_free(nlm);
+}
+#endif
+
static void ethtool_link_mode_clear_bit(__s8 nwords, int nr, __u32 *mask)
{
if (nr < 0)
return !!(mask[nr / 32] & (1U << (nr % 32)));
}
+static int
+system_get_ethtool_gro(struct device *dev)
+{
+ struct ethtool_value ecmd;
+ struct ifreq ifr = {
+ .ifr_data = (caddr_t)&ecmd,
+ };
+
+ memset(&ecmd, 0, sizeof(ecmd));
+ ecmd.cmd = ETHTOOL_GGRO;
+ strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
+
+ if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr))
+ return -1;
+
+ return ecmd.data;
+}
+
+static void
+system_set_ethtool_gro(struct device *dev, struct device_settings *s)
+{
+ struct ethtool_value ecmd;
+ struct ifreq ifr = {
+ .ifr_data = (caddr_t)&ecmd,
+ };
+
+ memset(&ecmd, 0, sizeof(ecmd));
+ ecmd.cmd = ETHTOOL_SGRO;
+ ecmd.data = s->gro;
+ strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
+
+ ioctl(sock_ioctl, SIOCETHTOOL, &ifr);
+}
+
static void
system_set_ethtool_pause(struct device *dev, struct device_settings *s)
{
ioctl(sock_ioctl, SIOCETHTOOL, &ifr);
}
+static void
+system_set_ethtool_eee_settings(struct device *dev, struct device_settings *s)
+{
+ struct ethtool_eee eeecmd;
+ struct ifreq ifr = {
+ .ifr_data = (caddr_t)&eeecmd,
+ };
+
+ memset(&eeecmd, 0, sizeof(eeecmd));
+ eeecmd.cmd = ETHTOOL_SEEE;
+ eeecmd.eee_enabled = s->eee;
+ strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
+
+ if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) != 0)
+ netifd_log_message(L_WARNING, "cannot set eee %d for device %s", s->eee, dev->ifname);
+}
+
static void
system_set_ethtool_settings(struct device *dev, struct device_settings *s)
{
system_set_ethtool_pause(dev, s);
+ if (s->flags & DEV_OPT_EEE)
+ system_set_ethtool_eee_settings(dev, s);
+
memset(&ecmd, 0, sizeof(ecmd));
ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
ioctl(sock_ioctl, SIOCETHTOOL, &ifr);
}
+static void
+system_set_ethtool_settings_after_up(struct device *dev, struct device_settings *s)
+{
+ if (s->flags & DEV_OPT_GRO)
+ system_set_ethtool_gro(dev, s);
+}
+
void
system_if_get_settings(struct device *dev, struct device_settings *s)
{
struct ifreq ifr;
char buf[10];
+ int ret;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
s->arp_accept = strtoul(buf, NULL, 0);
s->flags |= DEV_OPT_ARP_ACCEPT;
}
+
+ ret = system_get_ethtool_gro(dev);
+ if (ret >= 0) {
+ s->gro = ret;
+ s->flags |= DEV_OPT_GRO;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
+ ret = system_if_get_master_ifindex(dev);
+ if (ret >= 0) {
+ s->master_ifindex = ret;
+ s->flags |= DEV_OPT_MASTER;
+ }
+#endif
}
void
apply_mask &= s->flags;
+ if (apply_mask & DEV_OPT_MASTER) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
+ system_set_master(dev, s->master_ifindex);
+ if (!(apply_mask & (DEV_OPT_MACADDR | DEV_OPT_DEFAULT_MACADDR)) || dev->external)
+ system_refresh_orig_macaddr(dev, &dev->orig_settings);
+#else
+ netifd_log_message(L_WARNING, "%s Your kernel is older than linux 6.1.0, changing DSA port conduit is not supported!", dev->ifname);
+#endif
+ }
+
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
if (apply_mask & DEV_OPT_MTU) {
system_set_ethtool_settings(dev, s);
}
+void system_if_apply_settings_after_up(struct device *dev, struct device_settings *s)
+{
+ system_set_ethtool_settings_after_up(dev, s);
+}
+
int system_if_up(struct device *dev)
{
return system_if_flags(dev->ifname, IFF_UP, 0);
}
}
+ if (route->flags & DEVROUTE_NODEV)
+ dev = NULL;
+
msg = nlmsg_alloc_simple(cmd, flags);
if (!msg)
return -1;
ret = system_rtnl_call(msg);
if (ret)
- D(SYSTEM, "Error adding vxlan '%s': %d\n", name, ret);
+ D(SYSTEM, "Error adding vxlan '%s': %d", name, ret);
return ret;