MAC 地址

一、MAC 地址基础

1. 什么是 MAC 地址?

MAC 地址(媒体访问控制地址)是网络设备网卡的物理地址,用于在局域网内唯一标识一台设备,核心特性如下:

  • 格式:由 6 字节(48 位)十六进制数组成,分为前 3 字节(厂商标识 OUI)和后 3 字节(设备序列号),格式如 00:11:22:33:44:55(冒号分隔,也可用连字符 -);
  • 固化性:出厂时写入网卡 ROM,理论上全球唯一(可通过软件修改,非物理固化);
  • 作用范围:仅局限于局域网(同一广播域),跨网段通信时会被网关替换,无法传递到外网。
2. MAC 地址的核心分类

根据传输范围和用途,MAC 地址分为三类,对应不同通信场景:

  1. 单播 MAC 地址:最常用,目标为单一设备,前 3 字节的最低位为 0(如 00:11:22:33:44:55),用于点对点通信(如两台电脑局域网传文件);
  2. 广播 MAC 地址:全局广播,地址为 FF:FF:FF:FF:FF:FF,发送方发送后,局域网内所有设备都会接收(如 ARP 请求、DHCP Discover 报文);
  3. 多播 MAC 地址:目标为一组设备,前 3 字节的最低位为 1(如 01:00:5E:00:00:01),用于一对多通信(如组播视频流、路由协议报文)。
3. MAC 与 IP 的核心区别

MAC 地址和 IP 地址共同完成网络通信,二者定位不同,核心区别可通过表格清晰区分:'

|------|------------------|----------------------|
| 对比维度 | MAC 地址(物理地址) | IP 地址(逻辑地址) |
| 所属层级 | 数据链路层 | 网络层 |
| 作用范围 | 局域网(广播域内) | 跨网段(互联网全局) |
| 分配方式 | 厂商预分配,理论唯一 | 手动配置或 DHCP 自动分配(可修改) |
| 核心用途 | 局域网内数据帧投递 | 跨网段设备定位 |
| 变更特性 | 设备不变则地址不变(可软件修改) | 更换网络(如切换 WiFi)时可能变更 |

4. MAC 地址在通信中的作用

结合之前讲过的 ARP 协议,MAC 地址在局域网通信中的流程可串联为:

  1. 应用层发起请求(如访问同网段设备),仅知晓目标 IP 地址;
  2. 通过 ARP 协议查询目标 IP 对应的 MAC 地址,获取后封装到以太网帧头;
  3. 以太网帧以 "目标 MAC 地址" 为标识,在局域网内传输,交换机根据 MAC 地址表转发帧到目标设备;
  4. 目标设备接收帧后,核对帧头的目标 MAC 地址,匹配则接收解析,不匹配则丢弃。

二、MAC 地址高频考点

1. MAC 地址的长度、格式及组成?

48 位(6 字节)十六进制数,格式为 XX:XX:XX:XX:XX:XX;前 3 字节为厂商标识(OUI),后 3 字节为设备序列号,全球唯一。

2. MAC 地址能跨网段传输吗?为什么?

不能。跨网段通信需经过网关(路由器),路由器会剥离原以太网帧头(含源 / 目标 MAC),重新封装网关的 MAC 地址转发,原设备 MAC 无法传递到其他网段。

3. 交换机是如何通过 MAC 地址转发数据的?

交换机维护 MAC 地址表(MAC→端口映射),接收帧后读取目标 MAC,若表中存在对应端口则单播转发,不存在则广播转发;同时记录源 MAC 与接入端口,更新地址表。

4. MAC 地址可以修改吗?如何修改?

可以(软件层面)。① Linux:通过 ifconfigip link 命令临时修改,重启失效;② Windows:在网卡属性→高级→网络地址中手动设置;注意:修改后仅局域网内生效,且需避免与其他设备冲突。

5. 为什么需要 ARP 协议?与 MAC 地址的关联是什么?

用层仅知晓目标 IP,而局域网传输依赖 MAC 地址,ARP 协议的核心作用就是 "IP 地址到 MAC 地址的映射",为以太网帧封装提供目标 MAC,衔接网络层与数据链路层。

6. 广播 MAC 地址的应用场景有哪些?

① ARP 请求报文(查询目标 IP 对应的 MAC);② DHCP 自动获取 IP(客户端发送广播请求 IP);③ 局域网内无目标 MAC 映射时,交换机广播转发帧。

7. 跨网段通信时,目标 MAC 是最终设备的 MAC?

跨网段时,目标 MAC 为网关的 MAC,原设备 MAC 仅在本地局域网有效;

