/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "mac80211_create_radios" #include #include #include #include #include #include enum { HWSIM_CMD_UNSPEC, HWSIM_CMD_REGISTER, HWSIM_CMD_FRAME, HWSIM_CMD_TX_INFO_FRAME, HWSIM_CMD_NEW_RADIO, HWSIM_CMD_DEL_RADIO, HWSIM_CMD_GET_RADIO, }; enum { HWSIM_ATTR_UNSPEC, HWSIM_ATTR_ADDR_RECEIVER, HWSIM_ATTR_ADDR_TRANSMITTER, HWSIM_ATTR_FRAME, HWSIM_ATTR_FLAGS, HWSIM_ATTR_RX_RATE, HWSIM_ATTR_SIGNAL, HWSIM_ATTR_TX_INFO, HWSIM_ATTR_COOKIE, HWSIM_ATTR_CHANNELS, HWSIM_ATTR_RADIO_ID, HWSIM_ATTR_REG_HINT_ALPHA2, HWSIM_ATTR_REG_CUSTOM_REG, HWSIM_ATTR_REG_STRICT_REG, HWSIM_ATTR_SUPPORT_P2P_DEVICE, HWSIM_ATTR_USE_CHANCTX, HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, HWSIM_ATTR_RADIO_NAME, HWSIM_ATTR_NO_VIF, HWSIM_ATTR_FREQ, HWSIM_ATTR_PAD, HWSIM_ATTR_TX_INFO_FLAGS, HWSIM_ATTR_PERM_ADDR, HWSIM_ATTR_IFTYPE_SUPPORT, HWSIM_ATTR_CIPHER_SUPPORT, }; struct nl_sock_deleter { void operator()(struct nl_sock* x) const { nl_socket_free(x); } }; struct nl_msg_deleter { void operator()(struct nl_msg* x) const { nlmsg_free(x); } }; constexpr char kHwSimFamilyName[] = "MAC80211_HWSIM"; constexpr int kHwSimVersion = 1; const char* nlErrStr(const int e) { return (e < 0) ? nl_geterror(e) : ""; } #define RETURN(R) return (R); #define RETURN_ERROR(C, R) \ do { \ ALOGE("%s:%d '%s' failed", __func__, __LINE__, C); \ return (R); \ } while (false); #define RETURN_NL_ERROR(C, NLR, R) \ do { \ ALOGE("%s:%d '%s' failed with '%s'", __func__, __LINE__, C, nlErrStr((NLR))); \ return (R); \ } while (false); int parseInt(const char* str, int* result) { return sscanf(str, "%d", result); } std::unique_ptr createNlMessage( const int family, const int cmd) { std::unique_ptr msg(nlmsg_alloc()); if (!msg) { RETURN_ERROR("nlmsg_alloc", nullptr); } void* user = genlmsg_put(msg.get(), NL_AUTO_PORT, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, cmd, kHwSimVersion); if (!user) { RETURN_ERROR("genlmsg_put", nullptr); } RETURN(msg); } std::unique_ptr buildCreateRadioMessage(const int family, const uint8_t mac[ETH_ALEN]) { std::unique_ptr msg = createNlMessage(family, HWSIM_CMD_NEW_RADIO); if (!msg) { RETURN(nullptr); } int ret; ret = nla_put(msg.get(), HWSIM_ATTR_PERM_ADDR, ETH_ALEN, mac); if (ret) { RETURN_NL_ERROR("nla_put(HWSIM_ATTR_PERM_ADDR)", ret, nullptr); } ret = nla_put_flag(msg.get(), HWSIM_ATTR_SUPPORT_P2P_DEVICE); if (ret) { RETURN_NL_ERROR("nla_put(HWSIM_ATTR_SUPPORT_P2P_DEVICE)", ret, nullptr); } RETURN(msg); } int createRadios(struct nl_sock* socket, const int netlinkFamily, const int nRadios, const int macPrefix) { uint8_t mac[ETH_ALEN] = {}; mac[0] = 0x02; mac[1] = (macPrefix >> CHAR_BIT) & 0xFF; mac[2] = macPrefix & 0xFF; for (int idx = 0; idx < nRadios; ++idx) { mac[4] = idx; std::unique_ptr msg = buildCreateRadioMessage(netlinkFamily, mac); if (msg) { int ret = nl_send_auto(socket, msg.get()); if (ret < 0) { RETURN_NL_ERROR("nl_send_auto", ret, 1); } } else { RETURN(1); } } RETURN(0); } int manageRadios(const int nRadios, const int macPrefix) { std::unique_ptr socket(nl_socket_alloc()); if (!socket) { RETURN_ERROR("nl_socket_alloc", 1); } int ret; ret = genl_connect(socket.get()); if (ret) { RETURN_NL_ERROR("genl_connect", ret, 1); } const int netlinkFamily = genl_ctrl_resolve(socket.get(), kHwSimFamilyName); if (netlinkFamily < 0) { RETURN_NL_ERROR("genl_ctrl_resolve", ret, 1); } ret = createRadios(socket.get(), netlinkFamily, nRadios, macPrefix); if (ret) { RETURN(ret); } RETURN(0); } int printUsage(FILE* dst, const int ret) { fprintf(dst, "%s", "Usage:\n" " mac80211_create_radios n_radios mac_prefix\n" " where\n" " n_radios - int, [1,100], e.g. 2;\n" " mac_prefix - int, [0, 65535], e.g. 5555.\n\n" " mac80211_create_radios will delete all existing radios and\n" " create n_radios with MAC addresses\n" " 02:pp:pp:00:nn:00, where nn is incremented (from zero)\n"); return ret; } int main(int argc, char* argv[]) { if (argc != 3) { return printUsage(stdout, 0); } int nRadios; if (!parseInt(argv[1], &nRadios)) { return printUsage(stderr, 1); } if (nRadios < 1) { return printUsage(stderr, 1); } if (nRadios > 100) { return printUsage(stderr, 1); } int macPrefix; if (!parseInt(argv[2], &macPrefix)) { return printUsage(stderr, 1); } if (macPrefix < 0) { return printUsage(stderr, 1); } if (macPrefix > UINT16_MAX) { return printUsage(stderr, 1); } return manageRadios(nRadios, macPrefix); }