[TC8测试]TCP_FLAGS_PROCESSING_06

测试目的

验证TCP在 TIME-WAIT 状态下,对于重新传输的FIN段,是否能够发送FIN/ACK响应,并重新启动2*MSL超时计时器。

UT测试方法

Precondition :

DUT在TCP-closed状态下收到ACK回复RST

Step to reproduce:

Step1,使DUT进入Time-WAIT状态

Step2,等待1倍MSL时间,发送FIN报文

Step3,等待1.5倍MSL时间,再次发送FIN报文

Expected results:

At step2,DUT能够发送ACK

At step3,DUT能够发送ACK

非UT验证方式

Precondition:

一台Ubuntu电脑,T1转RJ45转换器,Ubuntu电脑可与DUT正常通信

Step to reproduce:

1、在Ubuntu系统配置vlan2网卡,ip=172.16.2.97

打开Ubuntu系统的wireshark软件,持续监控报文

2、编译EthUnitTest源码,执行如下

在Ubuntu系统中使用命令gcc -o server main.cpp -lpthread编译EthUnitTest源码

在Ubuntu系统中运行server

3、在DUT系统执行如下

4、当DUT执行完close接口,发送FIN后,立即在Ubuntu端输入如下命令

bash 复制代码
sudo iptables -A INPUT -s 172.16.2.99 -d 172.16.2.97 -p tcp --tcp-flags ACK ACK -j DROP

以此过滤DUT发给Ubuntu的ACK报文,这样当Ubuntu收到FIN+ACK后,调用close接口回复FIN+ACK时,DUT回复的ACK报文就会被过滤,导致Ubuntu测试程序持续重传FIN+ACK,DUT测试程序会保持TIME_WAIT状态。

5、在Ubuntu终端按Ctrl+C退出程序,查看wireshark程序中交互的端口号和seq值、ack值,然后执行如下

报文解析

报文显示DUT在2MSL后收到Tester发送的FIN+ACK报文后仍然回复ACK,说明2MSL定时器被重置,满足TC8规范。

也可以通过netstat工具监控连接状态,从而发现DUT的测试程序在2MSL后仍保持TIME_WAIT状态,也说明2MSL定时器被重置,满足TC8规范。

测试源码

cpp 复制代码
/**
* 多模式网络工具 - main.cpp 与 main_tester.cpp 合并版
*
* 用法: sudo ./EthUnitTest <local_ip> <local_port> <remote_ip> <remote_port>
* 示例: sudo ./EthUnitTest 172.16.2.97 55000 172.16.2.98 55001
*
* 交互命令:
*   1 - listen_on_server (listen on local address, support raw sending)
*   2 - connect_to_server (send out-of-window SYN after 10s)
*   3 - connect_sendPSH_client (send invalid-ACK data segment after 10s)
*   4 - raw packet sender (no connection, send FIN/PSH/SA/SYN/ACK)
*   5 - time_wait_send_raw (establish connection, then send FA/ACK/SA/SYN/SR)
*   6 - last_ack_test_server (bind local, recv FIN, close -> LAST_ACK)
*   7 - last_ack_test_client (connect remote, support close/FA/ACK/SA)
*   0 - quit
*
* 编译: gcc -o EthUnitTest main2.cpp -lpthread
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
static char local_ip[64];
static int  local_port;
static char remote_ip[64];
static int  remote_port;
/* 计算校验和 */
static unsigned short checksum(unsigned short *buf, int len) {
    unsigned long sum = 0;
    while (len > 1) {
        sum += *buf++;
        len -= 2;
    }
    if (len == 1)
        sum += *(unsigned char *)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}
