#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>
#include <errno.h>

#define MAX_EV 1024
#define BACKLOG 1024
#define ADDR "127.0.0.1"
#define PORT 8080
#define MAGIC_WINDOW_SIZE 4
#define CONTENT "HTTP/1.1 301 Moved Permanently\r\nLocation: http://www.baidu.com\r\n\r\n"

int set_reuseaddr(const int fd)
{
    const int opt = 1;
    return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
}

int set_nodelay(const int fd)
{
    const int opt = 1;
    return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
};

int set_recv_window(const int fd, const int size)
{
    const int opt = size;
    return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
}

int set_nonblocking(const int fd)
{
    const int old_opt = fcntl(fd, F_GETFL);
    const int new_opt = old_opt | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_opt);
    return old_opt;
}

int epoll_add(const int epfd, const int fd, const uint32_t interests)
{
    struct epoll_event ev;
    ev.events = interests;
    ev.data.fd = fd;
    return epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
}

int bind_then_listen()
{
    const int fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in sockaddr;
    memset(&sockaddr, 0, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(PORT);
    inet_pton(AF_INET, ADDR, &sockaddr.sin_addr);
    set_reuseaddr(fd);
    set_nonblocking(fd);
    bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
    listen(fd, BACKLOG);
    return fd;
}

int handle_accept(const int epoll_fd, const int listen_fd)
{
    while(1) {
        const int conn_fd = accept(listen_fd, NULL, 0);
        if (conn_fd == -1) return 0;
        set_nonblocking(conn_fd);
        set_recv_window(conn_fd, MAGIC_WINDOW_SIZE);
        set_nodelay(conn_fd);
        epoll_add(epoll_fd, conn_fd, EPOLLOUT|EPOLLET);
    }
}

int handle_connection(const int epoll_fd, const int conn_fd)
{
    const char *buf = CONTENT;
    send(conn_fd, buf, strlen(buf), 0);
    close(conn_fd);
    return 0;
}

int main(int argc, char **argv)
{
    const int epoll_fd = epoll_create(1);
    struct epoll_event events[MAX_EV] = {0};

    const int listen_fd = bind_then_listen();
    epoll_add(epoll_fd, listen_fd, EPOLLIN|EPOLLET);

    while(1) {
        const int nready = epoll_wait(epoll_fd, events, MAX_EV, -1);
        if (nready <= 0 ) continue;

        for (int i = 0; i < nready; ++i) {
            const uint32_t event = events[i].events;
            const int fd = events[i].data.fd;
            if (fd == listen_fd) {
                handle_accept(epoll_fd, fd);
            } else {
                handle_connection(epoll_fd, fd);
            }
        }
    }

    return 0;
}

 

只用SO_RCVBUF是无法在Linux下将TCP窗口大小设置为4的。Linux下SO_RCVBUF的最小大小为256,设置为4的话Linux内核仍旧会当作256处理。

需要结合Geneva或libnetfilter_queue直接对原始数据包进行修改,才能在Linux下将TCP窗口大小设置为4。在抢答模式中,TCP的窗口大小设置为0更合适一些,在Geneva中将TCP的窗口大小设置为0的规则。



[TCP:flags:SA]-tamper{TCP:window:replace:0}-|
[TCP:flags:A]-tamper{TCP:window:replace:0}-|
 
最后修改:2021 年 10 月 28 日 04 : 39 PM
如果觉得我的文章对你有用,请随意赞赏