8. MAC 地址绝对唯一且不可修改?

理论上唯一,可通过软件临时修改(非物理层面),修改后需注意局域网内冲突;

9. MAC 地址和网卡一一对应?

一台设备可能有多个网卡(有线、无线),每个网卡对应一个独立 MAC 地址。

三、MAC 地址获取与解析

1. 环境与依赖
  • Linux 环境:无需额外库,通过系统调用读取网卡信息(如 /sys/class/net 目录或 ioctl 函数);
  • Windows 环境:调用系统 API(如 IP_ADAPTER_INFO 结构体),无需第三方库;
  • 核心思路:分别实现 Linux/Windows 下的本地 MAC 获取、以太网帧中 MAC 解析,代码加详细注释,确保小白可复现。
2. Linux 下获取本地网卡 MAC 地址

实现功能:遍历本地网卡(如 eth0、wlan0),获取并输出对应网卡的 MAC 地址,适配小白操作。

cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/ioctl.h>
<netinet/in.h<net/if.h>
#include <arpa/inet.h>

// 获取指定网卡的MAC地址
std::string get_linux_mac(const std::string& ifname) {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
       < "创建套接字失败:" << strerror(errno) << std::endl;
        return "";
    }

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ - 1); // 传入网卡名称

    // 通过ioctl获取网卡MAC地址
    if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
       < "获取< "MAC地址失败:" << strerror(errno) << std::endl;
        close(sockfd);
        return "";
    }

    close(sockfd);

    // 格式化MAC地址(将二进制转为十六进制,加冒号分隔)
    unsigned char* mac = (unsigned char*)ifr.ifr_hwaddr.sa_data;
    char mac_str[20];
    snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x",
             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    return mac_str;
}

int main() {
    std::string ifname;
    std::cout << "请输入要查询的网卡名称(如eth0、wlan0):";
    std::cin >> ifname;

    std::string mac = get_linux_mac(ifname);
    if (!mac.empty()) {
       < ifname< " 的MAC地址:< std::endl;
    }

    return 0;
}

编译与运行(Linux)

  • 编译命令:g++ linux_mac_get.cpp -o linux_mac_get
  • 运行:./linux_mac_get,输入网卡名称(可通过 ifconfig 查看网卡列表),即可输出 MAC 地址。
3. Windows 下获取本地网卡 MAC 地址

Windows 下通过 API 读取网卡适配器信息,代码简洁,小白可直接用 VS 编译运行:

cpp 复制代码
<windows.h>
<iphlpapi<icmpapi.h>

// 链接IP辅助库
#pragma comment(lib, "iphlpapi.lib")

// 获取所有网卡的MAC地址及对应IP
void get_windows_mac() {
    DWORD buf_len = 0;
    // 先获取所需缓冲区大小
    GetAdaptersInfo(nullptr, &buf_len);
    IP_ADAPTER_INFO* adapter_info = (IP_ADAPTER_INFO*)malloc(buf_len);
    if (adapter_info == nullptr) {
        std< "内存分配< std::endl;
        return;
    }

    // 读取网卡信息
    if (GetAdaptersInfo(adapter_info, &buf_len) != ERROR_SUCCESS) {
        std::cerr << "获取网卡信息失败"< std::endl;
        free(adapter_info);
        return;
    }

    IP_ADAPTER_INFO* adapter = adapter_info;
    int index = 1;
    while (adapter != nullptr) {
        // 过滤掉环路地址、无效网卡
        if (adapter->Type != MIB_IF_TYPE_ETHERNET || strcmp(adapter->IpAddressList.IpAddress.String, "") == 0) {
            adapter = adapter->Next;
            continue;
        }

        // 格式化MAC地址
        char mac_str[20];
        unsigned char* mac = adapter->Address;
        snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x",
                 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

        // 输出网卡信息
        std< "网卡" << index++ << ":" << std::endl;
       < " < adapter< std::endl;
        std::< "  IP地址< adapter->IpAddressList.IpAddress.String< std::endl;
< "  MAC地址:" << std::endl< std::endl;

        adapter = adapter->Next;
    }

    free(adapter_info);
}

int main() {
    std::cout << "本地网卡信息(MAC+IP):" << std::endl;
    get_windows_mac();
    return 0;
}

编译与运行(Windows)

  • 环境:Visual Studio 或 MinGW;
  • 编译:创建控制台项目,粘贴代码编译(自动链接 iphlpapi.lib);
  • 运行:双击 exe 文件,直接输出所有以太网网卡的 MAC 地址和对应 IP,无需手动输入网卡名称。
