数据包结构定义

包结构定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define XNET_CFG_PACKET_MAX_SIZE        1516        // 收发数据包的最大大小



/**

 * 网络数据结构

 */

typedef struct _xnet_packet_t{

    uint16_t size;                              // 包中有效数据大小

    uint8_t * data;                             // 包的数据起始地址

    uint8_t payload[XNET_CFG_PACKET_MAX_SIZE];  // 最大负载数据量

}xnet_packet_t;




xnet_packet_t * xnet_alloc_for_send(uint16_t data_size);  //定义包发送函数

xnet_packet_t * xnet_alloc_for_read(uint16_t data_size);  //定义包读取函数

实现包发送和读取函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
static xnet_packet_t tx_packet, rx_packet;                      // 接收与发送缓冲区



/**

 * 分配一个网络数据包用于发送数据

 * @param data_size 数据空间大小

 * @return 分配得到的包结构

 */

xnet_packet_t * xnet_alloc_for_send(uint16_t data_size) {

    // 从tx_packet的后端往前分配,因为前边要预留作为各种协议的头部数据存储空间

    tx_packet.data = tx_packet.payload + XNET_CFG_PACKET_MAX_SIZE - data_size;

    tx_packet.size = data_size;

    return &tx_packet;

}



/**

 * 分配一个网络数据包用于读取

 * @param data_size 数据空间大小

 * @return 分配得到的数据包

 */

xnet_packet_t * xnet_alloc_for_read(uint16_t data_size) {

    // 从最开始进行分配,用于最底层的网络数据帧读取

    rx_packet.data = rx_packet.payload;

    rx_packet.size = data_size;

    return &rx_packet;

}

对包的一些操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#define min(a, b)               ((a) > (b) ? (b) : (a))



/**

 * 为发包添加一个头部

 * @param packet 待处理的数据包

 * @param header_size 增加的头部大小

 */

static void add_header(xnet_packet_t *packet, uint16_t header_size) {

    packet->data -= header_size;

    packet->size += header_size;

}



/**

 * 为接收向上处理移去头部

 * @param packet 待处理的数据包

 * @param header_size 移去的头部大小

 */

static void remove_header(xnet_packet_t *packet, uint16_t header_size) {

    packet->data += header_size;

    packet->size -= header_size;

}



/**

 * 将包的长度截断为size大小

 * @param packet 待处理的数据包

 * @param size 最终大小

 */

static void truncate_packet(xnet_packet_t *packet, uint16_t size) {

    packet->size = min(packet->size, size);

}

总代码

app.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>

#include "xnet_tiny.h"



int main (void) {

    xnet_init();



    printf("xnet running\n");

    while (1) {

        xnet_poll();

    }



    return 0;

}
xnet_tiny.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef XNET_TINY_H

#define XNET_TINY_H



#include <stdint.h>



#define XNET_CFG_PACKET_MAX_SIZE        1516        // 收发数据包的最大大小



/**

 * 网络数据结构

 */

typedef struct _xnet_packet_t{

    uint16_t size;                              // 包中有效数据大小

    uint8_t * data;                             // 包的数据起始地址

    uint8_t payload[XNET_CFG_PACKET_MAX_SIZE];  // 最大负载数据量

}xnet_packet_t;



xnet_packet_t * xnet_alloc_for_send(uint16_t data_size);

xnet_packet_t * xnet_alloc_for_read(uint16_t data_size);



void xnet_init (void);

void xnet_poll(void);



#endif // XNET_TINY_H
xnet_tiny.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "xnet_tiny.h"



#define min(a, b)               ((a) > (b) ? (b) : (a))



static xnet_packet_t tx_packet, rx_packet;                      // 接收与发送缓冲区



/**

 * 分配一个网络数据包用于发送数据

 * @param data_size 数据空间大小

 * @return 分配得到的包结构

 */

xnet_packet_t * xnet_alloc_for_send(uint16_t data_size) {

    // 从tx_packet的后端往前分配,因为前边要预留作为各种协议的头部数据存储空间

    tx_packet.data = tx_packet.payload + XNET_CFG_PACKET_MAX_SIZE - data_size;

    tx_packet.size = data_size;

    return &tx_packet;

}



/**

 * 分配一个网络数据包用于读取

 * @param data_size 数据空间大小

 * @return 分配得到的数据包

 */

xnet_packet_t * xnet_alloc_for_read(uint16_t data_size) {

    // 从最开始进行分配,用于最底层的网络数据帧读取

    rx_packet.data = rx_packet.payload;

    rx_packet.size = data_size;

    return &rx_packet;

}



/**

 * 为发包添加一个头部

 * @param packet 待处理的数据包

 * @param header_size 增加的头部大小

 */

static void add_header(xnet_packet_t *packet, uint16_t header_size) {

    packet->data -= header_size;

    packet->size += header_size;

}



/**

 * 为接收向上处理移去头部

 * @param packet 待处理的数据包

 * @param header_size 移去的头部大小

 */

static void remove_header(xnet_packet_t *packet, uint16_t header_size) {

    packet->data += header_size;

    packet->size -= header_size;

}



/**

 * 将包的长度截断为size大小

 * @param packet 待处理的数据包

 * @param size 最终大小

 */

static void truncate_packet(xnet_packet_t *packet, uint16_t size) {

    packet->size = min(packet->size, size);

}



/**

 * 协议栈的初始化

 */

void xnet_init (void) {

}



/**

 * 轮询处理数据包,并在协议栈中处理

 */

void xnet_poll(void) {

}

以太网协议

以太网封装驱动

定义驱动的函数

1
2
3
4
5
xnet_err_t xnet_driver_open (uint8_t * mac_addr);

xnet_err_t xnet_driver_send (xnet_packet_t * packet);

xnet_err_t xnet_driver_read (xnet_packet_t ** packet);

实现驱动的函数

port_pcap.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <string.h>

#include <stdlib.h>

#include "pcap_device.h"

#include "xnet_tiny.h"



static pcap_t * pcap;



// pcap所用的网卡

const char * ip_str = "192.168.254.1";      // 根据实际电脑上存在的网卡地址进行修改

const char my_mac_addr[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};

/**

 * 初始化网络驱动

 * @return 0成功,其它失败

 */

xnet_err_t xnet_driver_open (uint8_t * mac_addr) {

    memcpy(mac_addr, my_mac_addr, sizeof(my_mac_addr));

    pcap = pcap_device_open(ip_str, mac_addr, 1);

    if (pcap == (pcap_t *)0) {

        exit(-1);

    }

    return XNET_ERR_OK;

}



/**

 * 发送数据

 * @param frame 数据起始地址

 * @param size 数据长度

 * @return 0 - 成功,其它失败

 */

xnet_err_t xnet_driver_send (xnet_packet_t * packet) {

    return pcap_device_send(pcap, packet->data, packet->size);

}



