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 上层协议
 */



相关文章
|
19天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
100 9
|
17天前
|
负载均衡 网络协议 算法
|
9天前
|
网络协议 网络安全 网络虚拟化
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算。通过这些术语的详细解释,帮助读者更好地理解和应用网络技术,应对数字化时代的挑战和机遇。
38 3
|
12天前
|
网络虚拟化
生成树协议(STP)及其演进版本RSTP和MSTP,旨在解决网络中的环路问题,提高网络的可靠性和稳定性
生成树协议(STP)及其演进版本RSTP和MSTP,旨在解决网络中的环路问题,提高网络的可靠性和稳定性。本文介绍了这三种协议的原理、特点及区别,并提供了思科和华为设备的命令示例,帮助读者更好地理解和应用这些协议。
30 4
|
20天前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
46 13
|
14天前
|
网络协议 SEO
TCP连接管理与UDP协议IP协议与ethernet协议
TCP、UDP、IP和Ethernet协议是网络通信的基石,各自负责不同的功能和层次。TCP通过三次握手和四次挥手实现可靠的连接管理,适用于需要数据完整性的场景;UDP提供不可靠的传输服务,适用于低延迟要求的实时通信;IP协议负责数据包的寻址和路由,是网络层的重要协议;Ethernet协议定义了局域网的数据帧传输方式,广泛应用于局域网设备之间的通信。理解这些协议的工作原理和应用场景,有助于设计和维护高效可靠的网络系统。
26 4
|
19天前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
20天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
22天前
|
网络协议 安全 NoSQL
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
|
1月前
|
网络协议 网络虚拟化 网络架构
【网络实验】/主机/路由器/交换机/网关/路由协议/RIP+OSPF/DHCP(上)
【网络实验】/主机/路由器/交换机/网关/路由协议/RIP+OSPF/DHCP(上)
64 1