arp、ethernet、icmp、udp、ip协议的C语言实现(计算机网络协议栈实验)

简介: arp、ethernet、icmp、udp、ip协议的C语言实现(计算机网络协议栈实验)

41996206f6b142e4b8aef8de5365c09d.png


ARP协议部分代码:


#include <string.h>
#include <stdio.h>
#include "net.h"
#include "arp.h"
#include "ethernet.h"
/**
 * @brief 初始的arp包
 *
 */
static const arp_pkt_t arp_init_pkt = {
    .hw_type16 = swap16(ARP_HW_ETHER),
    .pro_type16 = swap16(NET_PROTOCOL_IP),
    .hw_len = NET_MAC_LEN,
    .pro_len = NET_IP_LEN,
    .sender_ip = NET_IF_IP,
    .sender_mac = NET_IF_MAC,
    .target_mac = {0}};
/**
 * @brief arp地址转换表,<ip,mac>的容器
 *
 */
map_t arp_table;
/**
 * @brief arp buffer,<ip,buf_t>的容器
 *
 */
map_t arp_buf;
/**
 * @brief 打印一条arp表项
 *
 * @param ip 表项的ip地址
 * @param mac 表项的mac地址
 * @param timestamp 表项的更新时间
 */
void arp_entry_print(void *ip, void *mac, time_t *timestamp)
{
    printf("%s | %s | %s\n", iptos(ip), mactos(mac), timetos(*timestamp));
}
/**
 * @brief 打印整个arp表
 *
 */
void arp_print()
{
    printf("===ARP TABLE BEGIN===\n");
    map_foreach(&arp_table, arp_entry_print);
    printf("===ARP TABLE  END ===\n");
}
/**
 * @brief 发送一个arp请求
 *
 * @param target_ip 想要知道的目标的ip地址
 */
void arp_req(uint8_t *target_ip)
{
    buf_init(&txbuf, sizeof(arp_pkt_t)); //对txbuf进行初始化
    arp_pkt_t *pkt = (arp_pkt_t *)txbuf.data;
    pkt->hw_type16 = swap16(1);
    pkt->pro_type16 = swap16(NET_PROTOCOL_IP);
    pkt->hw_len = 6;
    pkt->pro_len = 4;
    pkt->opcode16 = swap16(ARP_REQUEST);
    // for (int i = 0; i < 6; i++)
    // {
    //     pkt->sender_mac[i] = net_if_mac[i];
    //     pkt->target_mac[i] = 0x00;
    // }
    // for (int i = 0; i < 4; i++)
    // {
    //     pkt->sender_ip[i] = net_if_ip[i];
    //     pkt->target_ip[i] = *(target_ip + i);
    // }
    memcpy(pkt->sender_mac, net_if_mac, NET_MAC_LEN);
    memcpy(pkt->sender_ip, net_if_ip, NET_IP_LEN);
    memset(pkt->target_mac, 0, NET_MAC_LEN);
    memcpy(pkt->target_ip, target_ip, NET_IP_LEN);
    ethernet_out(&txbuf, ether_broadcast_mac, NET_PROTOCOL_ARP); //发送ARP报文
}
/**
 * @brief 发送一个arp响应
 *
 * @param target_ip 目标ip地址
 * @param target_mac 目标mac地址
 */


ETH协议全部代码:


#include "ethernet.h"
#include "utils.h"
#include "driver.h"
#include "arp.h"
#include "ip.h"
/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 */
void ethernet_in(buf_t *buf)
{
    if(buf->len < sizeof(ether_hdr_t))   //判断数据长度,小于以太网头部长度的话丢弃不处理
    {
        return;
    }
    ether_hdr_t *hdr = (ether_hdr_t *)buf->data;
    buf_remove_header(buf,sizeof(ether_hdr_t));  //移除以太网包头
    net_in(buf,swap16(hdr->protocol16),hdr->src);    //向上层传递数据包
}
/**
 * @brief 处理一个要发送的数据包
 * 
 * @param buf 要处理的数据包
 * @param mac 目标MAC地址
 * @param protocol 上层协议
 */
