c语言编程笔录

首页 >   > 笔记大全

笔记大全

TCPIP 网络层 ARP协议

更新时间:2023-07-11

TCP/IP 协议栈

TCP/IP 协议栈是互联网上最广泛使用的协议栈。它由四个不同的层构成:网络接口层(数据链路层和物理层),网络层,传输层和应用层。这些层分别处理不同的任务,以便数据可以在一个网络中顺畅地传输。

网络层和 ARP 协议

网络层是协议栈中的第三层,主要负责在不同的网络之间路由数据。当一台计算机想要向另一台计算机发送数据时,它必须知道接收方计算机的 MAC 地址。此时,ARP(地址解析协议)就会登场。ARP 协议通过 IP 地址查询 MAC 地址,以便计算机可以准确地将数据包发送到指定的目标地址。

ARP 协议工作原理

ARP 协议的工作原理非常简单,甚至可以在几行代码中进行实现。下面是一个简单的 ARP 实现,用于查询目标 IP 地址的 MAC 地址:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main() {
    int sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
    if (sock_fd < 0) {
        perror("socket");
        exit(-1);
    }

    struct ifreq ifreq_i;
    memset(&ifreq_i, 0, sizeof(ifreq_i));
    strncpy(ifreq_i.ifr_name, "eth0", IFNAMSIZ-1);
    if ((ioctl(sock_fd, SIOCGIFINDEX, &ifreq_i)) < 0) {
        printf("ERROR : Could not retrieve Route Information.\n");
        close(sock_fd);
        exit(-1);
    }

    unsigned char sendbuf[42];
    memset(sendbuf, 0, sizeof(sendbuf));

    struct ethhdr *eth = (struct ethhdr *)sendbuf;
    eth->h_dest[0] = 0xff;
    eth->h_dest[1] = 0xff;
    eth->h_dest[2] = 0xff;
    eth->h_dest[3] = 0xff;
    eth->h_dest[4] = 0xff;
    eth->h_dest[5] = 0xff;
    eth->h_source[0] = 0x00;
    eth->h_source[1] = 0x0c;
    eth->h_source[2] = 0x29;
    eth->h_source[3] = 0x67;
    eth->h_source[4] = 0xd1;
    eth->h_source[5] = 0x51;
    eth->h_proto = htons(ETH_P_ARP);

    struct arphdr *arp = (struct arphdr *)(sendbuf+sizeof(struct ethhdr));
    arp->ar_hrd = htons(ARPHRD_ETHER);
    arp->ar_pro = htons(ETH_P_IP);
    arp->ar_hln = 6;
    arp->ar_pln = 4;
    arp->ar_op = htons(ARPOP_REQUEST);

    unsigned char *sender_mac = (unsigned char *)(sendbuf+sizeof(struct ethhdr)+sizeof(struct arphdr));
    *sender_mac++ = 0x00;
    *sender_mac++ = 0x0c;
    *sender_mac++ = 0x29;
    *sender_mac++ = 0x67;
    *sender_mac++ = 0xd1;
    *sender_mac++ = 0x51;

    unsigned char *sender_ip = (unsigned char *)(sendbuf+sizeof(struct ethhdr)+sizeof(struct arphdr)+6);
    *sender_ip++ = 192;
    *sender_ip++ = 168;
    *sender_ip++ = 1;
    *sender_ip++ = 100;

    unsigned char *target_ip = (unsigned char *)(sendbuf+sizeof(struct ethhdr)+sizeof(struct arphdr)+6+4);
    *target_ip++ = 192;
    *target_ip++ = 168;
    *target_ip++ = 1;
    *target_ip++ = 1;

    struct sockaddr_ll socket_address;
    memset(&socket_address, 0, sizeof(socket_address));
    socket_address.sll_ifindex = ifreq_i.ifr_ifindex;
    socket_address.sll_halen = ETH_ALEN;
    socket_address.sll_addr[0] = 0xff;
    socket_address.sll_addr[1] = 0xff;
    socket_address.sll_addr[2] = 0xff;
    socket_address.sll_addr[3] = 0xff;
    socket_address.sll_addr[4] = 0xff;
    socket_address.sll_addr[5] = 0xff;

    int send_len = sendto(sock_fd, sendbuf, sizeof(sendbuf), 0, (struct sockaddr*)&socket_address, sizeof(socket_address));
    if (send_len < 0) {
        perror("sendto");
        close(sock_fd);
        exit(-1);
    }

    unsigned char recv_buf[60];
    int recv_len = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, NULL, NULL);
    if (recv_len < 0) {
        perror("recvfrom");
        close(sock_fd);
        exit(-1);
    }
}

代码解释

上面的代码是使用 C 语言实现的 ARP 查询代码示例。该示例使用 socket API 打开一个原始套接字,并向目标地址发送 ARP 请求。以下是示例代码的一些关键点:

- 套接字类型为 SOCK_RAW,表示我们只对原始数据感兴趣,而不关心其他层的封装。

- h_dest 和 h_source 分别指定了目标地址和源地址的 MAC 地址。在这个例子中,我们用全局变量来指定了源地址。在实际使用中,你需要使用本地网卡的地址。

- 包含了一个 ARPHDR 结构体,该结构体描述了 ARP 请求/应答的格式。

- sender_ip 和 target_ip 分别指定了源 IP 地址和目标 IP 地址。

- 最后,代码通过 sendto 函数发送请求,并使用 recvfrom 获取 ARP 响应。

此 ARP 实现是一个非常基本的示例,并不能满足复杂的网络场景。但是,理解基本的原理和代码,可以为想要实现高级网络功能的程序员打下坚实基础。