]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
3ddcbeea YW |
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" | |
74a27268 | 10 | #include "parse-util.h" |
3ddcbeea YW |
11 | #include "string-util.h" |
12 | ||
52aa38f1 MR |
13 | #define CAN_TERMINATION_OHM_VALUE 120 |
14 | ||
74a27268 RP |
15 | int config_parse_can_bitrate( |
16 | const char* unit, | |
17 | const char *filename, | |
18 | unsigned line, | |
19 | const char *section, | |
20 | unsigned section_line, | |
21 | const char *lvalue, | |
22 | int ltype, | |
23 | const char *rvalue, | |
24 | void *data, | |
25 | void *userdata) { | |
26 | ||
27 | uint32_t *br = data; | |
28 | uint64_t sz; | |
29 | int r; | |
30 | ||
31 | assert(filename); | |
32 | assert(lvalue); | |
33 | assert(rvalue); | |
34 | assert(data); | |
35 | ||
36 | r = parse_size(rvalue, 1000, &sz); | |
37 | if (r < 0) { | |
d96edb2c | 38 | log_syntax(unit, LOG_WARNING, filename, line, r, |
74a27268 RP |
39 | "Failed to parse can bitrate '%s', ignoring: %m", rvalue); |
40 | return 0; | |
41 | } | |
42 | ||
43 | /* Linux uses __u32 for bitrates, so the value should not exceed that. */ | |
44 | if (sz <= 0 || sz > UINT32_MAX) { | |
d96edb2c | 45 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
74a27268 RP |
46 | "Bit rate out of permitted range 1...4294967295"); |
47 | return 0; | |
48 | } | |
49 | ||
50 | *br = (uint32_t) sz; | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
3ddcbeea YW |
55 | static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
56 | int r; | |
57 | ||
58 | assert(link); | |
59 | ||
60 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
61 | return 1; | |
62 | ||
63 | r = sd_netlink_message_get_errno(m); | |
64 | if (r < 0) | |
65 | /* we warn but don't fail the link, as it may be brought up later */ | |
5ecb131d | 66 | log_link_message_warning_errno(link, m, r, "Could not bring up interface"); |
3ddcbeea YW |
67 | |
68 | return 1; | |
69 | } | |
70 | ||
71 | static int link_up_can(Link *link) { | |
72 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
73 | int r; | |
74 | ||
75 | assert(link); | |
76 | ||
77 | log_link_debug(link, "Bringing CAN link up"); | |
78 | ||
79 | r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); | |
80 | if (r < 0) | |
81 | return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); | |
82 | ||
83 | r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); | |
84 | if (r < 0) | |
85 | return log_link_error_errno(link, r, "Could not set link flags: %m"); | |
86 | ||
87 | r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler, | |
88 | link_netlink_destroy_callback, link); | |
89 | if (r < 0) | |
90 | return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); | |
91 | ||
92 | link_ref(link); | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { | |
98 | int r; | |
99 | ||
100 | assert(link); | |
101 | ||
102 | log_link_debug(link, "Set link"); | |
103 | ||
104 | r = sd_netlink_message_get_errno(m); | |
105 | if (r < 0 && r != -EEXIST) { | |
5ecb131d | 106 | log_link_message_warning_errno(link, m, r, "Failed to configure CAN link"); |
3ddcbeea YW |
107 | link_enter_failed(link); |
108 | } | |
109 | ||
110 | return 1; | |
111 | } | |
112 | ||
113 | static int link_set_can(Link *link) { | |
114 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
74f0fb90 | 115 | struct can_ctrlmode cm = {}; |
3ddcbeea YW |
116 | int r; |
117 | ||
118 | assert(link); | |
119 | assert(link->network); | |
120 | assert(link->manager); | |
121 | assert(link->manager->rtnl); | |
122 | ||
123 | log_link_debug(link, "Configuring CAN link."); | |
124 | ||
125 | r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex); | |
126 | if (r < 0) | |
127 | return log_link_error_errno(link, r, "Failed to allocate netlink message: %m"); | |
128 | ||
129 | r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK); | |
130 | if (r < 0) | |
131 | return log_link_error_errno(link, r, "Could not set netlink flags: %m"); | |
132 | ||
133 | r = sd_netlink_message_open_container(m, IFLA_LINKINFO); | |
134 | if (r < 0) | |
135 | return log_link_error_errno(link, r, "Failed to open netlink container: %m"); | |
136 | ||
137 | r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind); | |
138 | if (r < 0) | |
139 | return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); | |
140 | ||
141 | if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) { | |
142 | struct can_bittiming bt = { | |
143 | .bitrate = link->network->can_bitrate, | |
144 | .sample_point = link->network->can_sample_point, | |
145 | }; | |
146 | ||
3ddcbeea YW |
147 | log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate); |
148 | if (link->network->can_sample_point > 0) | |
149 | log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10); | |
150 | else | |
151 | log_link_debug(link, "Using default sample point"); | |
152 | ||
153 | r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt)); | |
154 | if (r < 0) | |
155 | return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m"); | |
156 | } | |
157 | ||
7e025e9c RP |
158 | if (link->network->can_data_bitrate > 0 || link->network->can_data_sample_point > 0) { |
159 | struct can_bittiming bt = { | |
160 | .bitrate = link->network->can_data_bitrate, | |
161 | .sample_point = link->network->can_data_sample_point, | |
162 | }; | |
163 | ||
164 | log_link_debug(link, "Setting data bitrate = %d bit/s", bt.bitrate); | |
165 | if (link->network->can_data_sample_point > 0) | |
166 | log_link_debug(link, "Setting data sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10); | |
167 | else | |
168 | log_link_debug(link, "Using default data sample point"); | |
169 | ||
170 | r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt)); | |
171 | if (r < 0) | |
172 | return log_link_error_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m"); | |
173 | } | |
174 | ||
175 | if (link->network->can_fd_mode >= 0) { | |
176 | cm.mask |= CAN_CTRLMODE_FD; | |
177 | SET_FLAG(cm.flags, CAN_CTRLMODE_FD, link->network->can_fd_mode > 0); | |
178 | log_link_debug(link, "%sabling FD mode", link->network->can_fd_mode > 0 ? "En" : "Dis"); | |
179 | } | |
180 | ||
181 | if (link->network->can_non_iso >= 0) { | |
182 | cm.mask |= CAN_CTRLMODE_FD_NON_ISO; | |
183 | SET_FLAG(cm.flags, CAN_CTRLMODE_FD_NON_ISO, link->network->can_non_iso > 0); | |
184 | log_link_debug(link, "%sabling FD non-ISO mode", link->network->can_non_iso > 0 ? "En" : "Dis"); | |
185 | } | |
186 | ||
3ddcbeea YW |
187 | if (link->network->can_restart_us > 0) { |
188 | char time_string[FORMAT_TIMESPAN_MAX]; | |
189 | uint64_t restart_ms; | |
190 | ||
191 | if (link->network->can_restart_us == USEC_INFINITY) | |
192 | restart_ms = 0; | |
193 | else | |
194 | restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC); | |
195 | ||
196 | format_timespan(time_string, FORMAT_TIMESPAN_MAX, restart_ms * 1000, MSEC_PER_SEC); | |
197 | ||
5a9494be YW |
198 | if (restart_ms > UINT32_MAX) |
199 | return log_link_error_errno(link, SYNTHETIC_ERRNO(ERANGE), "restart timeout (%s) too big.", time_string); | |
3ddcbeea YW |
200 | |
201 | log_link_debug(link, "Setting restart = %s", time_string); | |
202 | ||
203 | r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms); | |
204 | if (r < 0) | |
205 | return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m"); | |
206 | } | |
207 | ||
208 | if (link->network->can_triple_sampling >= 0) { | |
74f0fb90 YW |
209 | cm.mask |= CAN_CTRLMODE_3_SAMPLES; |
210 | SET_FLAG(cm.flags, CAN_CTRLMODE_3_SAMPLES, link->network->can_triple_sampling); | |
3ddcbeea | 211 | log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis"); |
74f0fb90 YW |
212 | } |
213 | ||
214 | if (link->network->can_listen_only >= 0) { | |
215 | cm.mask |= CAN_CTRLMODE_LISTENONLY; | |
216 | SET_FLAG(cm.flags, CAN_CTRLMODE_LISTENONLY, link->network->can_listen_only); | |
217 | log_link_debug(link, "%sabling listen-only mode", link->network->can_listen_only ? "En" : "Dis"); | |
218 | } | |
3ddcbeea | 219 | |
74f0fb90 | 220 | if (cm.mask != 0) { |
3ddcbeea YW |
221 | r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); |
222 | if (r < 0) | |
223 | return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m"); | |
224 | } | |
225 | ||
52aa38f1 MR |
226 | if (link->network->can_termination >= 0) { |
227 | ||
228 | log_link_debug(link, "%sabling can-termination", link->network->can_termination ? "En" : "Dis"); | |
229 | ||
230 | r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION, | |
231 | link->network->can_termination ? CAN_TERMINATION_OHM_VALUE : 0); | |
232 | if (r < 0) | |
233 | return log_link_error_errno(link, r, "Could not append IFLA_CAN_TERMINATION attribute: %m"); | |
234 | ||
235 | } | |
236 | ||
3ddcbeea YW |
237 | r = sd_netlink_message_close_container(m); |
238 | if (r < 0) | |
239 | return log_link_error_errno(link, r, "Failed to close netlink container: %m"); | |
240 | ||
241 | r = sd_netlink_message_close_container(m); | |
242 | if (r < 0) | |
243 | return log_link_error_errno(link, r, "Failed to close netlink container: %m"); | |
244 | ||
245 | r = netlink_call_async(link->manager->rtnl, NULL, m, link_set_handler, | |
246 | link_netlink_destroy_callback, link); | |
247 | if (r < 0) | |
248 | return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); | |
249 | ||
250 | link_ref(link); | |
251 | ||
252 | if (!(link->flags & IFF_UP)) | |
253 | return link_up_can(link); | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
258 | static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { | |
259 | int r; | |
260 | ||
261 | assert(link); | |
262 | ||
263 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
264 | return 1; | |
265 | ||
266 | r = sd_netlink_message_get_errno(m); | |
267 | if (r < 0) { | |
5ecb131d | 268 | log_link_message_warning_errno(link, m, r, "Could not bring down interface"); |
3ddcbeea YW |
269 | link_enter_failed(link); |
270 | return 1; | |
271 | } | |
272 | ||
273 | r = link_set_can(link); | |
274 | if (r < 0) | |
275 | link_enter_failed(link); | |
276 | ||
277 | return 1; | |
278 | } | |
279 | ||
280 | int link_configure_can(Link *link) { | |
281 | int r; | |
282 | ||
af9ba57a YW |
283 | link_set_state(link, LINK_STATE_CONFIGURING); |
284 | ||
3ddcbeea YW |
285 | if (streq_ptr(link->kind, "can")) { |
286 | /* The CAN interface must be down to configure bitrate, etc... */ | |
287 | if ((link->flags & IFF_UP)) { | |
288 | r = link_down(link, link_down_handler); | |
289 | if (r < 0) { | |
290 | link_enter_failed(link); | |
291 | return r; | |
292 | } | |
293 | } else { | |
294 | r = link_set_can(link); | |
295 | if (r < 0) { | |
296 | link_enter_failed(link); | |
297 | return r; | |
298 | } | |
299 | } | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | if (!(link->flags & IFF_UP)) { | |
305 | r = link_up_can(link); | |
306 | if (r < 0) { | |
307 | link_enter_failed(link); | |
308 | return r; | |
309 | } | |
310 | } | |
311 | ||
312 | return 0; | |
313 | } |