X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=system-linux.c;h=4463a2a8282a1151cb99734d65dd57689a7fad52;hb=HEAD;hp=e437377deb7bd96b00f23fc5f77ac9259d737e24;hpb=f429bd94f99e55548bf4fa8156c165017ce3c41c;p=project%2Fnetifd.git diff --git a/system-linux.c b/system-linux.c index e437377..4463a2a 100644 --- a/system-linux.c +++ b/system-linux.c @@ -169,19 +169,14 @@ static void handler_nl_event(struct uloop_fd *u, unsigned int events) { struct event_socket *ev = container_of(u, struct event_socket, uloop); - int err; - socklen_t errlen = sizeof(err); + int ret; - if (!u->error) { - nl_recvmsgs_default(ev->sock); + ret = nl_recvmsgs_default(ev->sock); + if (ret >= 0) return; - } - - if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen)) - goto abort; - switch(err) { - case ENOBUFS: + switch (-ret) { + case NLE_NOMEM: /* Increase rx buffer size on netlink socket */ ev->bufsize *= 2; if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) @@ -195,7 +190,6 @@ handler_nl_event(struct uloop_fd *u, unsigned int events) default: goto abort; } - u->error = false; return; abort: @@ -203,6 +197,14 @@ abort: 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) { @@ -220,6 +222,9 @@ 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; } @@ -692,34 +697,42 @@ static int system_get_arp_accept(struct device *dev, char *buf, const size_t buf dev->ifname, buf, buf_sz); } +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 +#endif + +static void +system_device_update_state(struct device *dev, unsigned int flags, unsigned int ifindex) +{ + if (dev->type == &simple_device_type) { + if (dev->external) + device_set_disabled(dev, !(flags & IFF_UP)); + + device_set_present(dev, ifindex > 0); + } + device_set_link(dev, flags & IFF_LOWER_UP ? true : false); +} + /* Evaluate netlink messages */ static int cb_rtnl_event(struct nl_msg *msg, void *arg) { struct nlmsghdr *nh = nlmsg_hdr(msg); + struct ifinfomsg *ifi = NLMSG_DATA(nh); struct nlattr *nla[__IFLA_MAX]; - int link_state = 0; - char buf[10]; + struct device *dev; if (nh->nlmsg_type != RTM_NEWLINK) - goto out; + return 0; nlmsg_parse(nh, sizeof(struct ifinfomsg), nla, __IFLA_MAX - 1, NULL); if (!nla[IFLA_IFNAME]) - goto out; + return 0; - struct device *dev = device_find(nla_data(nla[IFLA_IFNAME])); + dev = device_find(nla_data(nla[IFLA_IFNAME])); if (!dev) - goto out; - - if (!system_get_dev_sysfs("carrier", dev->ifname, buf, sizeof(buf))) - link_state = strtoul(buf, NULL, 0); - - if (dev->type == &simple_device_type) - device_set_present(dev, true); - - device_set_link(dev, link_state ? true : false); + return 0; -out: + system_device_update_state(dev, ifi->ifi_flags, ifi->ifi_index); return 0; } @@ -778,24 +791,19 @@ handle_hotplug_event(struct uloop_fd *u, unsigned int events) struct sockaddr_nl nla; unsigned char *buf = NULL; int size; - int err; - socklen_t errlen = sizeof(err); - if (!u->error) { - while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) { - if (nla.nl_pid == 0) - handle_hotplug_msg((char *) buf, size); + while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) { + if (nla.nl_pid == 0) + handle_hotplug_msg((char *) buf, size); - free(buf); - } - return; + free(buf); } - if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen)) - goto abort; + switch (-size) { + case 0: + return; - switch(err) { - case ENOBUFS: + case NLE_NOMEM: /* Increase rx buffer size on netlink socket */ ev->bufsize *= 2; if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) @@ -805,7 +813,6 @@ handle_hotplug_event(struct uloop_fd *u, unsigned int events) default: goto abort; } - u->error = false; return; abort: @@ -940,16 +947,19 @@ int system_bridge_addif(struct device *bridge, struct device *dev) 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) @@ -1228,9 +1238,9 @@ static int cb_clear_event(struct nl_msg *msg, void *arg) 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); @@ -1243,9 +1253,9 @@ static int cb_clear_event(struct nl_msg *msg, void *arg) 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); } @@ -1338,14 +1348,14 @@ void system_if_clear_state(struct device *dev) 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); } @@ -1438,7 +1448,7 @@ int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg) 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; @@ -1494,7 +1504,7 @@ int system_macvlan_add(struct device *macvlan, struct device *dev, struct macvla 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; @@ -1582,9 +1592,9 @@ int system_veth_add(struct device *veth, struct veth_config *cfg) 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; @@ -1690,7 +1700,7 @@ int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlande 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; @@ -1704,6 +1714,182 @@ int system_vlandev_del(struct device *vlandev) 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; + int 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); + } +} + +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))) + break; + + 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); + + 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 = 0; + 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 = 0; + 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 = 1, + }; + 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 > 0) + 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) @@ -1726,6 +1912,40 @@ static bool ethtool_link_mode_test_bit(__s8 nwords, int nr, const __u32 *mask) 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) { @@ -1775,6 +1995,23 @@ 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) { @@ -1791,6 +2028,9 @@ 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); @@ -1850,11 +2090,19 @@ system_set_ethtool_settings(struct device *dev, struct device_settings *s) 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); @@ -1975,6 +2223,20 @@ system_if_get_settings(struct device *dev, struct device_settings *s) 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 @@ -1985,6 +2247,16 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, uint64_t 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) { @@ -2076,6 +2348,11 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, uint64_t 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); @@ -2092,10 +2369,6 @@ struct if_check_data { int ret; }; -#ifndef IFF_LOWER_UP -#define IFF_LOWER_UP 0x10000 -#endif - static int cb_if_check_valid(struct nl_msg *msg, void *arg) { struct nlmsghdr *nh = nlmsg_hdr(msg); @@ -2105,10 +2378,7 @@ static int cb_if_check_valid(struct nl_msg *msg, void *arg) if (nh->nlmsg_type != RTM_NEWLINK) return NL_SKIP; - if (chk->dev->type == &simple_device_type) - device_set_present(chk->dev, ifi->ifi_index > 0 ? true : false); - device_set_link(chk->dev, ifi->ifi_flags & IFF_LOWER_UP ? true : false); - + system_device_update_state(chk->dev, ifi->ifi_flags, ifi->ifi_index); return NL_OK; } @@ -3019,6 +3289,9 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd) } } + if (route->flags & DEVROUTE_NODEV) + dev = NULL; + msg = nlmsg_alloc_simple(cmd, flags); if (!msg) return -1; @@ -4126,7 +4399,7 @@ static int system_add_vxlan(const char *name, const unsigned int link, struct bl 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;