• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * cable_test.c - netlink implementation of cable test command
3  *
4  * Implementation of ethtool --cable-test <dev>
5  */
6 
7 #include <errno.h>
8 #include <string.h>
9 #include <stdio.h>
10 
11 #include "../internal.h"
12 #include "../common.h"
13 #include "netlink.h"
14 #include "parser.h"
15 
16 struct cable_test_context {
17 	bool breakout;
18 };
19 
nl_get_cable_test_result(const struct nlattr * nest,uint8_t * pair,uint16_t * code,uint32_t * src)20 static int nl_get_cable_test_result(const struct nlattr *nest, uint8_t *pair,
21 				    uint16_t *code, uint32_t *src)
22 {
23 	const struct nlattr *tb[ETHTOOL_A_CABLE_RESULT_MAX+1] = {};
24 	DECLARE_ATTR_TB_INFO(tb);
25 	int ret;
26 
27 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
28 	if (ret < 0 ||
29 	    !tb[ETHTOOL_A_CABLE_RESULT_PAIR] ||
30 	    !tb[ETHTOOL_A_CABLE_RESULT_CODE])
31 		return -EFAULT;
32 
33 	*pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_PAIR]);
34 	*code = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_CODE]);
35 	if (tb[ETHTOOL_A_CABLE_RESULT_SRC])
36 		*src = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_RESULT_SRC]);
37 
38 	return 0;
39 }
40 
nl_get_cable_test_fault_length(const struct nlattr * nest,uint8_t * pair,unsigned int * cm,uint32_t * src)41 static int nl_get_cable_test_fault_length(const struct nlattr *nest,
42 					  uint8_t *pair, unsigned int *cm,
43 					  uint32_t *src)
44 {
45 	const struct nlattr *tb[ETHTOOL_A_CABLE_FAULT_LENGTH_MAX+1] = {};
46 	DECLARE_ATTR_TB_INFO(tb);
47 	int ret;
48 
49 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
50 	if (ret < 0 ||
51 	    !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR] ||
52 	    !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM])
53 		return -EFAULT;
54 
55 	*pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR]);
56 	*cm = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM]);
57 	if (tb[ETHTOOL_A_CABLE_FAULT_LENGTH_SRC])
58 		*src = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_SRC]);
59 
60 	return 0;
61 }
62 
nl_code2txt(uint16_t code)63 static char *nl_code2txt(uint16_t code)
64 {
65 	switch (code) {
66 	case ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC:
67 	default:
68 		return "Unknown";
69 	case ETHTOOL_A_CABLE_RESULT_CODE_OK:
70 		return "OK";
71 	case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
72 		return "Open Circuit";
73 	case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
74 		return "Short within Pair";
75 	case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
76 		return "Short to another pair";
77 	}
78 }
79 
nl_pair2txt(uint8_t pair)80 static char *nl_pair2txt(uint8_t pair)
81 {
82 	switch (pair) {
83 	case ETHTOOL_A_CABLE_PAIR_A:
84 		return "Pair A";
85 	case ETHTOOL_A_CABLE_PAIR_B:
86 		return "Pair B";
87 	case ETHTOOL_A_CABLE_PAIR_C:
88 		return "Pair C";
89 	case ETHTOOL_A_CABLE_PAIR_D:
90 		return "Pair D";
91 	default:
92 		return "Unexpected pair";
93 	}
94 }
95 
nl_src2txt(uint32_t src)96 static char *nl_src2txt(uint32_t src)
97 {
98 	switch (src) {
99 	case ETHTOOL_A_CABLE_INF_SRC_TDR:
100 		return "TDR";
101 	case ETHTOOL_A_CABLE_INF_SRC_ALCD:
102 		return "ALCD";
103 	default:
104 		return "Unknown";
105 	}
106 }
107 
nl_cable_test_ntf_attr(struct nlattr * evattr)108 static int nl_cable_test_ntf_attr(struct nlattr *evattr)
109 {
110 	unsigned int cm;
111 	uint32_t src = UINT32_MAX;
112 	uint16_t code;
113 	uint8_t pair;
114 	int ret;
115 
116 	switch (mnl_attr_get_type(evattr)) {
117 	case ETHTOOL_A_CABLE_NEST_RESULT:
118 		ret = nl_get_cable_test_result(evattr, &pair, &code, &src);
119 		if (ret < 0)
120 			return ret;
121 
122 		open_json_object(NULL);
123 		print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
124 		print_string(PRINT_ANY, "code", "code %s", nl_code2txt(code));
125 		if (src != UINT32_MAX)
126 			print_string(PRINT_ANY, "src", ", source: %s",
127 				     nl_src2txt(src));
128 		print_nl();
129 		close_json_object();
130 		break;
131 
132 	case ETHTOOL_A_CABLE_NEST_FAULT_LENGTH:
133 		ret = nl_get_cable_test_fault_length(evattr, &pair, &cm, &src);
134 		if (ret < 0)
135 			return ret;
136 		open_json_object(NULL);
137 		print_string(PRINT_ANY, "pair", "%s, ", nl_pair2txt(pair));
138 		print_float(PRINT_ANY, "length", "fault length: %0.2fm",
139 			    (float)cm / 100);
140 		if (src != UINT32_MAX)
141 			print_string(PRINT_ANY, "src", ", source: %s",
142 				     nl_src2txt(src));
143 		print_nl();
144 		close_json_object();
145 		break;
146 	}
147 	return 0;
148 }
149 
cable_test_ntf_nest(const struct nlattr * nest)150 static void cable_test_ntf_nest(const struct nlattr *nest)
151 {
152 	struct nlattr *pos;
153 	int ret;
154 
155 	mnl_attr_for_each_nested(pos, nest) {
156 		ret = nl_cable_test_ntf_attr(pos);
157 		if (ret < 0)
158 			return;
159 	}
160 }
161 
162 /* Returns MNL_CB_STOP when the test is complete. Used when executing
163  * a test, but not suitable for monitor.
164  */
cable_test_ntf_stop_cb(const struct nlmsghdr * nlhdr,void * data)165 static int cable_test_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
166 {
167 	const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_NTF_MAX + 1] = {};
168 	u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
169 	struct cable_test_context *ctctx;
170 	struct nl_context *nlctx = data;
171 	DECLARE_ATTR_TB_INFO(tb);
172 	bool silent;
173 	int err_ret;
174 	int ret;
175 
176 	ctctx = nlctx->cmd_private;
177 
178 	silent = nlctx->is_dump || nlctx->is_monitor;
179 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
180 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
181 	if (ret < 0)
182 		return err_ret;
183 
184 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_HEADER]);
185 	if (!dev_ok(nlctx))
186 		return err_ret;
187 
188 	if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
189 		status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
190 
191 	switch (status) {
192 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
193 		print_string(PRINT_FP, "status",
194 			     "Cable test started for device %s.\n",
195 			     nlctx->devname);
196 		break;
197 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
198 		print_string(PRINT_FP, "status",
199 			     "Cable test completed for device %s.\n",
200 			     nlctx->devname);
201 		break;
202 	default:
203 		break;
204 	}
205 
206 	if (tb[ETHTOOL_A_CABLE_TEST_NTF_NEST])
207 		cable_test_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_NTF_NEST]);
208 
209 	if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
210 		if (ctctx)
211 			ctctx->breakout = true;
212 		return MNL_CB_STOP;
213 	}
214 
215 	return MNL_CB_OK;
216 }
217 
218 /* Wrapper around cable_test_ntf_stop_cb() which does not return STOP,
219  * used for monitor
220  */
cable_test_ntf_cb(const struct nlmsghdr * nlhdr,void * data)221 int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
222 {
223 	int status = cable_test_ntf_stop_cb(nlhdr, data);
224 
225 	if (status == MNL_CB_STOP)
226 		status = MNL_CB_OK;
227 
228 	return status;
229 }
230 
nl_cable_test_results_cb(const struct nlmsghdr * nlhdr,void * data)231 static int nl_cable_test_results_cb(const struct nlmsghdr *nlhdr, void *data)
232 {
233 	const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
234 
235 	if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_NTF)
236 		return MNL_CB_OK;
237 
238 	return cable_test_ntf_stop_cb(nlhdr, data);
239 }
240 
241 /* Receive the broadcasted messages until we get the cable test
242  * results
243  */
nl_cable_test_process_results(struct cmd_context * ctx)244 static int nl_cable_test_process_results(struct cmd_context *ctx)
245 {
246 	struct nl_context *nlctx = ctx->nlctx;
247 	struct nl_socket *nlsk = nlctx->ethnl_socket;
248 	struct cable_test_context ctctx;
249 	int err;
250 
251 	nlctx->is_monitor = true;
252 	nlsk->port = 0;
253 	nlsk->seq = 0;
254 	nlctx->filter_devname = ctx->devname;
255 
256 	ctctx.breakout = false;
257 	nlctx->cmd_private = &ctctx;
258 
259 	while (!ctctx.breakout) {
260 		err = nlsock_process_reply(nlsk, nl_cable_test_results_cb,
261 					   nlctx);
262 		if (err)
263 			return err;
264 	}
265 
266 	return err;
267 }
268 
nl_cable_test(struct cmd_context * ctx)269 int nl_cable_test(struct cmd_context *ctx)
270 {
271 	struct nl_context *nlctx = ctx->nlctx;
272 	struct nl_socket *nlsk = nlctx->ethnl_socket;
273 	uint32_t grpid = nlctx->ethnl_mongrp;
274 	int ret;
275 
276 	/* Join the multicast group so we can receive the results in a
277 	 * race free way.
278 	 */
279 	if (!grpid) {
280 		fprintf(stderr, "multicast group 'monitor' not found\n");
281 		return -EOPNOTSUPP;
282 	}
283 
284 	ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
285 				    &grpid, sizeof(grpid));
286 	if (ret < 0)
287 		return ret;
288 
289 	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CABLE_TEST_ACT,
290 				      ETHTOOL_A_CABLE_TEST_HEADER, 0);
291 	if (ret < 0)
292 		return ret;
293 
294 	ret = nlsock_sendmsg(nlsk, NULL);
295 	if (ret < 0)
296 		fprintf(stderr, "Cannot start cable test\n");
297 	else {
298 		new_json_obj(ctx->json);
299 
300 		ret = nl_cable_test_process_results(ctx);
301 
302 		delete_json_obj();
303 	}
304 
305 	return ret;
306 }
307 
nl_get_cable_test_tdr_amplitude(const struct nlattr * nest,uint8_t * pair,int16_t * mV)308 static int nl_get_cable_test_tdr_amplitude(const struct nlattr *nest,
309 					   uint8_t *pair, int16_t *mV)
310 {
311 	const struct nlattr *tb[ETHTOOL_A_CABLE_AMPLITUDE_MAX+1] = {};
312 	DECLARE_ATTR_TB_INFO(tb);
313 	uint16_t mV_unsigned;
314 	int ret;
315 
316 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
317 	if (ret < 0 ||
318 	    !tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR] ||
319 	    !tb[ETHTOOL_A_CABLE_AMPLITUDE_mV])
320 		return -EFAULT;
321 
322 	*pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR]);
323 	mV_unsigned = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_AMPLITUDE_mV]);
324 	*mV = (int16_t)(mV_unsigned);
325 
326 	return 0;
327 }
328 
nl_get_cable_test_tdr_pulse(const struct nlattr * nest,uint16_t * mV)329 static int nl_get_cable_test_tdr_pulse(const struct nlattr *nest, uint16_t *mV)
330 {
331 	const struct nlattr *tb[ETHTOOL_A_CABLE_PULSE_MAX+1] = {};
332 	DECLARE_ATTR_TB_INFO(tb);
333 	int ret;
334 
335 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
336 	if (ret < 0 ||
337 	    !tb[ETHTOOL_A_CABLE_PULSE_mV])
338 		return -EFAULT;
339 
340 	*mV = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_PULSE_mV]);
341 
342 	return 0;
343 }
344 
nl_get_cable_test_tdr_step(const struct nlattr * nest,uint32_t * first,uint32_t * last,uint32_t * step)345 static int nl_get_cable_test_tdr_step(const struct nlattr *nest,
346 				      uint32_t *first, uint32_t *last,
347 				      uint32_t *step)
348 {
349 	const struct nlattr *tb[ETHTOOL_A_CABLE_STEP_MAX+1] = {};
350 	DECLARE_ATTR_TB_INFO(tb);
351 	int ret;
352 
353 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
354 	if (ret < 0 ||
355 	    !tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE] ||
356 	    !tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE] ||
357 	    !tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE])
358 		return -EFAULT;
359 
360 	*first = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE]);
361 	*last = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE]);
362 	*step = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE]);
363 
364 	return 0;
365 }
366 
nl_cable_test_tdr_ntf_attr(struct nlattr * evattr)367 static int nl_cable_test_tdr_ntf_attr(struct nlattr *evattr)
368 {
369 	uint32_t first, last, step;
370 	uint8_t pair;
371 	int ret;
372 
373 	switch (mnl_attr_get_type(evattr)) {
374 	case ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE: {
375 		int16_t mV;
376 
377 		ret = nl_get_cable_test_tdr_amplitude(
378 			evattr, &pair, &mV);
379 		if (ret < 0)
380 			return ret;
381 
382 		open_json_object(NULL);
383 		print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
384 		print_int(PRINT_ANY, "amplitude", "Amplitude %4d\n", mV);
385 		close_json_object();
386 		break;
387 	}
388 	case ETHTOOL_A_CABLE_TDR_NEST_PULSE: {
389 		uint16_t mV;
390 
391 		ret = nl_get_cable_test_tdr_pulse(evattr, &mV);
392 		if (ret < 0)
393 			return ret;
394 
395 		open_json_object(NULL);
396 		print_uint(PRINT_ANY, "pulse", "TDR Pulse %dmV\n", mV);
397 		close_json_object();
398 		break;
399 	}
400 	case ETHTOOL_A_CABLE_TDR_NEST_STEP:
401 		ret = nl_get_cable_test_tdr_step(evattr, &first, &last, &step);
402 		if (ret < 0)
403 			return ret;
404 
405 		open_json_object(NULL);
406 		print_float(PRINT_ANY, "first", "Step configuration: %.2f-",
407 			    (float)first / 100);
408 		print_float(PRINT_ANY, "last", "%.2f meters ",
409 			    (float)last / 100);
410 		print_float(PRINT_ANY, "step", "in %.2fm steps\n",
411 			    (float)step / 100);
412 		close_json_object();
413 		break;
414 	}
415 	return 0;
416 }
417 
cable_test_tdr_ntf_nest(const struct nlattr * nest)418 static void cable_test_tdr_ntf_nest(const struct nlattr *nest)
419 {
420 	struct nlattr *pos;
421 	int ret;
422 
423 	mnl_attr_for_each_nested(pos, nest) {
424 		ret = nl_cable_test_tdr_ntf_attr(pos);
425 		if (ret < 0)
426 			return;
427 	}
428 }
429 
430 /* Returns MNL_CB_STOP when the test is complete. Used when executing
431  * a test, but not suitable for monitor.
432  */
cable_test_tdr_ntf_stop_cb(const struct nlmsghdr * nlhdr,void * data)433 int cable_test_tdr_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
434 {
435 	const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX + 1] = {};
436 	u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
437 	struct cable_test_context *ctctx;
438 	struct nl_context *nlctx = data;
439 
440 	DECLARE_ATTR_TB_INFO(tb);
441 	bool silent;
442 	int err_ret;
443 	int ret;
444 
445 	ctctx = nlctx->cmd_private;
446 
447 	silent = nlctx->is_dump || nlctx->is_monitor;
448 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
449 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
450 	if (ret < 0)
451 		return err_ret;
452 
453 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER]);
454 	if (!dev_ok(nlctx))
455 		return err_ret;
456 
457 	if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
458 		status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
459 
460 	switch (status) {
461 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
462 		print_string(PRINT_FP, "status",
463 			     "Cable test TDR started for device %s.\n",
464 			     nlctx->devname);
465 		break;
466 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
467 		print_string(PRINT_FP, "status",
468 			     "Cable test TDR completed for device %s.\n",
469 			     nlctx->devname);
470 		break;
471 	default:
472 		break;
473 	}
474 
475 	if (tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST])
476 		cable_test_tdr_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST]);
477 
478 	if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
479 		if (ctctx)
480 			ctctx->breakout = true;
481 		return MNL_CB_STOP;
482 	}
483 
484 	return MNL_CB_OK;
485 }
486 
487 /* Wrapper around cable_test_tdr_ntf_stop_cb() which does not return
488  * STOP, used for monitor
489  */
cable_test_tdr_ntf_cb(const struct nlmsghdr * nlhdr,void * data)490 int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
491 {
492 	int status = cable_test_tdr_ntf_stop_cb(nlhdr, data);
493 
494 	if (status == MNL_CB_STOP)
495 		status = MNL_CB_OK;
496 
497 	return status;
498 }
499 
nl_cable_test_tdr_results_cb(const struct nlmsghdr * nlhdr,void * data)500 static int nl_cable_test_tdr_results_cb(const struct nlmsghdr *nlhdr,
501 					void *data)
502 {
503 	const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
504 
505 	if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_TDR_NTF)
506 		return MNL_CB_OK;
507 
508 	cable_test_tdr_ntf_cb(nlhdr, data);
509 
510 	return MNL_CB_STOP;
511 }
512 
513 /* Receive the broadcasted messages until we get the cable test
514  * results
515  */
nl_cable_test_tdr_process_results(struct cmd_context * ctx)516 static int nl_cable_test_tdr_process_results(struct cmd_context *ctx)
517 {
518 	struct nl_context *nlctx = ctx->nlctx;
519 	struct nl_socket *nlsk = nlctx->ethnl_socket;
520 	struct cable_test_context ctctx;
521 	int err;
522 
523 	nlctx->is_monitor = true;
524 	nlsk->port = 0;
525 	nlsk->seq = 0;
526 	nlctx->filter_devname = ctx->devname;
527 
528 	ctctx.breakout = false;
529 	nlctx->cmd_private = &ctctx;
530 
531 	while (!ctctx.breakout) {
532 		err = nlsock_process_reply(nlsk, nl_cable_test_tdr_results_cb,
533 					   nlctx);
534 		if (err)
535 			return err;
536 	}
537 
538 	return err;
539 }
540 
541 static const struct param_parser tdr_params[] = {
542 	{
543 		.arg		= "first",
544 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST,
545 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
546 		.handler	= nl_parse_direct_m2cm,
547 	},
548 	{
549 		.arg		= "last",
550 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST,
551 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
552 		.handler	= nl_parse_direct_m2cm,
553 	},
554 	{
555 		.arg		= "step",
556 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP,
557 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
558 		.handler	= nl_parse_direct_m2cm,
559 	},
560 	{
561 		.arg		= "pair",
562 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR,
563 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
564 		.handler	= nl_parse_direct_u8,
565 	},
566 	{}
567 };
568 
nl_cable_test_tdr(struct cmd_context * ctx)569 int nl_cable_test_tdr(struct cmd_context *ctx)
570 {
571 	struct nl_context *nlctx = ctx->nlctx;
572 	struct nl_socket *nlsk = nlctx->ethnl_socket;
573 	uint32_t grpid = nlctx->ethnl_mongrp;
574 	struct nl_msg_buff *msgbuff;
575 	int ret;
576 
577 	nlctx->cmd = "--cable-test-tdr";
578 	nlctx->argp = ctx->argp;
579 	nlctx->argc = ctx->argc;
580 	nlctx->devname = ctx->devname;
581 	msgbuff = &nlsk->msgbuff;
582 
583 	/* Join the multicast group so we can receive the results in a
584 	 * race free way.
585 	 */
586 	if (!grpid) {
587 		fprintf(stderr, "multicast group 'monitor' not found\n");
588 		return -EOPNOTSUPP;
589 	}
590 
591 	ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
592 				    &grpid, sizeof(grpid));
593 	if (ret < 0)
594 		return ret;
595 
596 	ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
597 		       NLM_F_REQUEST | NLM_F_ACK);
598 	if (ret < 0)
599 		return 2;
600 
601 	if (ethnla_fill_header_phy(msgbuff, ETHTOOL_A_CABLE_TEST_TDR_HEADER,
602 				   ctx->devname, ctx->phy_index, 0))
603 		return -EMSGSIZE;
604 
605 	ret = nl_parser(nlctx, tdr_params, NULL, PARSER_GROUP_NEST, NULL);
606 	if (ret < 0)
607 		return ret;
608 
609 	ret = nlsock_sendmsg(nlsk, NULL);
610 	if (ret < 0)
611 		fprintf(stderr, "Cannot start cable test TDR\n");
612 	else {
613 		new_json_obj(ctx->json);
614 
615 		ret = nl_cable_test_tdr_process_results(ctx);
616 
617 		delete_json_obj();
618 	}
619 
620 	return ret;
621 }
622