/**

 * 读取数据

 * @param frame 数据存储位置

 * @param size 数据长度

 * @return 0 - 成功,其它失败

 */

xnet_err_t xnet_driver_read (xnet_packet_t ** packet) {

    uint16_t size;

    xnet_packet_t * r_packet = xnet_alloc_for_read(XNET_CFG_PACKET_MAX_SIZE);



    size = pcap_device_read(pcap, r_packet->data, XNET_CFG_PACKET_MAX_SIZE);

    if (size) {

        r_packet->size = size;

        *packet = r_packet;

        return XNET_ERR_OK;

    }



    return XNET_ERR_IO;

}

定义枚举_xnet_err_t

1
2
3
4
5
6
7
typedef enum _xnet_err_t {

    XNET_ERR_OK = 0,

    XNET_ERR_IO = -1,

}xnet_err_t;

以太网输入输出处理

以太网初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**

 * 以太网初始化

 * @return 初始化结果

 */

static xnet_err_t ethernet_init (void) {

    xnet_err_t err = xnet_driver_open(netif_mac);

    if (err < 0) return err;



    return XNET_ERR_OK;

}

以太网数据帧格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma pack(1)



#define XNET_IPV4_ADDR_SIZE             4           // IP地址长度

#define XNET_MAC_ADDR_SIZE              6           // MAC地址长度



/**

 * 以太网数据帧格式:RFC894

 */

typedef struct _xether_hdr_t {

    uint8_t dest[XNET_MAC_ADDR_SIZE];           // 目标mac地址

    uint8_t src[XNET_MAC_ADDR_SIZE];            // 源mac地址

    uint16_t protocol;                          // 协议/长度

}xether_hdr_t;



#pragma pack()

发送和接收以太网帧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
static uint8_t netif_mac[XNET_MAC_ADDR_SIZE];                   // mac地址



#define swap_order16(v)   ((((v) & 0xFF) << 8) | (((v) >> 8) & 0xFF))

/**

 * 发送一个以太网数据帧

 * @param protocol 上层数据协议,IP或ARP

 * @param mac_addr 目标网卡的mac地址

 * @param packet 待发送的数据包

 * @return 发送结果

 */

static xnet_err_t ethernet_out_to(xnet_protocol_t protocol, const uint8_t *mac_addr, xnet_packet_t * packet) {

    xether_hdr_t* ether_hdr;



    // 添加头部

    add_header(packet, sizeof(xether_hdr_t));

    ether_hdr = (xether_hdr_t*)packet->data;

    memcpy(ether_hdr->dest, mac_addr, XNET_MAC_ADDR_SIZE);

    memcpy(ether_hdr->src, netif_mac, XNET_MAC_ADDR_SIZE);

    ether_hdr->protocol = swap_order16(protocol);



    // 数据发送

    return xnet_driver_send(packet);

}



/**

 * 以太网数据帧输入输出

 * @param packet 待处理的包

 */

static void ethernet_in (xnet_packet_t * packet) {

    // 至少要比头部数据大

    if (packet->size <= sizeof(xether_hdr_t)) {

        return;

    }



    // 往上分解到各个协议处理

    xether_hdr_t* hdr = (xether_hdr_t*)packet->data;

    switch (swap_order16(hdr->protocol)) {

        case XNET_PROTOCOL_ARP:   //协议类型是ARP

            break;

        case XNET_PROTOCOL_IP: {  //协议类型是IP

            break;

        }

    }

}

定义枚举协议的类型

1
2
3
4
5
6
7
typedef enum _xnet_protocol_t {

    XNET_PROTOCOL_ARP = 0x0806,     // ARP协议

    XNET_PROTOCOL_IP = 0x0800,      // IP协议

}xnet_protocol_t;

轮询处理数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**

 * 查询网络接口,看看是否有数据包,有则进行处理

 */

static void ethernet_poll (void) {

    xnet_packet_t * packet;



    if (xnet_driver_read(&packet) == XNET_ERR_OK) {

        // 正常情况下,在此打个断点,全速运行

        // 然后在对方端ping 192.168.254.2,会停在这里

        ethernet_in(packet);

    }

}



/**

 * 协议栈的初始化

 */

void xnet_init (void) {

    ethernet_init();

}



/**

 * 轮询处理数据包,并在协议栈中处理

 */

void xnet_poll(void) {

    ethernet_poll();

}

ARP协议

ARP的初始化

Q: 因为以太网包包头需要目的MAC地址, 已知目的IP地址如何获得目的MAC地址?

A: 发送一个广播包Broadcast, 询问目的MAC地址, 目的主机回复MAC地址.

xxxxxxxxxx public static void main(String[] args) {​    QueueArray queueArray = new QueueArray(3);​    Scanner scanner = new Scanner(System.in);​    boolean loop = true;​    while(loop) {​        System.out.println(“入队:in, 出队:out, 判空:empty, 判满:full, 查看:check, 退出:exit”);​        String next = scanner.next();​        switch (next) {​            case “in” :                System.out.println(“请输入数据”);​                queueArray.enter(scanner.nextInt());​                break;​            case “out” :                try {​                    System.out.println(“出队数据为: “ + queueArray.leave());​                } catch (Exception e) {​                        System.out.println(e.getMessage());​                }​                break;​            case “empty” :                if(queueArray.isEmpty()) {​                     System.out.println(“队列为空”);​                     break;​                }​                System.out.println(“队列非空”);​                break;​            case “full” :                if(queueArray.isFull()) {​                      System.out.println(“队列已满”);​                      break;​                }​                System.out.println(“队列未满”);​                break;​            case “check” :                queueArray.traverse();​                break;​            case “exit” :                System.out.println(“已退出”);​                loop = false;​            break;​        }​   }​   scanner.close();​}java

ARP表

ARP表结构定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#define XNET_IPV4_ADDR_SIZE             4           // IP地址长度

#define XNET_MAC_ADDR_SIZE              6           // MAC地址长度/**

 * IP地址

 */

typedef union _xipaddr_t {

    uint8_t array[XNET_IPV4_ADDR_SIZE];     // 以数组形式存储的ip

    uint32_t addr;                          // 32位的ip地址

}xipaddr_t;



#define XARP_ENTRY_FREE                0       // ARP表项空闲



/**

 * ARP表项

 */

typedef struct _xarp_entry_t {

    xipaddr_t ipaddr;                       // ip地址

    uint8_t    macaddr[XNET_MAC_ADDR_SIZE];    // mac地址

    uint8_t    state;                          // 状态位

    uint16_t tmo;                           // 当前超时

    uint8_t    retry_cnt;                      // 当前重试次数

}xarp_entry_t;

初始化ARP表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#define XARP_ENTRY_FREE                0       // ARP表项空闲

#define XARP_ENTRY_OK                1       // ARP表项解析成功

/**

 * ARP初始化

 */

void xarp_init(void) {

    arp_entry.state = XARP_ENTRY_FREE;

}



