如何在C/C++中基于libtins从MAC地址获取厂商OUI及设备名称
Hey there! Great progress so far with your libtins network scanner. Let's expand it to add MAC vendor OUI lookup and device name resolution (like DESKTOP-TO5P0BD). Here's how to implement both features step by step:
OUI (Organizationally Unique Identifier) is the first 3 bytes of a MAC address, mapping directly to a hardware vendor. You'll need a lookup table for this—we'll start with an embedded local map for simplicity, and you can expand it later with a full OUI dataset.
Step 1: Create an OUI lookup map
Add this static or global map to your code (you can populate it with more OUIs as needed):
#include <unordered_map> #include <string> static std::unordered_map<std::string, std::string> oui_database = { {"00:1A:2B", "Intel Corporate"}, {"00:1C:42", "Apple, Inc."}, {"00:0C:29", "VMware, Inc."}, {"00:22:68", "Dell Inc."}, // Add more OUIs from public datasets (like IEEE's official list) };
Step 2: Extract and look up the OUI
Modify your code to pull the first 3 segments of the MAC address and match it to the vendor:
// Convert MAC address to string format std::string mac_str = arp.sender_hw_addr().to_string(); // Extract OUI (first 3 segments: "xx:xx:xx") std::string oui = mac_str.substr(0, 8); // Get vendor name std::string vendor = "Unknown Vendor"; auto oui_match = oui_database.find(oui); if (oui_match != oui_database.end()) { vendor = oui_match->second; }
There are two reliable ways to get a device's name on a local network: reverse DNS lookup (universal) or NetBIOS query (ideal for Windows devices). Let's cover both.
Option A: Reverse DNS Lookup (Cross-Platform)
Use the standard socket API's getnameinfo function to resolve an IP to a hostname:
#include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> // For Windows, replace with <winsock2.h> and <ws2tcpip.h> std::string get_device_name(const Tins::IPv4Address& ip) { struct sockaddr_in sa; char host_buffer[NI_MAXHOST]; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = ip.to_uint(); // Attempt to resolve the IP to a hostname if (getnameinfo((struct sockaddr*)&sa, sizeof(sa), host_buffer, NI_MAXHOST, nullptr, 0, NI_NAMEREQD) == 0) { return std::string(host_buffer); } return "Unknown Device"; }
Option B: NetBIOS Name Query (Libtins-Based)
Libtins supports crafting NetBIOS queries, which work great for fetching Windows device names directly from the local network:
#include <Tins/PDU.h> #include <Tins/IP.h> #include <Tins/UDP.h> #include <Tins/NetBIOS.h> std::string query_netbios_name(const Tins::IPv4Address& target_ip, Tins::NetworkInterface& iface) { // Craft a NetBIOS workstation name query Tins::NetBIOS nb_query; nb_query.type(Tins::NetBIOS::NAME_QUERY); nb_query.question_name("*<00>"); // Build the UDP/IP packet Tins::UDP udp; udp.dport(137); // NetBIOS name service port udp.sport(rand() % 65535 + 1024); // Random high port Tins::IP ip; ip.dst_addr(target_ip); ip.src_addr(iface.ipv4_address()); auto pdu = ip / udp / nb_query; // Send query and wait for response (500ms timeout) auto response = Tins::PacketSender().send_recv(pdu, iface, 500); if (response) { const Tins::NetBIOS* nb_response = response->find_pdu<Tins::NetBIOS>(); if (nb_response && nb_response->type() == Tins::NetBIOS::NAME_QUERY_RESPONSE) { return nb_response->answer_name(); } } return "Unknown Device"; }
Update your ARP reply handling logic to include the new features—you'll also want to modify your addresses map to store vendor and device name data:
// Update your addresses map type to store more info using DeviceInfo = std::tuple<Tins::HWAddress<6>, std::string, std::string>; std::unordered_map<Tins::IPv4Address, DeviceInfo> addresses; // Retrieve the ARP layer info const Tins::ARP& arp = pdu.rfind_pdu<Tins::ARP>(); std::cout << "Found :" << arp.sender_ip_addr() << ", " << arp.sender_hw_addr() << std::endl; // Checking if it is an ARP reply? if (arp.opcode() == Tins::ARP::REPLY) { auto iter = addresses.find(arp.sender_ip_addr()); if (iter == addresses.end()) { // Get vendor info std::string mac_str = arp.sender_hw_addr().to_string(); std::string oui = mac_str.substr(0, 8); std::string vendor = "Unknown Vendor"; auto oui_match = oui_database.find(oui); if (oui_match != oui_database.end()) { vendor = oui_match->second; } // Get device name (choose either reverse DNS or NetBIOS) Tins::IPv4Address ip = arp.sender_ip_addr(); Tins::NetworkInterface iface(ip); std::string device_name = get_device_name(ip); // Or use NetBIOS: std::string device_name = query_netbios_name(ip, iface); // Print and save the full device info std::cout << "saving " << arp.sender_ip_addr() << ", " << arp.sender_hw_addr() << ", Vendor: " << vendor << ", Device Name: " << device_name << std::endl; addresses.insert({ ip, {arp.sender_hw_addr(), vendor, device_name} }); } else { std::cout << "already seen " << arp.sender_ip_addr() << ", " << arp.sender_hw_addr() << std::endl; // Check for MAC address conflicts if (arp.sender_hw_addr() != std::get<0>(iter->second)) { std::cout << "[WARNING] " << arp.sender_ip_addr() << " is at " << std::get<0>(iter->second) << " but also at " << arp.sender_hw_addr() << std::endl; } } }
Quick Notes:
- For a full OUI database, you can download the official IEEE OUI CSV and parse it into your map at startup.
- NetBIOS queries work best on Windows-dominated local networks, while reverse DNS is more universal (but depends on proper DNS setup).
- On Windows, link against
ws2_32.libto use socket functions.
内容的提问来源于stack exchange,提问作者Gwiza Erick




