// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) B.A.T.M.A.N. contributors: * * Antonio Quartulli * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bat-hosts.h" #include "batadv_packet.h" #include "batman_adv.h" #include "functions.h" #include "genl.h" #include "netlink.h" static struct ether_addr *dst_mac; static struct state *tp_state; struct tp_result { int error; uint32_t cookie; uint8_t return_value; uint8_t found:1; uint32_t test_time; uint64_t total_bytes; }; struct tp_cookie { int error; uint8_t found:1; uint32_t cookie; }; static int tpmeter_nl_print_error(struct sockaddr_nl *nla __maybe_unused, struct nlmsgerr *nlerr, void *arg) { struct tp_result *result = arg; if (nlerr->error != -EOPNOTSUPP) fprintf(stderr, "Error received: %s\n", strerror(-nlerr->error)); result->error = nlerr->error; return NL_STOP; } static int tp_meter_result_callback(struct nl_msg *msg, void *arg) { struct tp_result *result = arg; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[NUM_BATADV_ATTR]; struct genlmsghdr *ghdr; uint32_t cookie; if (!genlmsg_valid_hdr(nlh, 0)) { result->error = -EINVAL; return NL_STOP; } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_TP_METER) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); result->error = -EINVAL; return NL_STOP; } if (!attrs[BATADV_ATTR_TPMETER_COOKIE]) { result->error = -EINVAL; return NL_STOP; } if (!attrs[BATADV_ATTR_TPMETER_RESULT]) return NL_OK; cookie = nla_get_u32(attrs[BATADV_ATTR_TPMETER_COOKIE]); if (cookie != result->cookie) return NL_OK; result->found = true; result->return_value = nla_get_u8(attrs[BATADV_ATTR_TPMETER_RESULT]); if (attrs[BATADV_ATTR_TPMETER_TEST_TIME]) result->test_time = nla_get_u32(attrs[BATADV_ATTR_TPMETER_TEST_TIME]); if (attrs[BATADV_ATTR_TPMETER_BYTES]) result->total_bytes = nla_get_u64(attrs[BATADV_ATTR_TPMETER_BYTES]); return NL_OK; } static int tp_meter_cookie_callback(struct nl_msg *msg, void *arg) { struct tp_cookie *cookie = arg; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[NUM_BATADV_ATTR]; struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr(nlh, 0)) { cookie->error = -EINVAL; return NL_STOP; } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_TP_METER) { cookie->error = -EINVAL; return NL_STOP; } if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); cookie->error = -EINVAL; return NL_STOP; } if (!attrs[BATADV_ATTR_TPMETER_COOKIE]) { cookie->error = -EINVAL; return NL_STOP; } cookie->cookie = nla_get_u32(attrs[BATADV_ATTR_TPMETER_COOKIE]); cookie->found = true; return NL_OK; } static int tp_meter_start(struct state *state, struct ether_addr *dst_mac, uint32_t time, struct tp_cookie *cookie) { struct nl_msg *msg; struct nl_cb *cb; int err = 0; cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, tp_meter_cookie_callback, cookie); nl_cb_err(cb, NL_CB_CUSTOM, tpmeter_nl_print_error, cookie); msg = nlmsg_alloc(); if (!msg) return -ENOMEM; genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, state->batadv_family, 0, 0, BATADV_CMD_TP_METER, 1); nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, state->mesh_ifindex); nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst_mac); nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, time); nl_send_auto_complete(state->sock, msg); nlmsg_free(msg); nl_recvmsgs(state->sock, cb); nl_cb_put(cb); if (cookie->error < 0) err = cookie->error; else if (!cookie->found) err= -EINVAL; return err; } static int no_seq_check(struct nl_msg *msg __maybe_unused, void *arg __maybe_unused) { return NL_OK; } static int tp_recv_result(struct nl_sock *sock, struct tp_result *result) { int err = 0; struct nl_cb *cb; cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, tp_meter_result_callback, result); nl_cb_err(cb, NL_CB_CUSTOM, tpmeter_nl_print_error, result); while (result->error == 0 && !result->found) nl_recvmsgs(sock, cb); nl_cb_put(cb); if (result->error < 0) err = result->error; else if (!result->found) err= -EINVAL; return err; } static int tp_meter_stop(struct state *state, struct ether_addr *dst_mac) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, state->batadv_family, 0, 0, BATADV_CMD_TP_METER_CANCEL, 1); nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, state->mesh_ifindex); nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst_mac); nl_send_auto_complete(state->sock, msg); nlmsg_free(msg); return 0; } static struct nl_sock *tp_prepare_listening_sock(void) { struct nl_sock *sock; int family; int ret; int mcid; sock = nl_socket_alloc(); if (!sock) return NULL; ret = genl_connect(sock); if (ret < 0) { fprintf(stderr, "Failed to connect to generic netlink: %d\n", ret); goto err; } family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { fprintf(stderr, "Failed to resolve batman-adv netlink: %d\n", family); goto err; } mcid = nl_get_multicast_id(sock, BATADV_NL_NAME, BATADV_NL_MCAST_GROUP_TPMETER); if (mcid < 0) { fprintf(stderr, "Failed to resolve batman-adv tpmeter multicast group: %d\n", mcid); goto err; } ret = nl_socket_add_membership(sock, mcid); if (ret) { fprintf(stderr, "Failed to join batman-adv tpmeter multicast group: %d\n", ret); goto err; } return sock; err: nl_socket_free(sock); return NULL; } void tp_sig_handler(int sig) { switch (sig) { case SIGINT: case SIGTERM: fflush(stdout); tp_meter_stop(tp_state, dst_mac); break; default: break; } } static void tp_meter_usage(void) { fprintf(stderr, "Usage: batctl tp [parameters] \n"); fprintf(stderr, "Parameters:\n"); fprintf(stderr, "\t -t