/* 发送原始 TCP 报文(自定义标志、seq、ack、数据) */
static void send_raw_tcp(const char *src_ip, int src_port,
                        const char *dst_ip, int dst_port,
                        uint32_t seq, uint32_t ack,
                        uint8_t flags, const unsigned char *payload, int payload_len) {
    int raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (raw_sock < 0) {
        perror("socket raw");
        return;
    }
    // IP 头
    struct iphdr iph;
    memset(&iph, 0, sizeof(iph));
    iph.version = 4;
    iph.ihl = 5;
    iph.tot_len = htons(sizeof(iph) + sizeof(struct tcphdr) + payload_len);
    iph.id = htons(rand() % 65535);
    iph.ttl = 64;
    iph.protocol = IPPROTO_TCP;
    inet_pton(AF_INET, src_ip, &iph.saddr);
    inet_pton(AF_INET, dst_ip, &iph.daddr);
    iph.check = 0;
    iph.check = checksum((unsigned short *)&iph, sizeof(iph));
    // TCP 头
    struct tcphdr tcph;
    memset(&tcph, 0, sizeof(tcph));
    tcph.source = htons(src_port);
    tcph.dest = htons(dst_port);
    tcph.seq = htonl(seq);
    tcph.ack_seq = htonl(ack);
    tcph.doff = 5;  // 20字节头
    tcph.fin = (flags & 0x01) ? 1 : 0;
    tcph.syn = (flags & 0x02) ? 1 : 0;
    tcph.rst = (flags & 0x04) ? 1 : 0;
    tcph.psh = (flags & 0x08) ? 1 : 0;
    tcph.ack = (flags & 0x10) ? 1 : 0;
    tcph.urg = (flags & 0x20) ? 1 : 0;
    tcph.window = htons(65535);
    tcph.check = 0;
    // 伪首部
    struct pseudo_header {
        uint32_t src_addr;
        uint32_t dst_addr;
        uint8_t zero;
        uint8_t protocol;
        uint16_t tcp_len;
    } ps;
    ps.src_addr = iph.saddr;
    ps.dst_addr = iph.daddr;
    ps.zero = 0;
    ps.protocol = IPPROTO_TCP;
    ps.tcp_len = htons(sizeof(tcph) + payload_len);
    int total_ps_len = sizeof(ps) + sizeof(tcph) + payload_len;
    unsigned char *ps_buf = (unsigned char *)malloc(total_ps_len);
    memcpy(ps_buf, &ps, sizeof(ps));
    memcpy(ps_buf + sizeof(ps), &tcph, sizeof(tcph));
    if (payload && payload_len > 0)
        memcpy(ps_buf + sizeof(ps) + sizeof(tcph), payload, payload_len);
    tcph.check = checksum((unsigned short *)ps_buf, total_ps_len);
    free(ps_buf);
    // 组装完整报文
    int packet_len = sizeof(iph) + sizeof(tcph) + payload_len;
    unsigned char *packet = (unsigned char *)malloc(packet_len);
    memcpy(packet, &iph, sizeof(iph));
    memcpy(packet + sizeof(iph), &tcph, sizeof(tcph));
    if (payload && payload_len > 0)
        memcpy(packet + sizeof(iph) + sizeof(tcph), payload, payload_len);
    struct sockaddr_in dest;
    dest.sin_family = AF_INET;
    inet_pton(AF_INET, dst_ip, &dest.sin_addr);
    if (sendto(raw_sock, packet, packet_len, 0,
              (struct sockaddr *)&dest, sizeof(dest)) < 0) {
        perror("sendto");
    } else {
        char flags_str[10] = {0};
        if (flags & 0x01) strcat(flags_str, "FIN");
        if (flags & 0x02) strcat(flags_str, "SYN");
        if (flags & 0x04) strcat(flags_str, "RST");
        if (flags & 0x08) strcat(flags_str, "PSH");
        if (flags & 0x10) strcat(flags_str, "ACK");
        printf("[Raw] Sent %s: %s:%d -> %s:%d, seq=%u, ack=%u, len=%d\n",
              flags_str, src_ip, src_port, dst_ip, dst_port, seq, ack, payload_len);
    }
    free(packet);
    close(raw_sock);
}
/* PSH2 命令实现:输入是否循环(1 循环 / 0 单次)与数据长度 len;循环时再输入循环次数,
   按 len 构造数据,以当前 seq/ack 无间隔连续发送 PSH+ACK 报文,每次发送后 seq 前进 len */
static void send_psh2(const char *src_ip, int src_port,
                      const char *dst_ip, int dst_port,
                      uint32_t *seq, uint32_t ack) {
    int loop_mode, data_len, loop_cnt = 1, i;
    char line[32];

    printf("Enter loop mode (1: loop, 0: send once): ");
    fflush(stdout);
    if (fgets(line, sizeof(line), stdin) == NULL) {
        printf("Input error.\n");
        return;
    }
    if (sscanf(line, "%d", &loop_mode) != 1 || (loop_mode != 0 && loop_mode != 1)) {
        printf("Invalid loop mode. Valid: 0 or 1.\n");
        return;
    }

    printf("Enter data len (e.g., 100): ");
    fflush(stdout);
    if (fgets(line, sizeof(line), stdin) == NULL) {
        printf("Input error.\n");
        return;
    }
    if (sscanf(line, "%d", &data_len) != 1 || data_len <= 0 || data_len > 65495) {
        printf("Invalid data len. Valid range: 1 ~ 65495.\n");
        return;
    }

    if (loop_mode == 1) {
        printf("Enter loop count (e.g., 10): ");
        fflush(stdout);
        if (fgets(line, sizeof(line), stdin) == NULL) {
            printf("Input error.\n");
            return;
        }
        if (sscanf(line, "%d", &loop_cnt) != 1 || loop_cnt <= 0) {
            printf("Invalid loop count. Must be a positive number.\n");
            return;
        }
    }

    unsigned char *payload = (unsigned char *)malloc(data_len);
    if (!payload) {
        printf("malloc failed.\n");
        return;
    }
    memset(payload, 'A', data_len);
    if (loop_mode == 1)
        printf("[PSH2] Sending %d times: seq=%u, ack=%u, data_len=%d\n", loop_cnt, *seq, ack, data_len);
    else
        printf("[PSH2] Sending once: seq=%u, ack=%u, data_len=%d\n", *seq, ack, data_len);
    for (i = 0; i < loop_cnt; i++) {
        send_raw_tcp(src_ip, src_port, dst_ip, dst_port, *seq, ack, 0x18, payload, data_len);
        *seq += data_len;
    }
    free(payload);
}
/* 函数1:监听模式,建立连接后可通过命令发送原始 TCP 报文 */
static void listen_on_server(const char *ip, int port) {
    int listen_fd, reuse = 1;
    struct sockaddr_in addr;

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) { perror("socket"); return; }
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if (inet_pton(AF_INET, ip, &addr.sin_addr) <= 0) { perror("inet_pton"); close(listen_fd); return; }
    if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); close(listen_fd); return; }
    if (listen(listen_fd, 128) < 0) { perror("listen"); close(listen_fd); return; }
    printf("[Server] Listening on %s:%d\n", ip, port);

    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len);
    if (client_fd < 0) { perror("accept"); close(listen_fd); return; }

    char client_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
    int client_port = ntohs(client_addr.sin_port);
    printf("[Server] New connection from %s:%d (fd=%d)\n", client_ip, client_port, client_fd);

    struct sockaddr_in local_addr;
    socklen_t local_len = sizeof(local_addr);
    char local_ip_str[INET_ADDRSTRLEN];
    int local_port_actual = port;
    if (getsockname(client_fd, (struct sockaddr*)&local_addr, &local_len) == 0) {
        inet_ntop(AF_INET, &local_addr.sin_addr, local_ip_str, sizeof(local_ip_str));
        local_port_actual = ntohs(local_addr.sin_port);
    } else {
        strncpy(local_ip_str, ip, sizeof(local_ip_str) - 1);
        local_ip_str[sizeof(local_ip_str) - 1] = '\0';
    }

    printf("\n=== Listen Raw Sender Mode (Established Connection) ===\n");
    printf("Commands:\n");
    printf("  FIN   - send FIN packet\n");
    printf("  SA    - send SYN+ACK packet\n");
    printf("  ACK   - send ACK packet\n");
    printf("  RST   - send RST packet\n");
    printf("  PSH   - send PSH+ACK packet (manually enter seq & ack)\n");
    printf("  PSH2  - send PSH+ACK data segments (enter loop mode 1/0 & data len)\n");
    printf("  quit  - close connection and return to main menu\n");

    uint32_t seq = rand();
    uint32_t ack = rand();
    char cmd[16];

    while (1) {
        printf("listen> ");
        fflush(stdout);
        if (fgets(cmd, sizeof(cmd), stdin) == NULL) break;
        cmd[strcspn(cmd, "\n")] = '\0';

        if (strcmp(cmd, "quit") == 0) {
            printf("Closing connection.\n");
            break;
        } else if (strcmp(cmd, "FIN") == 0) {
            send_raw_tcp(local_ip_str, local_port_actual, client_ip, client_port, seq, ack, 0x01, NULL, 0);
            seq++;
        } else if (strcmp(cmd, "SA") == 0) {
            send_raw_tcp(local_ip_str, local_port_actual, client_ip, client_port, seq, ack, 0x12, NULL, 0);
            seq++;
        } else if (strcmp(cmd, "ACK") == 0) {
            send_raw_tcp(local_ip_str, local_port_actual, client_ip, client_port, seq, ack, 0x10, NULL, 0);
        } else if (strcmp(cmd, "RST") == 0) {
            send_raw_tcp(local_ip_str, local_port_actual, client_ip, client_port, seq, ack, 0x04, NULL, 0);
        } else if (strcmp(cmd, "PSH") == 0) {
            uint32_t user_seq, user_ack;
            char line[64];
            printf("Enter seq and ack (e.g., 12345 67890): ");
            fflush(stdout);
            if (fgets(line, sizeof(line), stdin) == NULL) {
                printf("Input error.\n");
                continue;
            }
            if (sscanf(line, "%u %u", &user_seq, &user_ack) != 2) {
                printf("Invalid input. Expected two numbers.\n");
                continue;
            }
            unsigned char payload[] = "ABCDE";
            int payload_len = sizeof(payload);
            printf("[Server] Sending PSH+ACK with seq=%u, ack=%u\n", user_seq, user_ack);
            send_raw_tcp(local_ip_str, local_port_actual, client_ip, client_port, user_seq, user_ack, 0x18, payload, payload_len);
        } else if (strcmp(cmd, "PSH2") == 0) {
            send_psh2(local_ip_str, local_port_actual, client_ip, client_port, &seq, ack);
        } else {
            printf("Unknown command. Available: FIN, SA, ACK, RST, PSH, PSH2, quit\n");
        }
    }
    close(client_fd);
    close(listen_fd);
}
/* 函数2:连接后支持发送窗口外 SYN 或 invalid-ACK 数据段,PSH 命令支持手动输入 seq/ack */
static void connect_to_server(const char *ip, int port) {
    int sockfd;
    struct sockaddr_in addr, local_addr;
    socklen_t addr_len = sizeof(local_addr);
    char src_ip_str[INET_ADDRSTRLEN] = {0};
    int src_port = 0;
    int have_local_addr = 0;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { perror("socket"); return; }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if (inet_pton(AF_INET, ip, &addr.sin_addr) <= 0) { perror("inet_pton"); close(sockfd); return; }

    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("connect"); close(sockfd); return; }

    if (getsockname(sockfd, (struct sockaddr*)&local_addr, &addr_len) == 0) {
        inet_ntop(AF_INET, &local_addr.sin_addr, src_ip_str, sizeof(src_ip_str));
        src_port = ntohs(local_addr.sin_port);
        printf("[Client] Connected to %s:%d (local %s:%d)\n", ip, port, src_ip_str, src_port);
        have_local_addr = 1;
    } else {
        printf("[Client] Connected to %s:%d\n", ip, port);
    }

    if (!have_local_addr) {
        strncpy(src_ip_str, local_ip, sizeof(src_ip_str) - 1);
        src_ip_str[sizeof(src_ip_str) - 1] = '\0';
        src_port = local_port;
    }

    printf("\n=== Connected Client Mode ===\n");
    printf("Commands:\n");
    printf("  OSYN  - send out-of-window SYN (seq=0x7fffffff)\n");
    printf("  PSH   - send invalid-ACK data segment (PSH+ACK) with manually entered seq & ack\n");
    printf("  PSH2  - send PSH+ACK data segments (enter loop mode 1/0 & data len)\n");
    printf("  FA    - send FIN+ACK packet\n");
    printf("  ACK   - send ACK packet\n");
    printf("  SA    - send SYN+ACK packet\n");
    printf("  SYN   - send SYN packet\n");
    printf("  SR    - send SYN+RST packet\n");
    printf("  quit  - close connection and return to main menu\n");

    uint32_t seq = rand();
    uint32_t ack = rand();
    char cmd[16];
    char buf[1024];

    while (1) {
        ssize_t n = recv(sockfd, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT);
        if (n == 0) {
            printf("[Client] Remote closed connection\n");
            break;
        } else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
            perror("recv");
            break;
        }

        printf("client> ");
        fflush(stdout);
        if (fgets(cmd, sizeof(cmd), stdin) == NULL) break;
        cmd[strcspn(cmd, "\n")] = '\0';

        if (strcmp(cmd, "quit") == 0) {
            printf("Closing connection.\n");
            break;
        } else if (strcmp(cmd, "OSYN") == 0) {
            uint32_t out_of_window_seq = 0x7fffffff;
            printf("[Client] Sending SYN with seq=%u (out of window)\n", out_of_window_seq);
            send_raw_tcp(src_ip_str, src_port, ip, port, out_of_window_seq, 0, 0x02, NULL, 0);
        } else if (strcmp(cmd, "PSH") == 0) {
            uint32_t snd_nxt, ack_val;
            char line[64];
            printf("Enter seq and ack (e.g., 12345 67890): ");
            fflush(stdout);
            if (fgets(line, sizeof(line), stdin) == NULL) {
                printf("Input error.\n");
                continue;
            }
            if (sscanf(line, "%u %u", &snd_nxt, &ack_val) != 2) {
                printf("Invalid input. Expected two numbers.\n");
                continue;
            }
            unsigned char payload[] = "ABCDE";
            int payload_len = sizeof(payload);
            printf("[Client] Sending PSH with seq=%u, ack=%u\n", snd_nxt, ack_val);
            send_raw_tcp(src_ip_str, src_port, ip, port, snd_nxt, ack_val, 0x18, payload, payload_len);
        } else if (strcmp(cmd, "PSH2") == 0) {
            send_psh2(src_ip_str, src_port, ip, port, &seq, ack);
        } else if (strcmp(cmd, "FA") == 0) {
            send_raw_tcp(src_ip_str, src_port, ip, port, seq, ack, 0x11, NULL, 0);
            seq++;
        } else if (strcmp(cmd, "ACK") == 0) {
            send_raw_tcp(src_ip_str, src_port, ip, port, seq, ack, 0x10, NULL, 0);
        } else if (strcmp(cmd, "SA") == 0) {
            send_raw_tcp(src_ip_str, src_port, ip, port, seq, ack, 0x12, NULL, 0);
            seq++;
        } else if (strcmp(cmd, "SYN") == 0) {
            send_raw_tcp(src_ip_str, src_port, ip, port, seq, ack, 0x02, NULL, 0);
            seq++;
        } else if (strcmp(cmd, "SR") == 0) {
            send_raw_tcp(src_ip_str, src_port, ip, port, seq, ack, 0x06, NULL, 0);
            seq++;
        } else {
            printf("Unknown command. Available: OSYN, PSH, PSH2, FA, ACK, SA, SYN, SR, quit\n");
        }
    }
    close(sockfd);
}

/* 函数3:直接发送原始报文(不建立连接),支持用户交互 */
static void send_raw_mode(const char *src_ip, int src_port,
                          const char *dst_ip, int dst_port) {
    printf("\n=== Raw Packet Sender Mode (No Connection) ===\n");
    printf("Source: %s:%d, Destination: %s:%d\n", src_ip, src_port, dst_ip, dst_port);
    printf("Commands:\n");
    printf("  FIN   - send FIN packet (manually enter seq & ack)\n");
    printf("  FA    - send FIN+ACK packet (manually enter seq & ack)\n");
    printf("  PSH   - send PSH+ACK packet (manually enter seq & ack, payload \"ABCDE\")\n");
    printf("  PSH2  - send PSH+ACK data segments (enter loop mode 1/0 & data len)\n");
    printf("  SA    - send SYN+ACK packet\n");
    printf("  SYN   - send SYN packet\n");
    printf("  ACK   - send ACK packet\n");
    printf("  quit  - return to main menu\n");

    uint32_t seq = rand();
    uint32_t ack = 0;
    char cmd[16];

    while (1) {
        printf("raw> ");
        fflush(stdout);
        if (fgets(cmd, sizeof(cmd), stdin) == NULL) break;
        cmd[strcspn(cmd, "\n")] = '\0';

        if (strcmp(cmd, "quit") == 0) {
            printf("Exiting raw mode.\n");
            break;
        } else if (strcmp(cmd, "FIN") == 0) {
            uint32_t user_seq, user_ack;
            char line[64];
            printf("Enter seq and ack (e.g., 12345 67890): ");
            fflush(stdout);
            if (fgets(line, sizeof(line), stdin) == NULL) {
                printf("Input error.\n");
                continue;
            }
            if (sscanf(line, "%u %u", &user_seq, &user_ack) != 2) {
                printf("Invalid input. Expected two numbers.\n");
                continue;
            }
            printf("[Raw] Sending FIN with seq=%u, ack=%u\n", user_seq, user_ack);
            send_raw_tcp(src_ip, src_port, dst_ip, dst_port, user_seq, user_ack, 0x01, NULL, 0);
        } else if (strcmp(cmd, "FA") == 0) {
            uint32_t user_seq, user_ack;
            char line[64];
            printf("Enter seq and ack (e.g., 12345 67890): ");
            fflush(stdout);
            if (fgets(line, sizeof(line), stdin) == NULL) {
                printf("Input error.\n");
                continue;
            }
            if (sscanf(line, "%u %u", &user_seq, &user_ack) != 2) {
                printf("Invalid input. Expected two numbers.\n");
                continue;
            }
            printf("[Raw] Sending FIN+ACK with seq=%u, ack=%u\n", user_seq, user_ack);
            send_raw_tcp(src_ip, src_port, dst_ip, dst_port, user_seq, user_ack, 0x11, NULL, 0);
        } else if (strcmp(cmd, "PSH") == 0) {
            uint32_t user_seq, user_ack;
            char line[64];
            printf("Enter seq and ack (e.g., 12345 67890): ");
            fflush(stdout);
            if (fgets(line, sizeof(line), stdin) == NULL) {
                printf("Input error.\n");
                continue;
            }
            if (sscanf(line, "%u %u", &user_seq, &user_ack) != 2) {
                printf("Invalid input. Expected two numbers.\n");
                continue;
            }
            unsigned char payload[] = "ABCDE";
            int payload_len = sizeof(payload);
            printf("[Raw] Sending PSH+ACK with seq=%u, ack=%u, payload \"ABCDE\"\n", user_seq, user_ack);
            send_raw_tcp(src_ip, src_port, dst_ip, dst_port, user_seq, user_ack, 0x18, payload, payload_len);
        } else if (strcmp(cmd, "PSH2") == 0) {
            send_psh2(src_ip, src_port, dst_ip, dst_port, &seq, ack);
        } else if (strcmp(cmd, "SA") == 0) {
            send_raw_tcp(src_ip, src_port, dst_ip, dst_port, seq, ack, 0x12, NULL, 0);
            seq++;
        } else if (strcmp(cmd, "SYN") == 0) {
            send_raw_tcp(src_ip, src_port, dst_ip, dst_port, seq, ack, 0x02, NULL, 0);
            seq++;
        } else if (strcmp(cmd, "ACK") == 0) {
            send_raw_tcp(src_ip, src_port, dst_ip, dst_port, seq, ack, 0x10, NULL, 0);
            // ACK 不消耗序列号
        } else {
            printf("Unknown command. Available: FIN, FA, PSH, PSH2, SA, SYN, ACK, quit\n");
        }
    }
}

/* 函数4:bind local_ip:local_port,建立连接后收到对端 FIN,调用 close 进入 LAST_ACK 状态 */
static void last_ack_test_server(const char *bind_ip, int bind_port) {
    int listen_fd, reuse = 1;
    struct sockaddr_in addr;
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) { perror("socket"); return; }
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(bind_port);
    if (inet_pton(AF_INET, bind_ip, &addr.sin_addr) <= 0) { perror("inet_pton"); close(listen_fd); return; }
    if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); close(listen_fd); return; }
    if (listen(listen_fd, 128) < 0) { perror("listen"); close(listen_fd); return; }
    printf("[Server] Listening on %s:%d\n", bind_ip, bind_port);
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len);
    if (client_fd < 0) { perror("accept"); close(listen_fd); return; }
    char client_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
    int client_port = ntohs(client_addr.sin_port);
    printf("[Server] Accepted connection from %s:%d\n", client_ip, client_port);
    printf("[Server] Waiting for peer to send FIN...\n");
    char buf[1024];
    while (1) {
        ssize_t n = recv(client_fd, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT);
        if (n == 0) {
            printf("[Server] Peer sent FIN, calling close() 10 seconds later to enter LAST_ACK...\n");
            sleep(10);
            close(client_fd);
            printf("[Server] Entered LAST_ACK state, keeping for 6000000 seconds...\n");
            sleep(6000000);
            break;
        } else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
            perror("recv");
            break;
        }
        sleep(1);
    }
    close(listen_fd);
}
/* 函数7:last_ack_test_client: 连接后,支持 close(正常关闭但不退出),支持 FA/ACK/SA 原始报文 */
static void last_ack_test_client(const char *dst_ip, int dst_port) {
    int sockfd;
    struct sockaddr_in addr, local_addr;
    socklen_t addr_len = sizeof(local_addr);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { perror("socket"); return; }
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(dst_port);
    if (inet_pton(AF_INET, dst_ip, &addr.sin_addr) <= 0) { perror("inet_pton"); close(sockfd); return; }
    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("connect");
        close(sockfd);
        return;
    }
    if (getsockname(sockfd, (struct sockaddr*)&local_addr, &addr_len) == 0) {
        char src_ip_str[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &local_addr.sin_addr, src_ip_str, sizeof(src_ip_str));
        int src_port = ntohs(local_addr.sin_port);
        printf("[Client] Connected to %s:%d (local %s:%d)\n", dst_ip, dst_port, src_ip_str, src_port);
        printf("\n=== Last ACK Test Client ===\n");
        printf("Commands:\n");
        printf("  close - call close() to shutdown connection normally (but stay in this mode)\n");
        printf("  FA    - send FIN+ACK packet (raw)\n");
        printf("  ACK   - send ACK packet (raw)\n");
        printf("  SA    - send SYN+ACK packet (raw)\n");
        printf("  quit  - close connection and return to main menu\n");
        uint32_t seq = rand();
        uint32_t ack = rand();
        char cmd[16];
        int conn_closed = 0;
        while (1) {
            printf("lastack> ");
            fflush(stdout);
            if (fgets(cmd, sizeof(cmd), stdin) == NULL) break;
            cmd[strcspn(cmd, "\n")] = '\0';
            if (strcmp(cmd, "quit") == 0) {
                if (!conn_closed) {
                    close(sockfd);
                }
                printf("Exiting last_ack_test_client.\n");
                break;
            } else if (strcmp(cmd, "close") == 0) {
                if (!conn_closed) {
                    printf("Calling close() - sending FIN normally.\n");
                    close(sockfd);
                    conn_closed = 1;
                    printf("Connection closed by close(). You may still send raw packets (FA/ACK/SA).\n");
                } else {
                    printf("Connection already closed.\n");
                }
            } else if (strcmp(cmd, "FA") == 0) {
                send_raw_tcp(src_ip_str, src_port, dst_ip, dst_port, seq, ack, 0x11, NULL, 0);
                seq++;
            } else if (strcmp(cmd, "ACK") == 0) {
                send_raw_tcp(src_ip_str, src_port, dst_ip, dst_port, seq, ack, 0x10, NULL, 0);
            } else if (strcmp(cmd, "SA") == 0) {
                send_raw_tcp(src_ip_str, src_port, dst_ip, dst_port, seq, ack, 0x12, NULL, 0);
                seq++;
            } else {
                printf("Unknown command. Available: close, FA, ACK, SA, quit\n");
            }
        }
    } else {
        printf("[Client] Cannot get local address, raw sending unavailable.\n");
        close(sockfd);
    }
}
int main(int argc, char *argv[]) {
    if (argc != 5) {
        fprintf(stderr, "Usage: %s <local_ip> <local_port> <remote_ip> <remote_port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    strncpy(local_ip, argv[1], sizeof(local_ip)-1);
    local_ip[sizeof(local_ip)-1] = '\0';
    local_port = atoi(argv[2]);
    strncpy(remote_ip, argv[3], sizeof(remote_ip)-1);
    remote_ip[sizeof(remote_ip)-1] = '\0';
    remote_port = atoi(argv[4]);
    signal(SIGPIPE, SIG_IGN);
    srand(time(NULL));
    printf("=== Multi-mode Network Tool (Merged) ===\n");
    printf("Local:  %s:%d\n", local_ip, local_port);
    printf("Remote: %s:%d\n", remote_ip, remote_port);
    printf("Select mode by number:\n");
    printf("  1 - listen_on_server (listen on local address, support raw sending)\n");
    printf("  2 - connect_to_server (interactive: send out-of-window SYN or invalid-ACK segment or send FA/ACK/SA/SYN/SR)\n");
    printf("  3 - raw packet sender (no connection, send FIN/PSH/SA/SYN/ACK)\n");
    printf("  4 - last_ack_test_server (bind local, recv FIN, close -> LAST_ACK)\n");
    printf("  5 - last_ack_test_client (connect remote, support close/FA/ACK/SA)\n");
    printf("  0 - quit\n");
    int choice;
    while (1) {
        printf("> ");
        fflush(stdout);
        if (scanf("%d", &choice) != 1) {
            int c;
            while ((c = getchar()) != '\n' && c != EOF);
            printf("Invalid input. Please enter a number.\n");
            continue;
        }
        // 清空输入缓冲区
        int c;
        while ((c = getchar()) != '\n' && c != EOF);
        if (choice == 0) {
            printf("Exiting.\n");
            break;
        } else if (choice == 1) {
            printf("Starting listen_on_server...\n");
            listen_on_server(local_ip, local_port);
        } else if (choice == 2) {
            printf("Starting connect_to_server...\n");
            connect_to_server(remote_ip, remote_port);
        } else if (choice == 3) {
            send_raw_mode(local_ip, local_port, remote_ip, remote_port);
        } else if (choice == 4) {
            printf("Starting last_ack_test_server...\n");
            last_ack_test_server(local_ip, local_port);
        } else if (choice == 5) {
            printf("Starting last_ack_test_client...\n");
            last_ack_test_client(remote_ip, remote_port);
        } else {
            printf("Invalid choice. Please enter 1, 2, 3, 4, 5, or 0.\n");
        }
    }
    return 0;
}
相关推荐
呆萌很2 小时前
达梦数据库(DM)和MySQL核心区别
数据库
l1t2 小时前
DeepSeek总结的pgrust以光速编程:构建 Gravity 查询引擎
数据库·人工智能·postgresql
木木_Q4057733493 小时前
Oracle 增量同步配置指南:只搬变了的数据,不重复也不漏数
数据库·oracle·数据同步·数据库同步·增量同步
升鲜宝供应链及收银系统源代码服务3 小时前
Java Spring Boot 商业版 升鲜宝供应链管理系统--商品模块表与字段分析(商品管理页面业务逻辑与数据表关系分析)
数据库·生鲜配送系统排名·生鲜配送系统架构设计·b2b订货商城解决方案·生鲜配送系统源代码出售·生鲜配送系统设计源代码
灵晔君3 小时前
【MySQL】库的操作
数据库·mysql
别讲概念4 小时前
JavaEE(6)TCP_IP
网络·tcp/ip·java-ee
2603_969694854 小时前
【MySQL学习笔记】DQL(基础查询/条件/聚合/分组/排序/分页)与 DCL(用户权限)全解析
数据库·笔记·学习·mysql·学习方法
Paper数据分析-经管学术4 小时前
1985-2025年地级市绿色发明专利被引量
数据库
念何架构之路4 小时前
Go 标准库 database/sql 详解
数据库·sql·golang