/**

 * 协议栈的初始化

 */

void xnet_init (void) {

    ethernet_init();

    xarp_init();

}

无回报ARP的生成

无回报ARP包

ARP数据包

ARP包结构定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define XARP_HW_ETHER               0x1         // 以太网

#define XARP_REQUEST                0x1         // ARP请求包

#define XARP_REPLY                  0x2         // ARP响应包



typedef struct _xarp_packet_t {

    uint16_t hw_type, pro_type;                 // 硬件类型和协议类型

    uint8_t hw_len, pro_len;                    // 硬件地址长 + 协议地址长

    uint16_t opcode;                            // 请求/响应

    uint8_t sender_mac[XNET_MAC_ADDR_SIZE];     // 发送包硬件地址

    uint8_t sender_ip[XNET_IPV4_ADDR_SIZE];     // 发送包协议地址

    uint8_t target_mac[XNET_MAC_ADDR_SIZE];     // 接收方硬件地址

    uint8_t target_ip[XNET_IPV4_ADDR_SIZE];     // 接收方协议地址

}xarp_packet_t;

ARP请求函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static const xipaddr_t netif_ipaddr = XNET_CFG_NETIF_IP;

static const uint8_t ether_broadcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};  //广播地址

static uint8_t netif_mac[XNET_MAC_ADDR_SIZE];                   // mac地址

static xnet_packet_t tx_packet, rx_packet;                      // 接收与发送缓冲区

static xarp_entry_t arp_entry;                                  // 节省内存,只使用一个ARP表项



typedef enum _xnet_err_t {

    XNET_ERR_OK = 0,

    XNET_ERR_IO = -1,

}xnet_err_t;



int xarp_make_request(const xipaddr_t * ipaddr);



/**

 * 产生一个ARP请求,请求网络指定ip地址的机器发回一个ARP响应

 * @param ipaddr 请求的IP地址

 * @param packet 指向网络数据结构xnet_packet_t

 * @param arp_packet 指向28字节ARP请求/应答包xarp_packet_t

 * @return 请求结果

 */

xnet_err_t xarp_make_request(const xipaddr_t * ipaddr) {

    xarp_packet_t* arp_packet;

    xnet_packet_t * packet = xnet_alloc_for_send(sizeof(xarp_packet_t));



    arp_packet = (xarp_packet_t *)packet->data;

    arp_packet->hw_type = swap_order16(XARP_HW_ETHER);                                

    arp_packet->pro_type = swap_order16(XNET_PROTOCOL_IP);                            

    arp_packet->hw_len = XNET_MAC_ADDR_SIZE;                                          

    arp_packet->pro_len = XNET_IPV4_ADDR_SIZE;                                        

    arp_packet->opcode = swap_order16(XARP_REQUEST);                                  

    memcpy(arp_packet->sender_mac, netif_mac, XNET_MAC_ADDR_SIZE);                    

    memcpy(arp_packet->sender_ip, netif_ipaddr.array, XNET_IPV4_ADDR_SIZE);          

    memset(arp_packet->target_mac, 0, XNET_MAC_ADDR_SIZE);

    memcpy(arp_packet->target_ip, ipaddr->array, XNET_IPV4_ADDR_SIZE);

    return ethernet_out_to(XNET_PROTOCOL_ARP, ether_broadcast, packet);  //返回一个以太网帧

}

以太网初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static const xipaddr_t netif_ipaddr = XNET_CFG_NETIF_IP;

static const uint8_t ether_broadcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};  //广播地址

static uint8_t netif_mac[XNET_MAC_ADDR_SIZE];                   // mac地址



/**

 * 以太网初始化

 * @return 初始化结果

 */

static xnet_err_t ethernet_init (void) {

    xnet_err_t err = xnet_driver_open(netif_mac);

    if (err < 0) return err;



    // 开启抓包工具wireshark,能在窗口发现如下数据包抓取

    // 1    0.000000    Dell_f9:e6:77    Broadcast    ARP    42    ARP Announcement for 192.168.254.2

    return xarp_make_request(&netif_ipaddr);

}

ARP输入处理

当收到一个ARP包时, 经如下步骤进行处理:

ARP输入处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#define xipaddr_is_equal_buf(addr, buf)      (memcmp((addr)->array, (buf), XNET_IPV4_ADDR_SIZE) == 0)



/**

 * ARP输入处理

 * @param packet 输入的ARP包

 */

void xarp_in(xnet_packet_t * packet) {

    if (packet->size >= sizeof(xarp_packet_t)) {

        xarp_packet_t * arp_packet = (xarp_packet_t *) packet->data;

        uint16_t opcode = swap_order16(arp_packet->opcode);



        // 包的合法性检查

        if ((swap_order16(arp_packet->hw_type) != XARP_HW_ETHER) ||

            (arp_packet->hw_len != XNET_MAC_ADDR_SIZE) ||

            (swap_order16(arp_packet->pro_type) != XNET_PROTOCOL_IP) ||

            (arp_packet->pro_len != XNET_IPV4_ADDR_SIZE)

            || ((opcode != XARP_REQUEST) && (opcode != XARP_REPLY))) {

            return;

        }



        // 只处理发给自己的请求或响应包

        if (!xipaddr_is_equal_buf(&netif_ipaddr, arp_packet->target_ip)) {

            return;

        }



        // 根据操作码进行不同的处理

        switch (swap_order16(arp_packet->opcode)) {

            case XARP_REQUEST:  // 请求,回送响应

                // 在对方机器Ping 自己,然后看wireshark,能看到ARP请求和响应

                // 接下来,很可能对方要与自己通信,所以更新一下

                xarp_make_response(arp_packet);

                update_arp_entry(arp_packet->sender_ip, arp_packet->sender_mac);

                break;

            case XARP_REPLY:    // 响应,更新自己的表

                update_arp_entry(arp_packet->sender_ip, arp_packet->sender_mac);

                break;

        }

    }

}
在以太网层处理ARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 往上分解到各个协议处理

    xether_hdr_t* hdr = (xether_hdr_t*)packet->data;

    switch (swap_order16(hdr->protocol)) {

        case XNET_PROTOCOL_ARP:

            remove_header(packet, sizeof(xether_hdr_t));

            xarp_in(packet);

            break;

    }

ARP响应函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**

 * 生成一个ARP响应

 * @param arp_packet 指向接收到的ARP请求包xarp_packet_t

 * @param packet 指向网络数据结构xnet_packet_t

 * @return 生成结果

 */

 xnet_err_t xarp_make_response(xarp_packet_t * arp_packet) {

    xarp_packet_t* response_packet;

    xnet_packet_t * packet = xnet_alloc_for_send(sizeof(xarp_packet_t));



    response_packet = (xarp_packet_t *)packet->data;

    response_packet->hw_type = swap_order16(XARP_HW_ETHER);

    response_packet->pro_type = swap_order16(XNET_PROTOCOL_IP);

    response_packet->hw_len = XNET_MAC_ADDR_SIZE;

    response_packet->pro_len = XNET_IPV4_ADDR_SIZE;

    response_packet->opcode= swap_order16(XARP_REPLY);

    memcpy(response_packet->target_mac, arp_packet->sender_mac, XNET_MAC_ADDR_SIZE);

    memcpy(response_packet->target_ip, arp_packet->sender_ip, XNET_IPV4_ADDR_SIZE);

    memcpy(response_packet->sender_mac, netif_mac, XNET_MAC_ADDR_SIZE);

    memcpy(response_packet->sender_ip, netif_ipaddr.array, XNET_IPV4_ADDR_SIZE);

    return ethernet_out_to(XNET_PROTOCOL_ARP, ether_broadcast, packet);

}

更新ARP表项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define XARP_ENTRY_FREE                0       // ARP表项空闲

#define XARP_ENTRY_OK                1       // ARP表项解析成功

/**

 * 更新ARP表项

 * @param src_ip 源IP地址

 * @param mac_addr 对应的mac地址

 */

