Last active
March 5, 2024 01:40
-
-
Save rcgoodfellow/c60f58962911f5ab419dc5703612bf1c to your computer and use it in GitHub Desktop.
ask Linux to map a mac address to an interface name through netlink
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************* | |
* | |
* mac_2_ifname: given a mac address return the corresponding interface name | |
* | |
* usage: mac2ifname <mac> | |
* | |
* build: c++ -std=c++14 mac2ifname.cxx -o mac2ifname | |
* | |
******************************************************************************/ | |
#include <linux/netlink.h> | |
#include <linux/rtnetlink.h> | |
#include <sys/socket.h> | |
#include <net/if_arp.h> | |
#include <unistd.h> | |
#include <cstdint> | |
#include <cstring> | |
#include <string> | |
#include <iostream> | |
using std::string; | |
using std::cerr; | |
using std::cout; | |
using std::endl; | |
using std::out_of_range; | |
struct RtReq | |
{ | |
nlmsghdr header; | |
ifinfomsg msg; | |
}; | |
inline string mac_2_ifname(string mac) | |
{ | |
static size_t seq_num{0}; | |
/* | |
* send netlink request | |
*/ | |
//prepare request | |
RtReq req; | |
memset(&req, 0, sizeof(req)); | |
req.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; | |
req.header.nlmsg_type = RTM_GETLINK; | |
req.header.nlmsg_seq = ++seq_num; | |
req.msg.ifi_family = AF_UNSPEC; | |
req.msg.ifi_change = 0xffffffff; | |
//prepare message | |
sockaddr_nl sa; | |
iovec iov = {&req, req.header.nlmsg_len}; | |
msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; | |
memset(&sa, 0, sizeof(sa)); | |
sa.nl_family = AF_NETLINK; | |
//open socket | |
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
//send message | |
sendmsg(fd, &msg, 0); | |
/* | |
* get netlink response | |
*/ | |
char buf[16192]; | |
iov = {buf, sizeof(buf) }; | |
msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; | |
nlmsghdr *nh; | |
bool over{false}; | |
while(!over) | |
{ | |
int len = recvmsg(fd, &msg, 0); | |
for(nh = (nlmsghdr*)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) | |
{ | |
if(nh->nlmsg_type == NLMSG_DONE) | |
{ | |
over = true; | |
break; | |
} | |
if(nh->nlmsg_type != RTM_BASE) continue; | |
// get message payload | |
ifinfomsg *msg = (ifinfomsg*)NLMSG_DATA(nh); | |
if(msg->ifi_type != ARPHRD_ETHER) continue; | |
//get message attributes | |
rtattr *rta = IFLA_RTA(msg); | |
int alen = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)); | |
//store the interface name and mac addr here | |
string name, addr; | |
//iterate through attributes | |
for(; RTA_OK(rta, alen); rta = RTA_NEXT(rta, alen)) | |
{ | |
//grab the mac address attribute | |
if(rta->rta_type == IFLA_ADDRESS) | |
{ | |
char addr_buf[64]; | |
unsigned char * c = (unsigned char*)RTA_DATA(rta); | |
snprintf(addr_buf, 64, "%02x:%02x:%02x:%02x:%02x:%02x", | |
c[0], c[1], c[2], c[3], c[4], c[5]); | |
addr = string{addr_buf}; | |
} | |
//grab the interface name attribute | |
if(rta->rta_type == IFLA_IFNAME) | |
{ | |
name = string{(char*)RTA_DATA(rta)}; | |
} | |
} | |
if(mac == addr) return name; | |
} | |
} | |
throw out_of_range{mac + " not found"}; | |
} | |
int main(int argc, char **argv) | |
{ | |
if(argc != 2) | |
{ | |
cerr << "usage: mac2ifname <mac>" << endl; | |
return 1; | |
} | |
try { cout << mac_2_ifname(string{argv[1]}) << endl; } | |
catch(out_of_range &e) | |
{ | |
cerr << e.what() << endl; | |
return 1; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment