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



相关文章
|
8天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
24 2
|
19天前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
46 13
|
21天前
|
网络协议 安全 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
|
2月前
|
网络架构
静态路由 网络实验
本文介绍了如何通过配置静态路由实现不同网络设备间的通信,包括网络拓扑图、设备IP配置、查看路由表信息、配置静态路由和测试步骤。通过在路由器上设置静态路由,使得不同子网内的设备能够互相通信。
静态路由 网络实验
|
2月前
|
网络虚拟化
网络实验 VlAN 中 Trunk Access端口的说明及实验
本文介绍了VLAN技术及其在网络实验中的应用,包括Access端口和Trunk端口的工作原理,以及如何通过实验划分不同VLAN实现内部通信和跨交换机实现VLAN间通信的详细步骤和配置。
网络实验 VlAN 中 Trunk Access端口的说明及实验
|
1月前
|
C语言
初识C语言:与计算机的交流之输入与输出(scanf和printf)
初识C语言:与计算机的交流之输入与输出(scanf和printf)
175 0
|
1月前
|
存储 程序员 编译器
初识C语言,计算机语言的基石
初识C语言,计算机语言的基石
|
1月前
|
网络协议 数据安全/隐私保护 网络虚拟化
【网络实验】/主机/路由器/交换机/网关/路由协议/RIP+OSPF/DHCP(下)
【网络实验】/主机/路由器/交换机/网关/路由协议/RIP+OSPF/DHCP(下)
59 0
|
1月前
|
移动开发 网络协议 测试技术
Mininet多数据中心网络拓扑流量带宽实验
Mininet多数据中心网络拓扑流量带宽实验
57 0
下一篇
无影云桌面