blob: 3b9f1493c431b89bbc207ac6469b1fba33a71be7 [file] [log] [blame]
/*
* 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 <memory>
#include <log/log.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/netlink.h>
#include <net/ethernet.h>
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;
constexpr int kChannels = 2;
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<struct nl_msg, nl_msg_deleter> createNlMessage(
const int family,
const int cmd) {
std::unique_ptr<struct nl_msg, nl_msg_deleter> 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<struct nl_msg, nl_msg_deleter>
buildCreateRadioMessage(const int family, const uint8_t mac[ETH_ALEN]) {
std::unique_ptr<struct nl_msg, nl_msg_deleter> 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); }
ret = nla_put_u32(msg.get(), HWSIM_ATTR_CHANNELS, kChannels);
if (ret) { RETURN_NL_ERROR("nla_put(HWSIM_ATTR_CHANNELS)", 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<struct nl_msg, nl_msg_deleter> 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<struct nl_sock, nl_sock_deleter> 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);
}