1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <net/if.h>
4 #include <linux/can/netlink.h>
5 
6 #include "networkd-can.h"
7 #include "networkd-link.h"
8 #include "networkd-network.h"
9 #include "networkd-setlink.h"
10 #include "parse-util.h"
11 #include "string-util.h"
12 
13 #define CAN_TERMINATION_DEFAULT_OHM_VALUE 120
14 
can_set_netlink_message(Link * link,sd_netlink_message * m)15 int can_set_netlink_message(Link *link, sd_netlink_message *m) {
16         int r;
17 
18         assert(link);
19         assert(link->network);
20         assert(m);
21 
22         r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK);
23         if (r < 0)
24                 return r;
25 
26         r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
27         if (r < 0)
28                 return r;
29 
30         r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind);
31         if (r < 0)
32                 return r;
33 
34         if (link->network->can_bitrate > 0) {
35                 struct can_bittiming bt = {
36                         .bitrate = link->network->can_bitrate,
37                         .sample_point = link->network->can_sample_point,
38                         .sjw = link->network->can_sync_jump_width,
39                 };
40 
41                 log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
42                 if (link->network->can_sample_point > 0)
43                         log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
44                 else
45                         log_link_debug(link, "Using default sample point");
46 
47                 r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
48                 if (r < 0)
49                         return r;
50         } else if (link->network->can_time_quanta_ns > 0) {
51                 struct can_bittiming bt = {
52                         .tq = link->network->can_time_quanta_ns,
53                         .prop_seg = link->network->can_propagation_segment,
54                         .phase_seg1 = link->network->can_phase_buffer_segment_1,
55                         .phase_seg2 = link->network->can_phase_buffer_segment_2,
56                         .sjw = link->network->can_sync_jump_width,
57                 };
58 
59                 log_link_debug(link, "Setting time quanta = %"PRIu32" nsec", bt.tq);
60                 r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
61                 if (r < 0)
62                         return r;
63         }
64 
65         if (link->network->can_data_bitrate > 0) {
66                 struct can_bittiming bt = {
67                         .bitrate = link->network->can_data_bitrate,
68                         .sample_point = link->network->can_data_sample_point,
69                         .sjw = link->network->can_data_sync_jump_width,
70                 };
71 
72                 log_link_debug(link, "Setting data bitrate = %d bit/s", bt.bitrate);
73                 if (link->network->can_data_sample_point > 0)
74                         log_link_debug(link, "Setting data sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
75                 else
76                         log_link_debug(link, "Using default data sample point");
77 
78                 r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt));
79                 if (r < 0)
80                         return r;
81         } else if (link->network->can_data_time_quanta_ns > 0) {
82                 struct can_bittiming bt = {
83                         .tq = link->network->can_data_time_quanta_ns,
84                         .prop_seg = link->network->can_data_propagation_segment,
85                         .phase_seg1 = link->network->can_data_phase_buffer_segment_1,
86                         .phase_seg2 = link->network->can_data_phase_buffer_segment_2,
87                         .sjw = link->network->can_data_sync_jump_width,
88                 };
89 
90                 log_link_debug(link, "Setting data time quanta = %"PRIu32" nsec", bt.tq);
91                 r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt));
92                 if (r < 0)
93                         return r;
94         }
95 
96         if (link->network->can_restart_us > 0) {
97                 uint64_t restart_ms;
98 
99                 if (link->network->can_restart_us == USEC_INFINITY)
100                         restart_ms = 0;
101                 else
102                         restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC);
103 
104                 log_link_debug(link, "Setting restart = %s", FORMAT_TIMESPAN(restart_ms * 1000, MSEC_PER_SEC));
105                 r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms);
106                 if (r < 0)
107                         return r;
108         }
109 
110         if (link->network->can_control_mode_mask != 0) {
111                 struct can_ctrlmode cm = {
112                         .mask = link->network->can_control_mode_mask,
113                         .flags = link->network->can_control_mode_flags,
114                 };
115 
116                 r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
117                 if (r < 0)
118                         return r;
119         }
120 
121         if (link->network->can_termination_set) {
122                 log_link_debug(link, "Setting can-termination to '%u'.", link->network->can_termination);
123 
124                 r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION, link->network->can_termination);
125                 if (r < 0)
126                         return r;
127         }
128 
129         r = sd_netlink_message_close_container(m);
130         if (r < 0)
131                 return r;
132 
133         r = sd_netlink_message_close_container(m);
134         if (r < 0)
135                 return r;
136 
137         return 0;
138 }
139 
config_parse_can_bitrate(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)140 int config_parse_can_bitrate(
141                 const char* unit,
142                 const char *filename,
143                 unsigned line,
144                 const char *section,
145                 unsigned section_line,
146                 const char *lvalue,
147                 int ltype,
148                 const char *rvalue,
149                 void *data,
150                 void *userdata) {
151 
152         uint32_t *br = data;
153         uint64_t sz;
154         int r;
155 
156         assert(filename);
157         assert(lvalue);
158         assert(rvalue);
159         assert(data);
160 
161         r = parse_size(rvalue, 1000, &sz);
162         if (r < 0) {
163                 log_syntax(unit, LOG_WARNING, filename, line, r,
164                            "Failed to parse can bitrate '%s', ignoring: %m", rvalue);
165                 return 0;
166         }
167 
168         /* Linux uses __u32 for bitrates, so the value should not exceed that. */
169         if (sz <= 0 || sz > UINT32_MAX) {
170                 log_syntax(unit, LOG_WARNING, filename, line, 0,
171                            "Bit rate out of permitted range 1...4294967295");
172                 return 0;
173         }
174 
175         *br = (uint32_t) sz;
176 
177         return 0;
178 }
179 
config_parse_can_time_quanta(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)180 int config_parse_can_time_quanta(
181                 const char* unit,
182                 const char *filename,
183                 unsigned line,
184                 const char *section,
185                 unsigned section_line,
186                 const char *lvalue,
187                 int ltype,
188                 const char *rvalue,
189                 void *data,
190                 void *userdata) {
191 
192         nsec_t val, *tq = data;
193         int r;
194 
195         assert(filename);
196         assert(lvalue);
197         assert(rvalue);
198         assert(data);
199 
200         r = parse_nsec(rvalue, &val);
201         if (r < 0) {
202                 log_syntax(unit, LOG_WARNING, filename, line, r,
203                            "Failed to parse can time quanta '%s', ignoring: %m", rvalue);
204                 return 0;
205         }
206 
207         /* Linux uses __u32 for bitrates, so the value should not exceed that. */
208         if (val <= 0 || val > UINT32_MAX) {
209                 log_syntax(unit, LOG_WARNING, filename, line, 0,
210                            "Time quanta out of permitted range 1...4294967295");
211                 return 0;
212         }
213 
214         *tq = val;
215         return 0;
216 }
217 
config_parse_can_restart_usec(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)218 int config_parse_can_restart_usec(
219                 const char* unit,
220                 const char *filename,
221                 unsigned line,
222                 const char *section,
223                 unsigned section_line,
224                 const char *lvalue,
225                 int ltype,
226                 const char *rvalue,
227                 void *data,
228                 void *userdata) {
229 
230         usec_t usec, *restart_usec = data;
231         int r;
232 
233         assert(filename);
234         assert(lvalue);
235         assert(rvalue);
236         assert(data);
237 
238         r = parse_sec(rvalue, &usec);
239         if (r < 0) {
240                 log_syntax(unit, LOG_WARNING, filename, line, r,
241                            "Failed to parse CAN restart sec '%s', ignoring: %m", rvalue);
242                 return 0;
243         }
244 
245         if (usec != USEC_INFINITY &&
246             DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
247                 log_syntax(unit, LOG_WARNING, filename, line, 0,
248                            "CAN RestartSec= must be in the range 0...%"PRIu32"ms, ignoring: %s", UINT32_MAX, rvalue);
249                 return 0;
250         }
251 
252         *restart_usec = usec;
253         return 0;
254 }
255 
config_parse_can_control_mode(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)256 int config_parse_can_control_mode(
257                 const char* unit,
258                 const char *filename,
259                 unsigned line,
260                 const char *section,
261                 unsigned section_line,
262                 const char *lvalue,
263                 int ltype,
264                 const char *rvalue,
265                 void *data,
266                 void *userdata) {
267 
268         Network *network = userdata;
269         uint32_t mask = ltype;
270         int r;
271 
272         assert(filename);
273         assert(lvalue);
274         assert(rvalue);
275         assert(userdata);
276         assert(mask != 0);
277 
278         if (isempty(rvalue)) {
279                 network->can_control_mode_mask &= ~mask;
280                 network->can_control_mode_flags &= ~mask;
281                 return 0;
282         }
283 
284         r = parse_boolean(rvalue);
285         if (r < 0) {
286                 log_syntax(unit, LOG_WARNING, filename, line, r,
287                            "Failed to parse CAN control mode '%s', ignoring: %s", lvalue, rvalue);
288                 return 0;
289         }
290 
291         network->can_control_mode_mask |= mask;
292         SET_FLAG(network->can_control_mode_flags, mask, r);
293         return 0;
294 }
295 
config_parse_can_termination(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)296 int config_parse_can_termination(
297                 const char* unit,
298                 const char *filename,
299                 unsigned line,
300                 const char *section,
301                 unsigned section_line,
302                 const char *lvalue,
303                 int ltype,
304                 const char *rvalue,
305                 void *data,
306                 void *userdata) {
307 
308         Network *network = userdata;
309         int r;
310 
311         assert(filename);
312         assert(lvalue);
313         assert(rvalue);
314         assert(data);
315 
316         if (isempty(rvalue)) {
317                 network->can_termination_set = false;
318                 return 0;
319         }
320 
321         /* Note that 0 termination ohm value means no termination resistor, and there is no conflict
322          * between parse_boolean() and safe_atou16() when Termination=0. However, Termination=1 must be
323          * treated as 1 ohm, instead of true (and then the default ohm value). So, we need to parse the
324          * string with safe_atou16() at first. */
325 
326         r = safe_atou16(rvalue, &network->can_termination);
327         if (r < 0) {
328                 r = parse_boolean(rvalue);
329                 if (r < 0) {
330                         log_syntax(unit, LOG_WARNING, filename, line, r,
331                                    "Failed to parse CAN termination value, ignoring: %s", rvalue);
332                         return 0;
333                 }
334 
335                 network->can_termination = r ? CAN_TERMINATION_DEFAULT_OHM_VALUE : 0;
336         }
337 
338         network->can_termination_set = true;
339         return 0;
340 }
341