]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-can.c
Merge pull request #15138 from ssahani/networkctl-vlan
[thirdparty/systemd.git] / src / network / networkd-can.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <net/if.h>
4 #include <linux/can/netlink.h>
5
6 #include "netlink-util.h"
7 #include "networkd-can.h"
8 #include "networkd-link.h"
9 #include "networkd-manager.h"
10 #include "string-util.h"
11
12 #define CAN_TERMINATION_OHM_VALUE 120
13
14 static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
15 int r;
16
17 assert(link);
18
19 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
20 return 1;
21
22 r = sd_netlink_message_get_errno(m);
23 if (r < 0)
24 /* we warn but don't fail the link, as it may be brought up later */
25 log_link_message_warning_errno(link, m, r, "Could not bring up interface");
26
27 return 1;
28 }
29
30 static int link_up_can(Link *link) {
31 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
32 int r;
33
34 assert(link);
35
36 log_link_debug(link, "Bringing CAN link up");
37
38 r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
39 if (r < 0)
40 return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
41
42 r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
43 if (r < 0)
44 return log_link_error_errno(link, r, "Could not set link flags: %m");
45
46 r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler,
47 link_netlink_destroy_callback, link);
48 if (r < 0)
49 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
50
51 link_ref(link);
52
53 return 0;
54 }
55
56 static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
57 int r;
58
59 assert(link);
60
61 log_link_debug(link, "Set link");
62
63 r = sd_netlink_message_get_errno(m);
64 if (r < 0 && r != -EEXIST) {
65 log_link_message_warning_errno(link, m, r, "Failed to configure CAN link");
66 link_enter_failed(link);
67 }
68
69 return 1;
70 }
71
72 static int link_set_can(Link *link) {
73 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
74 struct can_ctrlmode cm = {};
75 int r;
76
77 assert(link);
78 assert(link->network);
79 assert(link->manager);
80 assert(link->manager->rtnl);
81
82 log_link_debug(link, "Configuring CAN link.");
83
84 r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex);
85 if (r < 0)
86 return log_link_error_errno(link, r, "Failed to allocate netlink message: %m");
87
88 r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK);
89 if (r < 0)
90 return log_link_error_errno(link, r, "Could not set netlink flags: %m");
91
92 r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
93 if (r < 0)
94 return log_link_error_errno(link, r, "Failed to open netlink container: %m");
95
96 r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind);
97 if (r < 0)
98 return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m");
99
100 if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) {
101 struct can_bittiming bt = {
102 .bitrate = link->network->can_bitrate,
103 .sample_point = link->network->can_sample_point,
104 };
105
106 if (link->network->can_bitrate > UINT32_MAX) {
107 log_link_error(link, "bitrate (%" PRIu64 ") too big.", link->network->can_bitrate);
108 return -ERANGE;
109 }
110
111 log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
112 if (link->network->can_sample_point > 0)
113 log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
114 else
115 log_link_debug(link, "Using default sample point");
116
117 r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
118 if (r < 0)
119 return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
120 }
121
122 if (link->network->can_restart_us > 0) {
123 char time_string[FORMAT_TIMESPAN_MAX];
124 uint64_t restart_ms;
125
126 if (link->network->can_restart_us == USEC_INFINITY)
127 restart_ms = 0;
128 else
129 restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC);
130
131 format_timespan(time_string, FORMAT_TIMESPAN_MAX, restart_ms * 1000, MSEC_PER_SEC);
132
133 if (restart_ms > UINT32_MAX) {
134 log_link_error(link, "restart timeout (%s) too big.", time_string);
135 return -ERANGE;
136 }
137
138 log_link_debug(link, "Setting restart = %s", time_string);
139
140 r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms);
141 if (r < 0)
142 return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m");
143 }
144
145 if (link->network->can_triple_sampling >= 0) {
146 cm.mask |= CAN_CTRLMODE_3_SAMPLES;
147 SET_FLAG(cm.flags, CAN_CTRLMODE_3_SAMPLES, link->network->can_triple_sampling);
148 log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis");
149 }
150
151 if (link->network->can_listen_only >= 0) {
152 cm.mask |= CAN_CTRLMODE_LISTENONLY;
153 SET_FLAG(cm.flags, CAN_CTRLMODE_LISTENONLY, link->network->can_listen_only);
154 log_link_debug(link, "%sabling listen-only mode", link->network->can_listen_only ? "En" : "Dis");
155 }
156
157 if (cm.mask != 0) {
158 r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
159 if (r < 0)
160 return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m");
161 }
162
163 if (link->network->can_termination >= 0) {
164
165 log_link_debug(link, "%sabling can-termination", link->network->can_termination ? "En" : "Dis");
166
167 r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION,
168 link->network->can_termination ? CAN_TERMINATION_OHM_VALUE : 0);
169 if (r < 0)
170 return log_link_error_errno(link, r, "Could not append IFLA_CAN_TERMINATION attribute: %m");
171
172 }
173
174 r = sd_netlink_message_close_container(m);
175 if (r < 0)
176 return log_link_error_errno(link, r, "Failed to close netlink container: %m");
177
178 r = sd_netlink_message_close_container(m);
179 if (r < 0)
180 return log_link_error_errno(link, r, "Failed to close netlink container: %m");
181
182 r = netlink_call_async(link->manager->rtnl, NULL, m, link_set_handler,
183 link_netlink_destroy_callback, link);
184 if (r < 0)
185 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
186
187 link_ref(link);
188
189 if (!(link->flags & IFF_UP))
190 return link_up_can(link);
191
192 return 0;
193 }
194
195 static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
196 int r;
197
198 assert(link);
199
200 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
201 return 1;
202
203 r = sd_netlink_message_get_errno(m);
204 if (r < 0) {
205 log_link_message_warning_errno(link, m, r, "Could not bring down interface");
206 link_enter_failed(link);
207 return 1;
208 }
209
210 r = link_set_can(link);
211 if (r < 0)
212 link_enter_failed(link);
213
214 return 1;
215 }
216
217 int link_configure_can(Link *link) {
218 int r;
219
220 link_set_state(link, LINK_STATE_CONFIGURING);
221
222 if (streq_ptr(link->kind, "can")) {
223 /* The CAN interface must be down to configure bitrate, etc... */
224 if ((link->flags & IFF_UP)) {
225 r = link_down(link, link_down_handler);
226 if (r < 0) {
227 link_enter_failed(link);
228 return r;
229 }
230 } else {
231 r = link_set_can(link);
232 if (r < 0) {
233 link_enter_failed(link);
234 return r;
235 }
236 }
237
238 return 0;
239 }
240
241 if (!(link->flags & IFF_UP)) {
242 r = link_up_can(link);
243 if (r < 0) {
244 link_enter_failed(link);
245 return r;
246 }
247 }
248
249 return 0;
250 }