TCPIP 网络层 ARP协议
更新时间:2023-07-11TCP/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 实现是一个非常基本的示例,并不能满足复杂的网络场景。但是,理解基本的原理和代码,可以为想要实现高级网络功能的程序员打下坚实基础。