4. 解析以太网帧中的 MAC 地址(结合 libpcap)

基于 libpcap 抓包,解析以太网帧头中的源 / 目标 MAC 地址,衔接之前 ARP、ICMP 的抓包逻辑,深化实战能力:

cpp 复制代码
#include <pcap.h>
#include <netinet/if_ether.h>
#include <cstring>

// 回调函数:解析捕获到的以太网帧
void packet_handler(u_char* user_data, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
    // 以太网帧头长度为14字节(目标MAC6字节+源MAC6字节+帧类型2字节)
    if (pkth< 14) return;

    // 解析以太网帧头
    const struct ether_header* eth_header = (const struct ether_header*)packet;
    unsigned char* dst_mac = eth_header->ether_dhost; // 目标MAC
    unsigned char* src_mac = eth_header->ether_shost; // 源MAC
    uint16_t eth_type = ntohs(eth_header->ether_type); // 帧类型(如ARP=0x0806,IP=0x0800)

    // 格式化MAC地址
    char dst_mac_str[20], src_mac_str[20];
    snprintf(dst_mac_str, sizeof(dst_mac_str), "%02x:%02x:%02x:%02x:%02x:%02x",
             dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
    snprintf(src_mac_str, sizeof(src_mac_str), "%02x:%02x:%02x:%02x:%02x:%02x",
             src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]);

    // 输出解析结果
   < "捕获< std::endl;
    std< "  < dst< std::endl;
    std::< "  源< src_m< std::endl;
    std::cout< "  帧类型< std::hex << eth_type << std::< std::endl;
}

int main() {
    char err_buf[PCAP_ERRBUF_SIZE];
    pcap_if_t* alldevs, *dev;

    // 获取本地网卡设备
    if (pcap_findalldevs(&alldevs, err_buf) == -1) {
        std::cerr << "获取网卡设备失败:< std::endl;
        return -1;
    }
    dev = alldevs;
    std< "使用网卡< dev->name< std::endl;

    // 打开网卡,混杂模式抓包
    pcap_t* handle = pcap_open_live(dev->name, BUFSIZ, 1, 1000, err_buf);
    if (handle == nullptr) {
       < "打开< err< std::endl;
        pcap_freealldevs(alldevs);
        return -1;
    }
    pcap_freealldevs(alldevs);

    // 开始抓包(循环捕获10个帧后退出)
    std::< "开始抓包,共捕获10个< std::endl;
    pcap_loop(handle, 10, packet_handler, nullptr);

    pcap_close(handle);
    return 0;
}

编译与运行(Linux)

  • 依赖:安装 libpcap 库,命令 sudo apt-get install libpcap-dev
  • 编译:g++ mac_parse.cpp -o mac_parse -lpcap
  • 运行(需 root 权限):sudo ./mac_parse,捕获 10 个以太网帧并解析源 / 目标 MAC。
5. 扩展
  • 局域网 MAC 扫描:结合 ARP 协议,发送 ARP 广播请求,收集响应设备的 IP-MAC 映射,实现设备扫描工具;
  • MAC 地址过滤:抓包时过滤指定 MAC 地址的报文,仅保留目标设备的通信数据;
  • 跨平台封装:用条件编译整合 Linux/Windows 代码,实现一份代码获取双系统网卡 MAC;
  • MAC 绑定检测:对比本地存储的合法 MAC 列表,检测局域网内是否有非法设备接入。
相关推荐
googleccsdn2 小时前
ENSP Pro Lab笔记:配置BGP VXLAN双栈(3)
网络·笔记
百***78753 小时前
【实操】一步API对接GPT-5.2全流程(多语言示例+高并发优化+避坑指南)
网络·gpt
科技块儿3 小时前
【场景:识别C2通信】评估出站IP是否为已知恶意地址,方法:IP离线库+威胁情报融合
网络·网络协议·tcp/ip
小胖红3 小时前
Xcode 打包失败 处理
ide·macos·xcode
咕噜签名-铁蛋3 小时前
火山云豆包:重新定义AI交互,让智能触手可及
服务器
小陈phd4 小时前
langGraph从入门到精通(六)——基于 LangGraph 实现结构化输出与智能 Router 路由代理
android·网络·数据库
安当加密4 小时前
电力系统如何防“明文传输”?某电网公司用SM2+UKey构建“端到端加密”实战
服务器·数据库·安全·阿里云
石像鬼₧魂石4 小时前
Kali Linux 内网渗透:深度工程实施手册
linux·运维·服务器
Ares-Wang4 小时前
网络》》IP组播
网络·网络协议·tcp/ip