void ethernet_out(buf_t *buf, const uint8_t *mac, net_protocol_t protocol)
{
    if(buf->len < 46)
    {
        buf_add_padding(buf,46 - buf->len);
    }
    buf_add_header(buf,sizeof(ether_hdr_t));   //添加以太网包头
    ether_hdr_t *hdr = (ether_hdr_t *)buf->data;
    // for(int i = 0; i < 6; i++)   //填写目的MAC地址
    // {
    //     hdr->dst[i] = *(mac+i);
    // }
    // for(int i = 0; i < 6; i++)   //填写源MAC地址
    // {
    //     hdr->src[i] = net_if_mac[i]; //net_if_mac 得到本机的mac地址
    // }
    memcpy(hdr->dst, mac, NET_MAC_LEN);
    memcpy(hdr->src, net_if_mac, NET_MAC_LEN);
    hdr->protocol16 = swap16(protocol);
    driver_send(buf);   //将添加了以太网包头的数据帧发送到驱动层
}
/**
 * @brief 初始化以太网协议
 * 
 */
void ethernet_init()
{
    buf_init(&rxbuf, ETHERNET_MAX_TRANSPORT_UNIT + sizeof(ether_hdr_t));
}
/**
 * @brief 一次以太网轮询
 * 
 */
void ethernet_poll()
{
    if (driver_recv(&rxbuf) > 0)
        ethernet_in(&rxbuf);
}


ICMP协议部分代码:


#include "net.h"
#include "icmp.h"
#include "ip.h"
/**
 * @brief 发送icmp响应
 * 
 * @param req_buf 收到的icmp请求包
 * @param src_ip 源ip地址
 */
static void icmp_resp(buf_t *req_buf, uint8_t *src_ip)
{
    buf_t *buf = &txbuf;
    buf_init(buf, req_buf->len );
    memcpy(buf->data,req_buf->data,req_buf->len); 
    icmp_hdr_t *hdr1 = (icmp_hdr_t *)req_buf->data;
    icmp_hdr_t hdr2 = {
    .code = hdr1->code,
    .checksum16 = 0,
    .id16 = hdr1->id16,
    .seq16 = hdr1->seq16,
    .type = ICMP_TYPE_ECHO_REPLY};
    memcpy(buf->data, &hdr2, sizeof(icmp_hdr_t));
    hdr2.checksum16 = checksum16((uint16_t *)buf->data, buf->len);
    memcpy(buf->data, &hdr2, sizeof(icmp_hdr_t));
    ip_out(buf, src_ip, NET_PROTOCOL_ICMP);
}
/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_ip 源ip地址
 */
void icmp_in(buf_t *buf, uint8_t *src_ip)
{
    if (buf->len < sizeof(icmp_hdr_t))
    {
        return;
    }
    icmp_hdr_t *hdr = (icmp_hdr_t *)buf->data;
    if (hdr->type == ICMP_TYPE_ECHO_REQUEST)
    {
        icmp_resp(buf, src_ip);
    }
    else
    {
        return;
    }
}
/**
 * @brief 发送icmp不可达
 * 
 * @param recv_buf 收到的ip数据包
 * @param src_ip 源ip地址
 * @param code icmp code,协议不可达或端口不可达
 */


UDP协议部分代码:


#include "udp.h"
#include "ip.h"
#include "icmp.h"
/**
 * @brief udp处理程序表
 * 
 */
map_t udp_table;
/**
 * @brief udp伪校验和计算
 * 
 * @param buf 要计算的包
 * @param src_ip 源ip地址
 * @param dst_ip 目的ip地址
 * @return uint16_t 伪校验和
 */
static uint16_t udp_checksum(buf_t *buf, uint8_t *src_ip, uint8_t *dst_ip)
{
    uint8_t src[4];
    uint8_t dst[4];
    memcpy(src, src_ip, 4);
    memcpy(dst, dst_ip, 4);
    buf_add_header(buf, sizeof(udp_peso_hdr_t));
    udp_peso_hdr_t *peso_hdr = (udp_peso_hdr_t *)buf->data;
    memcpy(peso_hdr->src_ip, src, 4);
    memcpy(peso_hdr->dst_ip, dst, 4);
    peso_hdr->protocol = NET_PROTOCOL_UDP;
    peso_hdr->total_len16 = swap16(buf->len - sizeof(udp_peso_hdr_t));
    peso_hdr->placeholder = 0;
    uint16_t *p;
    p = (uint16_t *)peso_hdr;
    int n = buf->len / 2;
    int i = 0;
    uint32_t checksum = 0;
    while (i < n)
    {
        checksum += swap16(*p);
        p++;
        i++;
    }
    if (buf->len % 2 != 0)
    {
        checksum += swap16(*p) & 0xFF00;
    }
    while ((checksum >> 16) != 0)
    {
        checksum = (checksum >> 16) + (checksum & 0xFFFF);
    }
    buf_remove_header(buf, sizeof(udp_peso_hdr_t));
    memcpy(src_ip, src, 4);
    memcpy(dst_ip, dst, 4);
    return (uint16_t)(~checksum);
}
/**
 * @brief 处理一个收到的udp数据包
 * 
 * @param buf 要处理的包
 * @param src_ip 源ip地址
 */