static void update_arp_entry(uint8_t * src_ip, uint8_t * mac_addr) {

    memcpy(arp_entry.ipaddr.array, src_ip, XNET_IPV4_ADDR_SIZE);

    memcpy(arp_entry.macaddr, mac_addr, 6);

    arp_entry.state = XARP_ENTRY_OK;

ARP表定时更新

获得当前时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef uint32_t xnet_time_t;           // 时间类型,返回当前系统跑了多少个100ms



/**

 * 获取自程序启动以来,过去了多长时间

 * @return 程序的系统时间

 */

const xnet_time_t xsys_get_time(void) {

    static uint32_t pre = 0;

    return (xnet_time_t)(clock() / CLOCKS_PER_SEC);

}

检查是否超时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**

 * 检查是否超时

 * @param time 前一时间

 * @param sec 预期超时时间,值为0时,表示获取当前时间

 * @return 0 - 未超时,1-超时

 */

int xnet_check_tmo(xnet_time_t * time, uint32_t sec) {

    xnet_time_t curr = xsys_get_time();

    if (sec == 0) {          // 如果预期超时时间为0,取当前时间, 用于初始化

        *time = curr;

        return 0;

    } else if (curr - *time >= sec) {   // 非0检查超时

        *time = curr;       // 当超时时,才更新时间

        return 1;

    }

    return 0;

}



/**

 * ARP初始化

 */

void xarp_init(void) {

    arp_entry.state = XARP_ENTRY_FREE;



    // 获取初始时间

    xnet_check_tmo(&arp_timer, 0);

}

查询是否超时, 超时则重新请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
static xnet_time_t arp_timer;                                   // ARP扫描定时



#define XARP_CFG_ENTRY_PENDING_TMO        (1)                 // ARP表项挂起超时时间

#define XARP_CFG_MAX_RETRIES            4                   // ARP表挂起时重试查询次数



#define XARP_ENTRY_FREE                0       // ARP表项空闲

#define XARP_ENTRY_OK                1       // ARP表项解析成功

#define XARP_ENTRY_RESOLVING        2       // ARP表项正在解析

#define XARP_TIMER_PERIOD           1       // ARP扫描周期,1s足够



#define XARP_CFG_ENTRY_OK_TMO            (5)                 // ARP表项超时时间

#define XARP_CFG_ENTRY_PENDING_TMO        (1)                 // ARP表项挂起超时时间

#define XARP_CFG_MAX_RETRIES            4                   // ARP表挂起时重试查询次数

/**

 * ARP表项

 */

typedef struct _xarp_entry_t {

    xipaddr_t ipaddr;                       // ip地址

    uint8_t    macaddr[XNET_MAC_ADDR_SIZE];    // mac地址

    uint8_t    state;                          // 状态位

    uint16_t tmo;                           // 当前超时

    uint8_t    retry_cnt;                      // 当前重试次数

}xarp_entry_t;



/**

 * 查询ARP表项是否超时,超时则重新请求

 */

void xarp_poll(void) {

    if (xnet_check_tmo(&arp_timer, XARP_TIMER_PERIOD)) {  

        switch (arp_entry.state) {

            case XARP_ENTRY_RESOLVING:

                if (--arp_entry.tmo == 0) {     // 重试完毕,回收

                    if (arp_entry.retry_cnt-- == 0) {

                        arp_entry.state = XARP_ENTRY_FREE;

                    } else {    // 继续重试

                        xarp_make_request(&arp_entry.ipaddr);

                        arp_entry.state = XARP_ENTRY_RESOLVING;

                        arp_entry.tmo = XARP_CFG_ENTRY_PENDING_TMO;

                    }

                }

                break;

            case XARP_ENTRY_OK:

                if (--arp_entry.tmo == 0) {     // 超时,重新请求

                    xarp_make_request(&arp_entry.ipaddr);

                    arp_entry.state = XARP_ENTRY_RESOLVING;

                    arp_entry.tmo = XARP_CFG_ENTRY_PENDING_TMO;

                }

                break;

        }

    }

}

IP协议

IP层的输入处理

IP数据包结构

IP包结构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef struct _xip_hdr_t {

    uint8_t hdr_len : 4;                // 首部长, 4字节为单位

    uint8_t version : 4;                // 版本号

    uint8_t tos;                        // 服务类型

    uint16_t total_len;                    // 总长度

    uint16_t id;                        // 标识符

    uint16_t flags_fragment;            // 标志与分段

    uint8_t ttl;                        // 存活时间

    uint8_t protocol;                    // 上层协议

    uint16_t hdr_checksum;              // 首部校验和

    uint8_t    src_ip[XNET_IPV4_ADDR_SIZE];        // 源IP

    uint8_t dest_ip[XNET_IPV4_ADDR_SIZE];        // 目标IP

}xip_hdr_t;

检验和计算

IP首部的检验和采用下面的简单计算方法:在发送方,先把IP数据报首部划分为许多16位字的序列,并把检验和字段置零。用反码算术运算把所有16位字相加后,将得到的和的反码写入检验和字段。接收方收到数据报后,将首部的所有16位字再使用反码算术运算相加一次。将得到的和取反码, 即得出接收方检验和的计算结果。若首部未发生任何变化,则此结果必为0,于是就保留这个数据报。否则即认为出差错,并将此数据报丢弃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**

 * 校验和计算

 * @param buf 校验数据区的起始地址

 * @param len 数据区的长度,以字节为单位

 * @param pre_sum 累加的之前的值,用于多次调用checksum对不同的的数据区计算出一个校验和

 * @param complement 是否对累加和的结果进行取反

 * @return 校验和结果

 */

static uint16_t checksum16(uint16_t * buf, uint16_t len, uint16_t pre_sum, int complement) {

    uint32_t checksum = pre_sum;

    uint16_t high;



    while (len > 1) {

        checksum += *buf++;

        len -= 2;

    }

    if (len > 0) {

        checksum += *(uint8_t *)buf;

    }



    // 注意,这里要不断累加。不然结果在某些情况下计算不正确

    while ((high = checksum >> 16) != 0) {

        checksum = high + (checksum & 0xffff);

    }

    return complement ? (uint16_t)~checksum : (uint16_t)checksum;

}

IP层的输入处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#define xipaddr_is_equal_buf(addr, buf)      (memcmp((addr)->array, (buf), XNET_IPV4_ADDR_SIZE) == 0)



typedef struct _xip_hdr_t {

    uint8_t hdr_len : 4;                // 首部长, 4字节为单位

    uint8_t version : 4;                // 版本号

    uint8_t tos;                        // 服务类型

    uint16_t total_len;                    // 总长度

    uint16_t id;                        // 标识符

    uint16_t flags_fragment;            // 标志与分段

    uint8_t ttl;                        // 存活时间

    uint8_t protocol;                    // 上层协议

    uint16_t hdr_checksum;              // 首部校验和

    uint8_t    src_ip[XNET_IPV4_ADDR_SIZE];        // 源IP

    uint8_t dest_ip[XNET_IPV4_ADDR_SIZE];        // 目标IP

}xip_hdr_t;



/**

 * IP层的输入处理

 * @param packet 输入的IP数据包

 */

void xip_in(xnet_packet_t * packet) {

    xip_hdr_t* iphdr = (xip_hdr_t*)packet->data;

    uint32_t total_size, header_size;

    uint16_t pre_checksum;



    // 进行一些必要性的检查:版本号要求

    if (iphdr->version != XNET_VERSION_IPV4) {

        return;

    }



    // 长度要求检查

    header_size = iphdr->hdr_len * 4;

    total_size = swap_order16(iphdr->total_len);

    if ((header_size < sizeof(xip_hdr_t)) || ((total_size < header_size) || (packet->size < total_size))) {

        return;

    }



    // 校验和要求检查

    pre_checksum = iphdr->hdr_checksum;

    iphdr->hdr_checksum = 0;

    if (pre_checksum != checksum16((uint16_t*)iphdr, header_size, 0, 1)) {

        return;

    }



    // 只处理目标IP为自己的数据包,其它广播之类的IP全部丢掉

    if (!xipaddr_is_equal_buf(&netif_ipaddr, iphdr->dest_ip)) {

        return;

    }



    // 多跟复用,分别交由ICMP、UDP、TCP处理

    switch(iphdr->protocol) {

        default:

            break;

    }

}
在以太网层处理IP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 往上分解到各个协议处理

    xether_hdr_t* hdr = (xether_hdr_t*)packet->data;

    switch (swap_order16(hdr->protocol)) {

        case XNET_PROTOCOL_IP: {

            // 以下代码是从IP包头中提取IP地址,以及从以太网包头中提取mac地址

            // 然后用其更新ARP表

            xip_hdr_t *iphdr = (xip_hdr_t *) (packet->data + sizeof(xether_hdr_t));

            update_arp_entry(iphdr->src_ip, hdr->src);

            remove_header(packet, sizeof(xether_hdr_t));

            xip_in(packet);

            break;

        }

    }

IP层的输出处理

IP包的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
typedef struct _xip_hdr_t {

    uint8_t hdr_len : 4;                // 首部长, 4字节为单位

    uint8_t version : 4;                // 版本号

    uint8_t tos;                        // 服务类型

    uint16_t total_len;                    // 总长度

    uint16_t id;                        // 标识符

    uint16_t flags_fragment;            // 标志与分段

    uint8_t ttl;                        // 存活时间

    uint8_t protocol;                    // 上层协议

    uint16_t hdr_checksum;              // 首部校验和

    uint8_t    src_ip[XNET_IPV4_ADDR_SIZE];        // 源IP

    uint8_t dest_ip[XNET_IPV4_ADDR_SIZE];        // 目标IP

}xip_hdr_t;



/**

 * IP包的输出

 * @param protocol 上层协议,ICMP、UDP或TCP

 * @param dest_ip 目的IP地址

 * @param packet 网络数据包

 * @return

 */

xnet_err_t xip_out(xnet_protocol_t protocol, xipaddr_t* dest_ip, xnet_packet_t * packet) {

    static uint32_t ip_packet_id = 0;

    xip_hdr_t * iphdr;



    add_header(packet, sizeof(xip_hdr_t));

    iphdr = (xip_hdr_t*)packet->data;

    iphdr->version = XNET_VERSION_IPV4;

    iphdr->hdr_len = sizeof(xip_hdr_t) / 4;

    iphdr->tos = 0;

    iphdr->total_len = swap_order16(packet->size);

    iphdr->id = swap_order16(ip_packet_id);

    iphdr->flags_fragment = 0;

    iphdr->ttl = XNET_IP_DEFAULT_TTL;

    iphdr->protocol = protocol;

    memcpy(iphdr->dest_ip, dest_ip->array, XNET_IPV4_ADDR_SIZE);

    memcpy(iphdr->src_ip, netif_ipaddr.array, XNET_IPV4_ADDR_SIZE);

    iphdr->hdr_checksum = 0;

    iphdr->hdr_checksum = checksum16((uint16_t *)iphdr, sizeof(xip_hdr_t), 0, 1);;



    ip_packet_id++;

    return ethernet_out(dest_ip, packet);

}

将IP数据包通过以太网发送出去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**

 * 将IP数据包通过以太网发送出去

 * @param dest_ip 目标IP地址

 * @param packet 待发送IP数据包

 * @return 发送结果

 */

static xnet_err_t ethernet_out (xipaddr_t * dest_ip, xnet_packet_t * packet) {

    xnet_err_t err;

    uint8_t * mac_addr;



    if ((err = xarp_resolve(dest_ip, &mac_addr) == XNET_ERR_OK)) { //检查ip地址是否在ARP表中

        return ethernet_out_to(XNET_PROTOCOL_IP, mac_addr, packet);

    }

    return err;

}

ICMP协议

ICMP包数据格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//ICMP数据包结构定义

typedef struct _xicmp_hdr_t {

    uint8_t type;           // 类型

    uint8_t code;            // 代码

    uint16_t checksum;        // ICMP报文的校验和

    uint16_t id;            // 标识符

    uint16_t seq;           // 序号

}xicmp_hdr_t;

ICMP包输入处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#define XICMP_CODE_ECHO_REQUEST             8           // 回显请求

#define XICMP_CODE_ECHO_REPLY               0           // 回显响应



/**

 * ICMP包输入处理

 * @param src_ip 数据包来源

 * @param packet 待处理的数据包

 */

void xicmp_in(xipaddr_t *src_ip, xnet_packet_t * packet) {

    xicmp_hdr_t* icmphdr = (xicmp_hdr_t *)packet->data;



    if ((packet->size >= sizeof(xicmp_hdr_t)) && (icmphdr->type == XICMP_CODE_ECHO_REQUEST)) {

        reply_icmp_request(icmphdr, src_ip, packet);  //发送ICMP ECHO响应

    }

}

ICMP ECHO响应

即回应ping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#define XICMP_CODE_ECHO_REQUEST             8           // 回显请求

#define XICMP_CODE_ECHO_REPLY               0           // 回显响应



/**

 * 发送ICMP ECHO响应,即回应ping

 * @param icmp_hdr 收到的icmp包头

 * @param src_ip 包的来源ip

 * @param packet 收到的数据包

 * @return 处理结果

 */

static xnet_err_t reply_icmp_request(xicmp_hdr_t * icmp_hdr, xipaddr_t* src_ip, xnet_packet_t * packet) {

    xicmp_hdr_t * replay_hdr;

    xnet_packet_t * tx = xnet_alloc_for_send(packet->size);



    replay_hdr = (xicmp_hdr_t *)tx->data;

    replay_hdr->type = XICMP_CODE_ECHO_REPLY;

    replay_hdr->code = 0;

    replay_hdr->id = icmp_hdr->id;

    replay_hdr->seq = icmp_hdr->seq;

    replay_hdr->checksum = 0;

    memcpy(((uint8_t *)replay_hdr) + sizeof(xicmp_hdr_t), ((uint8_t *)icmp_hdr) + sizeof(xicmp_hdr_t),

            packet->size - sizeof(xicmp_hdr_t));

    replay_hdr->checksum = checksum16((uint16_t*)replay_hdr, tx->size, 0, 1);

    return xip_out(XNET_PROTOCOL_ICMP, src_ip, tx);

}



typedef enum _xnet_protocol_t {

    XNET_PROTOCOL_ARP = 0x0806,     // ARP协议

    XNET_PROTOCOL_IP = 0x0800,      // IP协议

    XNET_PROTOCOL_ICMP = 1,         // ICMP协议

}xnet_protocol_t;



/**

 * IP包的输出

 * @param protocol 上层协议,ICMP、UDP或TCP

 * @param dest_ip

 * @param packet

 * @return

 */

xnet_err_t xip_out(xnet_protocol_t protocol, xipaddr_t* dest_ip, xnet_packet_t * packet) {

    static uint32_t ip_packet_id = 0;

    xip_hdr_t * iphdr;



    add_header(packet, sizeof(xip_hdr_t));

    iphdr = (xip_hdr_t*)packet->data;

    iphdr->version = XNET_VERSION_IPV4;

    iphdr->hdr_len = sizeof(xip_hdr_t) / 4;

    iphdr->tos = 0;

    iphdr->total_len = swap_order16(packet->size);

    iphdr->id = swap_order16(ip_packet_id);

    iphdr->flags_fragment = 0;

    iphdr->ttl = XNET_IP_DEFAULT_TTL;

    iphdr->protocol = protocol;

    memcpy(iphdr->dest_ip, dest_ip->array, XNET_IPV4_ADDR_SIZE);

    memcpy(iphdr->src_ip, netif_ipaddr.array, XNET_IPV4_ADDR_SIZE);

    iphdr->hdr_checksum = 0;

    iphdr->hdr_checksum = checksum16((uint16_t *)iphdr, sizeof(xip_hdr_t), 0, 1);;



    ip_packet_id++;

    return ethernet_out(dest_ip, packet);

}

IP层处理ICMP包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
switch(iphdr->protocol) {

        case XNET_PROTOCOL_ICMP:

            remove_header(packet, header_size);

            xicmp_in(&src_ip, packet);

            break;

        default:

            break;

    }

实现目标不可达的响应

ICMP的一个规则是,ICMP差错报文必须包括生成该差错报文的数据报IP首部(包含任何选项),还必须至少包括跟在该IP首部后面的前8个字节(包含源端口和目的端口)。在我们的例子中,跟在IP首部后面的前8个字节包含UDP的首部。

发送ICMP端口不可达或协议不可达的响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#define XICMP_TYPE_UNREACH                  3           // 目的不可达

#define XICMP_CODE_PORT_UNREACH             3           // 端口不可达

#define XICMP_CODE_PRO_UNREACH              2           // 协议不可达



/**

 * 发送ICMP端口不可达或协议不可达的响应

 * @param code 不可达的类型码

 * @param ip_hdr 收到的ip包

 * @return 处理结果

 */

xnet_err_t xicmp_dest_unreach(uint8_t code, xip_hdr_t *ip_hdr) {

    xicmp_hdr_t * icmp_hdr;

    xipaddr_t dest_ip;

    xnet_packet_t* packet;



    // 计算要拷贝的ip数据量

    uint16_t ip_hdr_size = ip_hdr->hdr_len * 4;

    uint16_t ip_data_size = swap_order16(ip_hdr->total_len) - ip_hdr_size;



    // RFC文档里写的是8字节。但实际测试windows上发现复制了不止8个字节

    ip_data_size = ip_hdr_size + min(ip_data_size, 8);



    // 生成数据包,然后发送

    packet = xnet_alloc_for_send(ip_data_size + sizeof(xicmp_hdr_t));

    icmp_hdr = (xicmp_hdr_t*)packet->data;

    icmp_hdr->type = XICMP_TYPE_UNREACH;

    icmp_hdr->code = code;

    icmp_hdr->checksum = 0;

    icmp_hdr->id = icmp_hdr->seq = 0;

    memcpy(((uint8_t *)icmp_hdr) + sizeof(xicmp_hdr_t), ip_hdr, ip_data_size);

    icmp_hdr->checksum = 0;

    icmp_hdr->checksum = checksum16((uint16_t *)icmp_hdr, packet->size, 0, 1);

    xipaddr_from_buf(&dest_ip, ip_hdr->src_ip);

    return xip_out(XNET_PROTOCOL_ICMP, &dest_ip, packet);

}

UDP协议

UDP控制块

控制块负责控制进程与端口之间的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define XUDP_CFG_MAX_UDP                10                  // 最大支持的UDP连接数

typedef struct _xudp_t xudp_t;

static xudp_t udp_socket[XUDP_CFG_MAX_UDP];                     // UDP连接块

typedef xnet_err_t (*xudp_handler_t)(xudp_t * udp, xipaddr_t * src_ip, uint16_t src_port, xnet_packet_t * packet);

struct _xudp_t {

    enum {

        XUDP_STATE_FREE,            // UDP未使用

        XUDP_STATE_USED,            // UDP已使用

    } state;                        // 状态



    uint16_t local_port;            // 本地端口

    xudp_handler_t handler;         // 事件处理回调

};

UDP初始化

1
2
3
4
5
6
7
8
9
10
11
/**

 * UDP初始化

 */

void xudp_init(void) {

    memset(udp_socket, 0, sizeof(udp_socket));      // Free也是0,所以没什么问题

}

控制块基本操作接口

 

打开UDP结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**

 * 打开UDP结构

 * @param handler 事件处理回调函数

 * @return 打开的xudp_t结构

 */

xudp_t* xudp_open(xudp_handler_t handler) {

    xudp_t * udp, * end;



    for (udp = udp_socket, end = &udp_socket[XUDP_CFG_MAX_UDP]; udp < end; udp++) {

        if (udp->state == XUDP_STATE_FREE) {

            udp->state = XUDP_STATE_USED;

            udp->local_port = 0;

            udp->handler = handler;

            return udp;

        }

    }

    return (xudp_t *)0;

}

关闭UDP连接

1
2
3
4
5
6
7
8
9
10
11
12
13
/**

 * 关闭UDP连接

 * @param udp 待关闭的xudp_t结构

 */

void xudp_close(xudp_t *udp) {

    udp->state = XUDP_STATE_FREE;

}

查找指定端口的UDP协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**

 * 查找指定端口对应的udp结构

 * @param port 待查找的端口

 * @return 找到的xudp_t结构

 */

xudp_t* xudp_find(uint16_t port) {

    xudp_t * udp, * end = &udp_socket[XUDP_CFG_MAX_UDP];



    for (udp = udp_socket; udp < end; udp++) {

        if ((udp->state != XUDP_STATE_FREE) && (udp->local_port == port)) {

            return udp;

        }

    }



    return (xudp_t *)0;

}

绑定控制块到端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**

 * 绑定xudp_t结构到指定端口

 * @param udp 待绑定的结构

 * @param local_port 目标端口

 * @return 绑定结果

 */

xnet_err_t xudp_bind(xudp_t *udp, uint16_t local_port) {

    xudp_t * curr, * end;



    if (local_port == 0) {

        return XNET_ERR_PARAM;

    }



    for (curr = udp_socket, end = &udp_socket[XUDP_CFG_MAX_UDP]; curr < end; curr++) {

        if ((curr != udp) && (curr->local_port == local_port)) {

            return XNET_ERR_BINDED;

        }

    }



    udp->local_port = local_port;

    return XNET_ERR_OK;

}

协议栈初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**

 * 协议栈的初始化

 */

void xnet_init (void) {

    ethernet_init();

    xarp_init();

    xip_init();

    xicmp_init();

    xudp_init();

}

UDP数据包格式

在这里插入图片描述

1
2
3
4
5
6
7
8
9
typedef struct _xudp_hdr_t {

    uint16_t src_port, dest_port;   // 源端口 + 目标端口

    uint16_t total_len;                // 整个数据包的长度

    uint16_t checksum;                // 校验和

}xudp_hdr_t;

UDP伪首部

在这里插入图片描述

计算UDP伪检验和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**

 * 计算UDP伪校验和

 * @param src_ip 源IP

 * @param dest_ip 目标IP

 * @param protocol 协议

 * @param buf 数据区

 * @param len 数据长度

 * @return 校验和结果

 */

static uint16_t checksum_peso(const xipaddr_t *src_ip, const xipaddr_t *dest_ip,

                              uint8_t protocol, uint16_t *buf, uint16_t len) {

    uint8_t zero_protocol[2] = {0, protocol};

    uint16_t c_len = swap_order16(len);



    uint32_t sum = checksum16((uint16_t *)src_ip->array, XNET_IPV4_ADDR_SIZE, 0, 0);

    sum = checksum16((uint16_t *)dest_ip->array, XNET_IPV4_ADDR_SIZE, sum, 0);

    sum = checksum16((uint16_t *)zero_protocol, 2, sum, 0);

    sum = checksum16((uint16_t *)&c_len, 2, sum, 0);

    return checksum16(buf, len, sum, 1);

}

UDP输入处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**

 * UDP输入处理

 * @param udp 待处理的UDP

 * @param src_ip 数据包来源

 * @param packet 数据包结构

 */

void xudp_in(xudp_t *udp, xipaddr_t *src_ip,xnet_packet_t * packet) {

    xudp_hdr_t * udp_hdr = (xudp_hdr_t *)packet->data;

    uint16_t pre_checksum;

    uint16_t src_port;



    if ((packet->size < sizeof(xudp_hdr_t)) || (packet->size < swap_order16(udp_hdr->total_len))) {

        return;

    }



    pre_checksum = udp_hdr->checksum;

    udp_hdr->checksum = 0;

    if (pre_checksum != 0) {

        uint16_t checksum = checksum_peso(src_ip, &netif_ipaddr, XNET_PROTOCOL_UDP,    //typedef XNET_PROTOCOL_UDP 17  

                                          (uint16_t *) udp_hdr, swap_order16(udp_hdr->total_len));

        checksum = (checksum == 0) ? 0xFFFF : checksum;

        if (checksum != pre_checksum) {

            return;

        }

    }



    src_port = swap_order16(udp_hdr->src_port);

    remove_header(packet, sizeof(xudp_hdr_t));

    if (udp->handler) {

        udp->handler(udp, src_ip, src_port, packet);

    }

}

在IP层处理UDP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
switch(iphdr->protocol) {

        case XNET_PROTOCOL_UDP:

            if (packet->size >= sizeof(xudp_hdr_t)) {

                xudp_hdr_t *udp_hdr = (xudp_hdr_t *) (packet->data + header_size);

                xudp_t *udp = xudp_find(swap_order16(udp_hdr->dest_port));  //获得与控制相应端口的控制块

                if (udp) {

                    truncate_packet(packet, total_size);

                    remove_header(packet, header_size);

                    xudp_in(udp, &src_ip, packet);

                } else {

                    xicmp_dest_unreach(XICMP_CODE_PORT_UNREACH, iphdr);

                }

            }

            break;

}

发送UDP数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**

 * 发送一个UDP数据包

 * @param udp udp结构

 * @param dest_ip 目标ip

 * @param dest_port 目标端口

 * @param packet 待发送的包

 * @return 发送结果

 */

int xudp_out(xudp_t* udp, xipaddr_t * dest_ip, uint16_t dest_port, xnet_packet_t * packet) {

    xudp_hdr_t* udp_hdr;

    uint16_t checksum;



    add_header(packet, sizeof(xudp_hdr_t));

    udp_hdr = (xudp_hdr_t*)packet->data;

    udp_hdr->src_port = swap_order16(udp->local_port);

    udp_hdr->dest_port = swap_order16(dest_port);

    udp_hdr->total_len = swap_order16(packet->size);

    udp_hdr->checksum = 0;

    checksum = checksum_peso(&netif_ipaddr, dest_ip, XNET_PROTOCOL_UDP, (uint16_t *) udp_hdr, packet->size);

    udp_hdr->checksum = (checksum == 0) ? 0xFFFF : checksum;

    return xip_out(XNET_PROTOCOL_UDP, dest_ip, packet);

}

时间返回功能

客户端向服务器通过发送一个UDP数据包申请获得当前时间, 服务器返回一个UDP数据包, 其中包含当前时间.

获取当前时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <string.h>

#include <time.h>

#include "xnet_tiny.h"

#define XSERVER_DATETIME_H



//typedef xnet_err_t (*xudp_handler_t)(xudp_t * udp, xipaddr_t * src_ip, uint16_t src_port, xnet_packet_t * packet);

xnet_err_t xserver_datetime_create(uint16_t port);  



#define TIME_STR_SIZE       128         // 时间字符串存储长度



static xnet_err_t datetime_handler (xudp_t * udp, xipaddr_t * src_ip, uint16_t src_port, xnet_packet_t * packet) {

    xnet_packet_t * tx_packet;

    time_t rawtime;

    const struct tm * timeinfo;

    size_t str_size;



    tx_packet = xnet_alloc_for_send(TIME_STR_SIZE);



    // 参见:http://www.cplusplus.com/reference/ctime/localtime/

    time (&rawtime);

    timeinfo = localtime (&rawtime);



    // strftime参见:http://www.cplusplus.com/reference/ctime/strftime/

    // Weekday, Month Day, Year Time-Zone

    str_size = strftime((char *)tx_packet->data, TIME_STR_SIZE, "%A, %B %d, %Y %T-%z", timeinfo);



    return xudp_out(udp, src_ip, src_port, tx_packet);

}



//通过在main函数内调用 xserver_datetime_create(13); 将13端口与返回时间应用绑定

//到时候客户端只需要发送UDP到13端口就可以通过控制块控制13端口的时间应用返回时间

xnet_err_t xserver_datetime_create(uint16_t port) {

    xnet_err_t err;



    xudp_t* udp = xudp_open(datetime_handler);  

    if (udp == (xudp_t*)0) {

        return -1;

    }



    err = xudp_bind(udp, port);

    if (err < 0) {

        xudp_close(udp);

        return err;

    }

    return 0;

}

TCP协议

TCP控制块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#define XTCP_CFG_MAX_TCP                        40                  // 最大支持的TCP连接数



typedef enum _xtcp_state_t {

    XTCP_STATE_FREE,

    XTCP_STATE_CLOSED,

    XTCP_STATE_LISTEN,

}xtcp_state_t;



typedef enum _xtcp_conn_state_t {

    XTCP_CONN_CONNECTED,

    XTCP_CONN_DATA_RECV,

    XTCP_CONN_CLOSED,

}xtcp_conn_state_t;



typedef struct _xtcp_t xtcp_t;

typedef xnet_err_t(*xtcp_handler_t)(xtcp_t* tcp, xtcp_conn_state_t event);

struct _xtcp_t {

    xtcp_state_t state;                 // 状态

    uint16_t local_port, remote_port;   // 本地端口 + 源端口

    xipaddr_t remote_ip;                // 源IP

    xtcp_handler_t handler;             // 事件处理回调

};

TCP控制块基本操作接口

分配一个TCP控制块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**

 * 分配一个tcp连接块

 * @return 分配结果,0-分配失败

 */

static xtcp_t * tcp_alloc(void) {

    xtcp_t * tcp, * end;



    for (tcp = tcp_socket, end = tcp_socket + XTCP_CFG_MAX_TCP; tcp < end; tcp++) {

        if (tcp->state == XTCP_STATE_FREE) {

            tcp->state = XTCP_STATE_CLOSED;

            tcp->local_port = 0;

            tcp->remote_port = 0;

            tcp->remote_ip.addr = 0;

            tcp->handler = (xtcp_handler_t)0;

            return tcp;

        }

    }



    return (xtcp_t *)0;

}

释放一个连接块

1
2
3
4
5
6
7
8
9
10
11
12
13
/**

 * 释放一个连接块

 * @param tcp 待释放的

 */

static void tcp_free(xtcp_t* tcp) {

    tcp->state = XTCP_STATE_FREE;

}

寻找TCP控制块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**

 * 根据远端的端口、ip找一个对应的tcp连接进行处理。

 * 优先找端口、IP全匹配的,其次找处于监听状态的

 * @param remote_ip

 * @param remote_port

 * @param local_port

 * @return

 */

static xtcp_t* tcp_find(xipaddr_t *remote_ip, uint16_t remote_port, uint16_t local_port) {

    xtcp_t * tcp, * end;

    xtcp_t * founded_tcp = (xtcp_t *)0;



    for (tcp = tcp_socket, end = tcp_socket + XTCP_CFG_MAX_TCP; tcp < end; tcp++) {

        if ((tcp->state == XTCP_STATE_FREE) || (tcp->local_port != local_port)) {

            continue;

        }



        if (xipaddr_is_equal(remote_ip, &tcp->remote_ip) && (remote_port == tcp->remote_port)) {

            return tcp;     // 优先,远程的端口和ip完全相同,立即返回

        }



        if (tcp->state == XTCP_STATE_LISTEN) {

            founded_tcp = tcp;  // 没有,默认使用监听端口

        }

    }



    return founded_tcp;

}

TCP初始化

1
2
3
4
5
6
7
8
9
10
11
/**

 * TCP初始化

 */

void xtcp_init(void) {

    memset(tcp_socket, 0, sizeof(tcp_socket));

}

打开TCP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**

 * 打开TCP

 */

xtcp_t * xtcp_open(xtcp_handler_t handler) {

    xtcp_t * tcp = tcp_alloc();

    if (!tcp) return (xtcp_t *)0;



    tcp->state = XTCP_STATE_CLOSED;

    tcp->handler = handler;

    return tcp;

}

TCP控制块绑定端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**

 * 建立tcp与指定本地端口的关联,使得其能够处理来自该端口的包

 * 以及通过该端口发送数据包

 */

xnet_err_t xtcp_bind(xtcp_t* tcp, uint16_t local_port) {

    xtcp_t * curr, * end;

    for (curr = tcp_socket, end = &tcp_socket[XTCP_CFG_MAX_TCP]; curr < end; curr++) {

        if ((curr != tcp) && (curr->local_port == local_port)) {

            return XNET_ERR_BINDED;

        }

    }



    tcp->local_port = local_port;

    return XNET_ERR_OK;

}

TCP进入监听状态

1
2
3
4
5
6
7
8
9
10
11
12
13
/**

 * 控制tcp进入监听状态

 */

xnet_err_t xtcp_listen(xtcp_t * tcp) {

    tcp->state = XTCP_STATE_LISTEN;

    return XNET_ERR_OK;

}

关闭TCP连接

1
2
3
4
5
6
7
8
9
10
11
12
13
/**

 * 关掉tcp连接

 */

xnet_err_t xtcp_close(xtcp_t *tcp) {

    tcp_free(tcp);

    return XNET_ERR_OK;

}