summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHiram van Paassen <hiram.van.paassen@mastervolt.com>2018-04-10 17:26:20 +0200
committerMarc Kleine-Budde <mkl@pengutronix.de>2018-06-09 15:12:31 +0200
commit06828bb617713d522775c725be5db9935e3aea6e (patch)
treee75b5f633bb0741d721694fd2a123c3b4912b746 /src
parentbd5038f8b76e2fc1b30e09b5a48d8f96e778f8b1 (diff)
downloadsystemd-06828bb617713d522775c725be5db9935e3aea6e.tar.gz
networkd-link: add support to configure CAN interfaces
This patch adds support for kind "can". Fixes: #4042.
Diffstat (limited to 'src')
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c8
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h1
-rw-r--r--src/network/networkd-link.c122
-rw-r--r--src/network/networkd-network-gperf.gperf3
-rw-r--r--src/network/networkd-network.c3
-rw-r--r--src/network/networkd-network.h5
6 files changed, 140 insertions, 2 deletions
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index ddb4d90eaf..a82b1e1a89 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -300,6 +300,11 @@ static const NLType rtnl_link_info_data_geneve_types[] = {
[IFLA_GENEVE_LABEL] = { .type = NETLINK_TYPE_U32 },
};
+static const NLType rtnl_link_info_data_can_types[] = {
+ [IFLA_CAN_BITTIMING] = { .size = sizeof(struct can_bittiming) },
+ [IFLA_CAN_RESTART_MS] = { .type = NETLINK_TYPE_U32 },
+};
+
/* these strings must match the .kind entries in the kernel */
static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_BOND] = "bond",
@@ -326,6 +331,7 @@ static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_VXCAN] = "vxcan",
[NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
[NL_UNION_LINK_INFO_DATA_NETDEVSIM] = "netdevsim",
+ [NL_UNION_LINK_INFO_DATA_CAN] = "can",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
@@ -371,6 +377,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
.types = rtnl_link_info_data_geneve_types },
[NL_UNION_LINK_INFO_DATA_VXCAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxcan_types),
.types = rtnl_link_info_data_vxcan_types },
+ [NL_UNION_LINK_INFO_DATA_CAN] = { .count = ELEMENTSOF(rtnl_link_info_data_can_types),
+ .types = rtnl_link_info_data_can_types },
};
static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index a7542eb33d..559976c24c 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -83,6 +83,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_VXCAN,
NL_UNION_LINK_INFO_DATA_WIREGUARD,
NL_UNION_LINK_INFO_DATA_NETDEVSIM,
+ NL_UNION_LINK_INFO_DATA_CAN,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
} NLUnionLinkInfoData;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index b36c383d37..c0496407ab 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -7,6 +7,7 @@
#include <netinet/ether.h>
#include <linux/if.h>
+#include <linux/can/netlink.h>
#include <unistd.h>
#include <stdio_ext.h>
@@ -1872,6 +1873,105 @@ static int link_up_can(Link *link) {
return 0;
}
+static int link_set_can(Link *link) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+
+ log_link_debug(link, "link_set_can");
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set netlink flags: %m");
+
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+ if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) {
+ struct can_bittiming bt = {
+ .bitrate = link->network->can_bitrate,
+ .sample_point = link->network->can_sample_point,
+ };
+
+ if (link->network->can_bitrate > UINT32_MAX) {
+ log_link_error(link, "bitrate (%zu) too big.", link->network->can_bitrate);
+ return -ERANGE;
+ }
+
+ log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
+ if (link->network->can_sample_point > 0)
+ log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
+ else
+ log_link_debug(link, "Using default sample point");
+
+ r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
+ }
+
+ if (link->network->can_restart_us > 0) {
+ char time_string[FORMAT_TIMESPAN_MAX];
+ uint64_t restart_ms;
+
+ if (link->network->can_restart_us == USEC_INFINITY)
+ restart_ms = 0;
+ else
+ restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC);
+
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, restart_ms * 1000, MSEC_PER_SEC);
+
+ if (restart_ms > UINT32_MAX) {
+ log_link_error(link, "restart timeout (%s) too big.", time_string);
+ return -ERANGE;
+ }
+
+ log_link_debug(link, "Setting restart = %s", time_string);
+
+ r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m");
+ }
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_call_async(link->manager->rtnl, m, link_set_handler, link, 0, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+
+ if (!(link->flags & IFF_UP)) {
+ r = link_up_can(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
+ }
+ }
+
+ log_link_debug(link, "link_set_can done");
+
+ return r;
+}
+
static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_(link_unrefp) Link *link = userdata;
int r;
@@ -1885,6 +1985,11 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *user
if (r < 0)
log_link_warning_errno(link, r, "Could not bring down interface: %m");
+ if (streq_ptr(link->kind, "can")) {
+ link_ref(link);
+ link_set_can(link);
+ }
+
return 1;
}
@@ -2602,6 +2707,21 @@ static int link_update_lldp(Link *link) {
static int link_configure_can(Link *link) {
int r;
+ if (streq_ptr(link->kind, "can")) {
+ /* The CAN interface must be down to configure bitrate, etc... */
+ if ((link->flags & IFF_UP)) {
+ r = link_down(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
+ }
+
+ return 0;
+ }
+
+ return link_set_can(link);
+ }
+
if (!(link->flags & IFF_UP)) {
r = link_up_can(link);
if (r < 0) {
@@ -2620,7 +2740,7 @@ static int link_configure(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_PENDING);
- if (streq_ptr(link->kind, "vcan"))
+ if (STRPTR_IN_SET(link->kind, "can", "vcan"))
return link_configure_can(link);
/* Drop foreign config, but ignore loopback or critical devices.
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 7e625e48fa..e6ca6631ed 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -178,6 +178,9 @@ IPv6Prefix.OnLink, config_parse_prefix_flags,
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
+CAN.BitRate, config_parse_si_size, 0, offsetof(Network, can_bitrate)
+CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
+CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
/* backwards compatibility: do not add new entries to this section */
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index c3a11ddca0..b2a75c7e98 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -272,7 +272,8 @@ static int network_load_one(Manager *manager, const char *filename) {
"BridgeFDB\0"
"BridgeVLAN\0"
"IPv6PrefixDelegation\0"
- "IPv6Prefix\0",
+ "IPv6Prefix\0"
+ "CAN\0",
config_item_perf_lookup, network_network_gperf_lookup,
CONFIG_PARSE_WARN, network);
if (r < 0)
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index b8e2c523a3..5b6b40d5da 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -191,6 +191,11 @@ struct Network {
uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN];
uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
+ /* CAN support */
+ size_t can_bitrate;
+ unsigned can_sample_point;
+ usec_t can_restart_us;
+
AddressFamilyBoolean ip_forward;
bool ip_masquerade;