void udp_in(buf_t *buf, uint8_t *src_ip)
{
    if (buf->len < sizeof(udp_hdr_t))
    {
        return;
    }
    udp_hdr_t *hdr = (udp_hdr_t *)buf->data;
    if (buf->len < swap16(hdr->total_len16))
    {
        return;
    }
    uint16_t temp = swap16(hdr->checksum16);
    hdr->checksum16 = 0;
    hdr->checksum16 = udp_checksum(buf, src_ip, net_if_ip);
    if (temp != hdr->checksum16)
    {
        return;
    }
    uint16_t dst_port16 = swap16(hdr->dst_port16);
    uint16_t src_port16 = swap16(hdr->src_port16);
    if (!(map_get(&udp_table, &dst_port16)))
    {
        buf_add_header(buf, sizeof(ip_hdr_t));
        icmp_unreachable(buf, src_ip, ICMP_CODE_PORT_UNREACH);
    }
    else
    {
        udp_handler_t *handler = map_get(&udp_table, &dst_port16);
        buf_remove_header(buf, sizeof(udp_hdr_t));
        (*handler)(buf->data, buf->len, src_ip, src_port16);
    }
}
/**
 * @brief 处理一个要发送的数据包
 * 
 * @param buf 要处理的包
 * @param src_port 源端口号
 * @param dst_ip 目的ip地址
 * @param dst_port 目的端口号
 */


IP协议部分代码:


#include "net.h"
#include "ip.h"
#include "ethernet.h"
#include "arp.h"
#include "icmp.h"
/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_mac 源mac地址
 */
void ip_in(buf_t *buf, uint8_t *src_mac)
{
    if(buf->len < sizeof(ip_hdr_t))
    {
        return;
    }
    ip_hdr_t *hdr = (ip_hdr_t *)buf->data;
    if(hdr->version != 4 || swap16(hdr->total_len16) > buf->len)
    {
        return;
    }
    uint16_t hdr_checksum_temp = hdr->hdr_checksum16;
    hdr->hdr_checksum16 = 0;
    uint16_t checksum = checksum16((uint16_t *)hdr, sizeof(ip_hdr_t));
    hdr->hdr_checksum16 = swap16(checksum);
    if(checksum != hdr_checksum_temp)
    {
        return;
    }
    else
    {
        hdr->hdr_checksum16 = hdr_checksum_temp;
    }
    for (size_t i = 0; i < NET_IP_LEN; i++)
    {
        if(hdr->dst_ip[i] != net_if_ip[i])
        {
            return;
        }
    }
    if(buf->len > swap16(hdr->total_len16))
    {
        buf_remove_padding(buf, buf->len - swap16(hdr->total_len16));
    }
    if(hdr->protocol != NET_PROTOCOL_ICMP && hdr->protocol != NET_PROTOCOL_UDP)
    {
        icmp_unreachable(buf, hdr->src_ip, ICMP_CODE_PROTOCOL_UNREACH);
    }
    else 
    {
        buf_remove_header(buf, sizeof(ip_hdr_t));
        net_in(buf, hdr->protocol, hdr->src_ip);
    }
}
/**
 * @brief 处理一个要发送的ip分片
 * 
 * @param buf 要发送的分片
 * @param ip 目标ip地址
 * @param protocol 上层协议
 * @param id 数据包id
 * @param offset 分片offset,必须被8整除
 * @param mf 分片mf标志,是否有下一个分片
 */
void ip_fragment_out(buf_t *buf, uint8_t *ip, net_protocol_t protocol, int id, uint16_t offset, int mf)
{
    buf_add_header(buf, sizeof(ip_hdr_t));
    ip_hdr_t *hdr = (ip_hdr_t *)buf->data;
    hdr->hdr_len = 5;
    hdr->version = 4;
    hdr->tos = 0;
    hdr->total_len16 = swap16(buf->len);
    hdr->id16 = swap16(id);
    hdr->protocol = protocol;
    hdr->ttl = 64;
    if (mf == 1)
    {
        hdr->flags_fragment16 = swap16((offset >> 3) + IP_MORE_FRAGMENT);
    }
    else
    {
        hdr->flags_fragment16 = swap16(offset >> 3);
    }
    memcpy(hdr->src_ip, net_if_ip, NET_IP_LEN);
    memcpy(hdr->dst_ip, ip, NET_IP_LEN);
    hdr->hdr_checksum16 = 0;
    hdr->hdr_checksum16 = checksum16((uint16_t *)hdr, sizeof(ip_hdr_t));
    arp_out(buf, ip);
}
/**
 * @brief 处理一个要发送的ip数据包
 * 
 * @param buf 要处理的包
 * @param ip 目标ip地址
 * @param protocol 上层协议
 */



相关文章
|
7月前
|
网络协议 Linux 虚拟化
配置VM网络:如何设定静态IP以访问主机IP和互联网
以上就是设定虚拟机网络和静态IP地址的基本步骤。需要注意的是,这些步骤可能会因为虚拟机软件、操作系统以及网络环境的不同而有所差异。在进行设定时,应根据具体情况进行调整。
520 10
|
10月前
|
监控 安全 Go
使用Go语言构建网络IP层安全防护
在Go语言中构建网络IP层安全防护是一项需求明确的任务,考虑到高性能、并发和跨平台的优势,Go是构建此类安全系统的合适选择。通过紧密遵循上述步骤并结合最佳实践,可以构建一个强大的网络防护系统,以保障数字环境的安全完整。
208 12
|
10月前
|
网络协议 开发者
探讨UDP协议中connect函数的作用及影响
总结来看,虽然UDP是无连接的,`connect()` 函数的使用在UDP编程中是一种可选的技术,它可以带来编程上的便利和某些性能上的改进,同时它改变的是程序逻辑上的行为,而非UDP协议本身的无连接特性。在实际应用中,根据通信模式和需求的不同,开发者可以根据情况选择是否调用 `connect()` 函数。
409 8
|
11月前
|
监控 安全 网络安全
网络安全新姿势:多IP配置的五大好处
服务器配置多IP地址,既能提升网络速度与安全性,又能实现多站点托管和故障转移。本文详解多IP的五大妙用、配置方法及进阶技巧。从理论到实践,合理规划IP资源,让服务器性能跃升新高度。
334 2
|
监控 网络协议 视频直播
UDP协议(特点与应用场景)
UDP(用户数据报协议)是传输层的一种无连接协议,具有简单高效、低延迟的特点。其主要特点包括:无连接(无需握手)、不可靠传输(不保证数据完整性)、面向数据报(独立传输)。尽管UDP不如TCP可靠,但在实时通信(如语音通话、视频会议)、在线游戏、多媒体流媒体(如直播、点播)及网络监控等领域广泛应用,满足了对速度和实时性要求较高的需求。
1652 19
|
网络协议
为何UDP协议不可靠?DNS为何选择UDP?
总的来说,UDP和TCP各有优势,选择哪种协议取决于应用的具体需求。UDP可能不如TCP可靠,但其简单、快速的特性使其在某些场景下成为更好的选择。而DNS就是这样的一个例子,它利用了UDP的优势,以实现快速、高效的名字解析服务。
647 14
|
网络协议 Java 开发工具
全平台开源即时通讯IM框架MobileIMSDK:7端+TCP/UDP/WebSocket协议,鸿蒙NEXT端已发布,5.7K Stars
全平台开源即时通讯IM框架MobileIMSDK:7端+TCP/UDP/WebSocket协议,鸿蒙NEXT端已发布,5.7K Stars
698 1
|
域名解析 API PHP
VM虚拟机全版本网盘+免费本地网络穿透端口映射实时同步动态家庭IP教程
本文介绍了如何通过网络穿透技术让公网直接访问家庭电脑,充分发挥本地硬件性能。相比第三方服务受限于转发带宽,此方法利用自家宽带实现更高效率。文章详细讲解了端口映射教程,包括不同网络环境(仅光猫、光猫+路由器)下的设置步骤,并提供实时同步动态IP的两种方案:自建服务器或使用三方API接口。最后附上VM虚拟机全版本下载链接,便于用户在穿透后将服务运行于虚拟环境中,提升安全性与适用性。
948 7
|
安全 网络安全 UED
为何长效代理静态IP是网络管理的关键要素
在信息化时代,静态长效IP代理对网络管理至关重要。它能提升网络服务质量,确保远程办公、视频会议等应用的稳定性和连续性;减少延迟和网络拥堵,加快数据传输;提高网络安全,便于设置访问权限,防止未授权访问。91HTTP高质量代理IP服务商助力高效信息获取。
310 23