在 Rust 中实现 TCP : 3. TCP连接四元组

news2024/11/17 15:35:26

连接四元组

我们的项目已经取得了很大的进展——接下来能够开始解决 TCP 协议的实现问题。下面将讨论 TCP 的一些行为及其各种状态。

在这里插入图片描述

在多任务操作系统中,各种应用程序(例如 Web 服务器、电子邮件客户端等)需要同时进行网络访问。为了区分这些不同的网络活动,每个应用程序将自己绑定到一个唯一的端口号。端口号与主机的 IP地址的组合形成(在非常基本的级别)所谓的“套接字” - 内核管理的抽象对象。为了建立连接,需要使用一对这样的套接字——一个用于发送端机器,一个用于接收端机器。而套接字对是唯一的,可以有效地标识一个连接。

与 TCP 连接端点 相关的非常重要的数据结构是 传输控制块 (TCB)。 TCB 充当与已建立或挂起的连接 相关的所有参数和变量的 存储库。 TCB 包含从套接字地址和端口号到序列号和窗口大小的所有内容,是 TCP 操作逻辑的基础。

每个 TCB 都是 某个特定连接 的 TCP 状态的封装。它存储“传输控制协议”中“可靠性”所必需的详细信息,例如未确认的数据、流量控制参数和下一个预期序列号。本质上,TCB充当TCP操作的控制中心,实时维护和更新连接状态。 TCB 充当 TCP 连接的内核账簿,存储从端口号和 IP 地址到流量控制参数和待处理数据的所有内容。本质上,TCB 是 TCP 协议栈需要维护的所有指标和变量的存储库,以便可靠有效地处理连接。

现在,考虑一台涉及多个 TCP 连接的机器。它如何区分它们并辨别哪个传入数据包属于哪个连接?这就是“TCP连接四元组”概念 ,它至关重要。连接四元组由具有四个元素组成:源 IP 地址、源端口、目标 IP 地址和目标端口。该元组充当每个 活动TCP连接 的唯一标识符。连接四元组 作为 TCB 哈希表 的索引很实用。当数据包到达时,TCP 栈使用连接四元组来查找相应的 TCB,进而查找相关的状态机。这确保了根据封装在该 TCB 中的状态机所定义的正确的规则和变量集来处理数据包。 TCP 创建并维护传输控制块 (TCB) 的哈希表来存储每个 TCP 连接的数据。每个活动连接的控制块都会添加到hash表中。连接关闭后不久,控制块就会被删除。

连接在其生命周期内会经历一系列状态。这些状态包括:LISTEN、SYN-SENT、SYN-RECEIVED、ESTABLISHED、FIN-WAIT-1、FIN-WAIT-2、CLOSE-WAIT、CLOSING、LAST-ACK、TIME-WAIT 和虚构状态 CLOSED。这些状态保存在传输控制块 (TCB) 中,充当指导每个连接点的连接行为的关键标记。从最初的握手到数据传输和最终的断开,连接的状态都会被仔细跟踪,以保证可靠和有序的数据交换。

初始状态 - CLOSEDLISTEN - 作为连接的起点。当处于 CLOSED 状态时,不存在传输控制块(TCB),本质上是连接不存在。它类似于未初始化的变量;在此状态下收到的任何数据包都将被忽略。将此与 LISTEN 状态进行对比,在该状态下系统正在主动等待传入的连接请求。一旦收到 SYN 数据包,状态就会转换为 SYN-RECEIVED ,启动 TCP 握手。连接在握手后达到 稳定的 ESTABLISHED 状态,此时发生大部分数据交换。

连接结束时,它会级联一系列 FIN-WAITCLOSE-WAIT 状态,最终达到 TIME-WAITLAST-ACK ,具体取决于拆除动作。这些终端状态确保网络中任何延迟的数据包都得到考虑,从而提供优雅的连接拆除。系统最终恢复到 CLOSED 状态,释放TCB和相关资源以用于将来的连接。

                    +---------+ ---------\      active OPEN
                              |  CLOSED |            \    -----------
                              +---------+<---------\   \   create TCB
                                |     ^              \   \  snd SYN
                   passive OPEN |     |   CLOSE        \   \
                   ------------ |     | ----------       \   \
                    create TCB  |     | delete TCB         \   \
                                V     |                      \   \
                              +---------+            CLOSE    |    \
                              |  LISTEN |          ---------- |     |
                              +---------+          delete TCB |     |
                   rcv SYN      |     |     SEND              |     |
                  -----------   |     |    -------            |     V
 +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
 |         |<-----------------           ------------------>|         |
 |   SYN   |                    rcv SYN                     |   SYN   |
 |   RCVD  |<-----------------------------------------------|   SENT  |
 |         |                    snd ACK                     |         |
 |         |------------------           -------------------|         |
 +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
   |           --------------   |     |   -----------
   |                  x         |     |     snd ACK
   |                            V     V
   |  CLOSE                   +---------+
   | -------                  |  ESTAB  |
   | snd FIN                  +---------+
   |                   CLOSE    |     |    rcv FIN
   V                  -------   |     |    -------
 +---------+          snd FIN  /       \   snd ACK          +---------+
 |  FIN    |<-----------------           ------------------>|  CLOSE  |
 | WAIT-1  |------------------                              |   WAIT  |
 +---------+          rcv FIN  \                            +---------+
   | rcv ACK of FIN   -------   |                            CLOSE  |
   | --------------   snd ACK   |                           ------- |
   V        x                   V                           snd FIN V
 +---------+                  +---------+                   +---------+
 |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
 +---------+                  +---------+                   +---------+
   |                rcv ACK of FIN |                 rcv ACK of FIN |
   |  rcv FIN       -------------- |    Timeout=2MSL -------------- |
   |  -------              x       V    ------------        x       V
    \ snd ACK                 +---------+delete TCB         +---------+
     ------------------------>|TIME WAIT|------------------>| CLOSED  |
                              +---------+                   +---------+

TCP 连接状态图 图 6。如 RFC 793 中所示。

说得够多了,让我们写一些代码吧!首先,将使用枚举对 状态转换图 进行编码。为了使事情更加模块化,我们可以创建一个名为 TCP 的新模块,并在 TCP.rs 中放置一些代码。与 TCP 状态机相关的 Enum 和其他逻辑将保留在此处。

// tcp.rs
// Defining possible TCP states. 
// Each state represents a specific stage in the TCP connection.
pub enum State { 
/// The connection is closed and no active connection exists. 
Closed, 
/// The endpoint is waiting for a connection attempt from a remote endpoint. 
Listen, 
/// The endpoint has received a SYN (synchronize) segment and has sent a SYN-ACK /// (synchronize-acknowledgment) segment in response. It is awaiting an ACK (acknowledgment) /// segment from the remote endpoint. 
SynRcvd, 
/// The connection is established, and both endpoints can send and receive data. Estab, 
}


// Implementing the Default trait for State. // Sets the default TCP state to 'Listen'. 
impl Default for State { 
	fn default() -> Self { 
		State::Listen 
	} 
}

// Implementing methods for State. 
impl State {
// Method to handle incoming TCP packets. 
// 'iph' contains the parsed IPv4 header, 'tcph' contains the parsed TCP header, and 'data' contains the TCP payload. 
pub fn on_packet<'a>( 
	&mut self, 
	iph: etherparse::Ipv4HeaderSlice<'a>, 
	tcph: etherparse::TcpHeaderSlice<'a>, 
	data: &'a [u8], 
	) { 
	// Log the source and destination IP addresses and ports, as well as the payload length. 
		eprintln!( 
			"{}:{} -> {}:{} {}b of TCP", 
			iph.source_addr(), 
			tcph.source_port(), 
			iph.destination_addr(), 
			tcph.destination_port(), 
			data.len() 
		); 
	} 
}
//main.rs
// Importing necessary modules and packages.
use std::io;
use std::collections::HashMap;
use std::net::Ipv4Addr;

// Defining the Quad struct that holds information about both the source and destination IP address and port.
// This uniquely identifies a TCP connection and will be used as a key in the HashMap.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
struct Quad {
    src: (Ipv4Addr, u16),
    dst: (Ipv4Addr, u16),
}

fn main() -> io::Result<()> {
    // Initialize a HashMap to store the TCP connection state against the Quad.
    // Quad is the key and the TCP state (from tcp.rs) is the value.
    let mut connections: HashMap<Quad, tcp::State> = Default::default();
    
    // Initialize the network interface.
    let nic = tun_tap::Iface::new("tun0", tun_tap::Mode::Tun)?;
    
    // Buffer to store the incoming packets.
    let mut buf = [0u8; 1504];

    loop {
    
			-----SKIP-----

        // Attempt to parse the IPv4 header.
        match etherparse::Ipv4HeaderSlice::from_slice(&buf[4..nbytes]) {
            Ok(iph) => {
					-----SKIP-----

                // Attempt to parse the TCP header.
                match etherparse::TcpHeaderSlice::from_slice(&buf[4 + iph.slice().len()..nbytes]) {
                    Ok(tcph) => {
                        // Calculate the start index of the actual data in the packet.
                        let datai = 4 + iph.slice().len() + tcph.slice().len();
                        
                        // Look for or create a new entry in the HashMap for this connection.
  
			// Check if the connection already exists in the HashMap, otherwise create a new entry 
			match connections.entry(Quad { 
			src: (src, tcph.source_port()), 
			dst: (dst, tcph.destination_port()), 
			}) { 
			Entry::Occupied(mut c) => { 
			c.get_mut().on_packet(&mut nic, iph, tcph, &buf[datai..nbytes])?; 
			} 
			Entry::Vacant(e) => { 
			if let Some(c) = tcp::Connection::on_accept(&mut nic, iph, tcph, &buf[datai..nbytes])? { 
			e.insert(c); 
			} } } }
                    }
                    Err(e) => {
                        // Handle TCP header parsing errors.
                        eprintln!("An error occurred while parsing the TCP packet: {:?}", e);
                    }
                }
            }
            Err(e) => {
                // Handle IPv4 header parsing errors.
                eprintln!("An error occurred while parsing the IP packet: {:?}", e);
            }
        }
    }
}

在上面代码中,通过定义 TCP 连接所需的关键数据结构和函数来建立 TCP 协议栈的基础。该架构的核心是 Quad 结构,它充当名为 connectionsHashMap 中每个条目的 唯一标识符。此 HashMap 充当 活动或正在建立的 TCP 连接的内存中注册表。 HashMap 中的每个条目都包含映射到其当前 TCP 连接的 Quad 实例。该状态由 tcp.rs 中定义的枚举类型表示,包含四种 TCP 连接状态之一: ClosedListenSynRcvd ,或 Estab

我们引入两个主要的处理数据包方法: on_accepton_packeton_accept 方法负责处理发起 新连接的传入数据包。相反, on_packet 方法管理 现有连接的数据包。这两种方法都会记录基本信息,例如源和目标 IP 地址和端口以及有效负载长度。最后,在 main.rs 中,我们根据传入数据包利用模式匹配来区分新连接和现有连接。

我们正在稳步取得进展。到目前为止,已经确保接收到正确的 IPv4 数据包,并且已经实现了一种机制,将传入数据包与其各自的状态相关联,并由唯一的连接四元组作为键。我们的下一个目标是专注于实现 TCP 握手,这是保证客户端和服务器之间建立可靠连接的关键步骤。客户端通过发送 SYN(同步)数据包来启动此过程,而服务器则监听这些传入请求。此握手是一个 三阶段过程,涉及 SYN 数据包、随后的 SYN-ACK(同步确认)数据包和最后的 ACK(确认)数据包。在此阶段,我们将增强 accept 方法来管理这些不同类型的握手数据包。

// main.rs
// Required imports
use std::io;
mod tcp;  // Importing the tcp module (defined below)
use std::collections::HashMap;
use std::net::Ipv4Addr;

// Define the Quad struct to represent the 4-tuple of source IP, source port, destination IP, and destination port.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
struct Quad {
    src: (Ipv4Addr, u16),  // Source IP and port
    dst: (Ipv4Addr, u16),  // Destination IP and port
}

fn main() -> io::Result<()> {
    // Create a hashmap to manage TCP connections
    let mut connections: HashMap<Quad, tcp::Connection> = Default::default();

    // Create a new virtual interface in TUN mode.
    // TUN mode allows to work with IP packets directly, while TAP mode works at Ethernet frame level.
    let mut nic = tun_tap::Iface::without_packet_info("tun0", tun_tap::Mode::Tun)?;
    
    // Buffer to hold the packet data
    let mut buf = [0u8; 1504];
    
    // Main processing loop to handle incoming packets
    loop {
        // Read the packet into the buffer
        let nbytes = nic.recv(&mut buf[..])?;

        // Parse the IP header from the packet. If successful, it gives a slice of the IP header.
        match etherparse::Ipv4HeaderSlice::from_slice(&buf[..nbytes]) {
            Ok(iph) => {
                // Parsing of the IP header was successful. Further processing happens here.
                // -----SKIP----- (omitting some IP processing steps as indicated)

                // Attempt to parse the TCP header from the packet.
                match etherparse::TcpHeaderSlice::from_slice(&buf[iph.slice().len()..nbytes]) {
                    Ok(tcph) => {
                        // Parsing of the TCP header was successful. Continue processing.
                        // Determine the index where the actual data starts in the packet (after IP and TCP headers).
                        let datai = iph.slice().len() + tcph.slice().len();
                        
                        // Lookup the connection using the Quad (4-tuple) as the key.
                        match connections.entry(Quad {
                            src: (src, tcph.source_port()),
                            dst: (dst, tcph.destination_port()),
                        }) {
                            // If a connection already exists for this Quad:
                            Entry::Occupied(mut c) => {
                                c.get_mut()
                                    .on_packet(&mut nic, iph, tcph, &buf[datai..nbytes])?;
                            }
                            // If there's no connection yet for this Quad:
                            Entry::Vacant(mut e) => {
                                // Attempt to establish a new connection.
                                if let Some(c) = tcp::Connection::accept(
                                    &mut nic,
                                    iph,
                                    tcph,
                                    &buf[datai..nbytes],
                                )? {
                                    e.insert(c);
                                }
                            }
                        }
                    }
                    Err(e) => {
                        // Handle TCP parsing errors.
                        eprintln!("An error occurred while parsing TCP packet {:?}", e);
                    }
                }
            }
            Err(e) => {
                // Handle IP parsing errors.
                eprintln!("An error occurred while parsing IP packet {:?}", e);
            }
        }
    }
}

//tcp.rs
use std::io;
use std::io::prelude::*;

/// The possible states a TCP connection can be in, based on the TCP state machine.
/// It's a subset of the states available in the full TCP state diagram.
pub enum State {
    Closed,
    Listen,
    SynRcvd,
    Estab,
}

pub struct Connection {
    /// The current state of the TCP connection.
    state: State,
    /// The sequence space for sent data. It keeps track of various sequence numbers for data we've sent.
    send: SendSequenceSpace,
    /// The sequence space for received data. It keeps track of sequence numbers for data we're receiving.
    recv: RecvSequenceSpace,
    ip: etherparse::Ipv4Header
    tcp: etherparse::TcpHeader
}

/// Representation of the Send Sequence Space as described in RFC 793 Section 3.2 Figure 4.
/// It provides a visual representation of various sequence numbers associated with data that's being sent.
///
/// ```
///            1         2          3          4
///       ----------|----------|----------|----------
///              SND.UNA    SND.NXT    SND.UNA
///                                   +SND.WND
///
/// 1 - Old sequence numbers which have been acknowledged by the receiver.
/// 2 - Sequence numbers of unacknowledged data that has been sent.
/// 3 - Sequence numbers allowed for transmitting new data.
/// 4 - Future sequence numbers that are not allowed for transmission yet.
/// ```
struct SendSequenceSpace {
    /// SND.UNA: Oldest sequence number not yet acknowledged by the receiver.
    una: u32,
    /// SND.NXT: Next sequence number to be used for new data for transmission.
    nxt: u32,
    /// SND.WND: The window size or the number of bytes that are allowed to be outstanding (unacknowledged).
    wnd: u16,
    /// Indicates if the URG control bit is set. If true, then the sequence number in the urgent pointer field is in effect.
    up: bool,
    /// Sequence number of the segment used for the last window update. 
    wl1: usize,
    /// Acknowledgment number used for the last window update.
    wl2: usize,
    /// Initial send sequence number. It's the first sequence number used when the connection was established.
    iss: u32,
}

/// Representation of the Receive Sequence Space as described in RFC 793 Section 3.2 Figure 5.
/// It provides a visual representation of sequence numbers associated with data that's being received.
///
/// ```
///                1          2          3
///            ----------|----------|----------
///                   RCV.NXT    RCV.NXT
///                             +RCV.WND
///
/// 1 - Old sequence numbers which have been acknowledged.
/// 2 - Sequence numbers allowed for receiving new data.
/// 3 - Future sequence numbers which are not allowed for reception yet.
/// ```
struct RecvSequenceSpace {
    /// RCV.NXT: Next expected sequence number that the receiver is expecting.
    nxt: u32,
    /// RCV.WND: The number of bytes that the receiver is willing to accept.
    wnd: u16,
    /// Indicates if the URG control bit is set on received data.
    up: bool,
    /// Initial receive sequence number. The sequence number of the first byte received.
    irs: u32,
}

/// Default state for a TCP connection is set to `Listen`.
impl Default for State {
    fn default() -> Self {
        State::Listen
    }
}

impl Connection {
    /// Handles an incoming TCP packet for establishing a connection.
    /// If the incoming packet is a SYN packet, it prepares and sends a SYN-ACK packet in response.
    /// Otherwise, it ignores the packet.
    ///
    /// Parameters:
    /// - `nic`: The network interface to use for sending the SYN-ACK packet.
    /// - `iph`: The IPv4 header of the incoming packet.
    /// - `tcph`: The TCP header of the incoming packet.
    /// - `data`: The payload of the incoming packet.
    ///
    /// Returns:
    /// A new `Connection` in the `SynRcvd` state if the incoming packet was a SYN packet.
    /// Otherwise, returns `None`.
    pub fn accept<'a>(
        nic: &mut tun_tap::Iface,
        iph: etherparse::Ipv4HeaderSlice<'a>,
        tcph: etherparse::TcpHeaderSlice<'a>,
        data: &'a [u8],
    ) -> io::Result<Option<Self>> {
        let mut buf = [0u8; 1500];
        if !tcph.syn() {
            // Ignore packets that aren't SYN packets.
            return Ok(None);
        }
        let iss = 0;
        let wnd = 10;
        let mut c = Connection {
            state: State::SynRcvd,
            send: SendSequenceSpace {
                iss,
                una: iss,
                nxt: 1,
                wnd: wnd,
                up: false,
                wl1: 0,
                wl2: 0,
            },
            recv: RecvSequenceSpace {
                // Initialize the receive sequence number to the incoming sequence number.
                irs: tcph.sequence_number(),
                // Expect the next byte after the incoming sequence number.
                nxt: tcph.sequence_number() + 1,
                // Use the incoming packet's window size for our receive window.
                wnd: tcph.window_size(),
                up: false,
            },
        // TODO: Consider keeping track of sender info for future use.
        // Prepare a SYN-ACK packet in response to the SYN packet.
        tcp: etherparse::TcpHeader::new(
            tcph.destination_port(),
            tcph.source_port(),
            iss,
            wnd,
		),
            ip: etherparse::Ipv4Header::new(
            syn_ack.header_len(),
            64,
            etherparse::IpNumber::Tcp as u8,
            [
                iph.destination()[0],
                iph.destination()[1],
                iph.destination()[2],
                iph.destination()[3],
            ],
            [
                iph.source()[0],
                iph.source()[1],
                iph.source()[2],
                iph.source()[3],
            ],
        )
        };

        c.tcp.acknowledgment_number = c.recv.nxt;
        c.tcp.syn = true;
        c.tcp.ack = true;
        
        c.ip.set_payload_len(c.tcp.header_len() as usize + 0);

        // Calculate and set the checksum for the SYN-ACK packet.
        c.tcp.checksum = c.tcp
            .calc_checksum_ipv4(&c.ip, &[])
            .expect("Failed to compute checksum");
        
        // Write out the TCP and IP headers to a buffer to be sent.
        let unwritten = {
            let mut unwritten = &mut buf[..];
            ip.write(&mut unwritten);
            syn_ack.write(&mut unwritten);
            unwritten.len()
        };

        // Send the SYN-ACK packet.
        nic.send(&buf[..unwritten])?;
        Ok(Some(c))
    }

    /// TODO: Implement a function to handle incoming packets for an established connection.


    // Function to handle incoming packets once a connection is established.
    pub fn on_packet<'a>(
        &mut self, 
        nic: &mut tun_tap::Iface,                  // The network interface
        iph: etherparse::Ipv4HeaderSlice<'a>,       // The parsed IP header
        tcph: etherparse::TcpHeaderSlice<'a>,       // The parsed TCP header
        data: &'a [u8],                            // The actual data from the packet
    ) -> io::Result<()> {
        // Process the packet based on its flags and the current state of the connection.
        // The code is omitted, but would involve handling the different possible states and flags 
        // (e.g., ACK, FIN, RST) as per the TCP state machine.
        // ----SKIP----
        Ok(())
    }

    // Function to send a SYN-ACK packet.

}

在从包含实现框架的代码过渡到建立 TCP 握手的充实版本的过程中,经历了重大的架构演变。该架构的核心是 State 枚举和 tcp.rs 中的 Connection 结构体的扩展。之前, State 枚举是四种可能的 TCP 状态的简单表示。在新的实现中,重点转移到建立更强大的连接状态表示。 Connection 结构已扩展为现在将 SendSequenceSpaceRecvSequenceSpace 的实例作为其属性。这些结构对于 TCP 序列号和确认号的登记 很有帮助,这对于数据的可靠传输至关重要。让我们深入了解一下具体情况。

The SendSequenceSpace struct encapsulates several variables crucial for the sending side of our TCP connection. Key among them are:
SendSequenceSpace 结构体封装了几个对于 TCP 连接发送端至关重要的变量。其中关键是:

  • una (send unacknowledged): The earliest sequence number in the send buffer that has yet to receive an acknowledgment.
    una (发送未确认):发送缓冲区中尚未收到确认的最早序列号。

  • nxt (send next): The next sequence number to be used for new data.
    nxt (发送下一个):用于新数据的下一个序列号。

  • wnd (send window): Specifies the range of acceptable sequence numbers for the receiver. These fields are critical in managing flow control, ensuring reliable data transfer, and implementing features like sliding windows.
    wnd (发送窗口):指定接收方可接受的序列号范围。这些字段对于管理流量控制、确保可靠的数据传输以及实现滑动窗口等功能至关重要。
    Complementing this, the RecvSequenceSpace struct handles the receiving end. It possesses fields like:
    作为补充, RecvSequenceSpace 结构处理接收端。它拥有以下字段:

  • nxt (receive next): The sequence number expected for the next incoming packet.
    nxt (接收下一个):下一个传入数据包的预期序列号。

  • wnd (receive window): The window size advertising the range of acceptable sequence numbers for the sender.
    wnd (接收窗口):通告发送方可接受的序列号范围的窗口大小。

值得注意的是, SendSequenceSpace 和 RecvSequenceSpace 都包含初始序列号(分别为 iss 和 irs )。这些在连接建立阶段非常关键,特别是在 TCP 三向握手过程中。

我们开始实现 accept 方法,该方法现已增强以处理 SYN 数据包,从而通过将 SYN-ACK 数据包分派回客户端来启动三向握手。为了实现这一目标,使用 etherparse 库函数来构建和发送 TCP/IP 头,使我们的实现与协议规范紧密结合。

顺便说一句 - 值得注意的是,在当前的实现中, connections HashMap 容易受到 SYN 洪水攻击。在此类攻击中,攻击者可以发送大量 TCP SYN(同步)数据包,每个数据包具有不同的源地址和端口,但都针对相同的目的地。由于 connections HashMap 自动为每个唯一的 Connection 创建一个新条目,因此攻击者可以通过使用大量虚假条目填充 HashMap 来轻松耗尽系统内存。这可能会导致资源耗尽,并最终导致拒绝服务 (DoS)。

在生产级 TCP 实施中,通常会采取其他措施来减轻此类风险。这可以包括使用 syn cookie - 一种无状态方法,其中服务器在握手完成之前不会为 SYN 请求分配资源。然而,对于这个项目,我们不会采取任何措施来防止 syn 洪水攻击。

此时,您可能会好奇并看到我们编写的这么长的代码的实际效果。我们应该继续测试应用程序。首先,通过运行 run.sh 脚本来启动程序,该脚本将构建并执行二进制文件并为其提供必要的提升网络访问权限,接下来,将使用 Netcat 尝试与应用程序建立 TCP 连接,最后为了可视化,将使用 tshark 通过运行 tshark -I tun0 来监视和捕获 tun0 接口上的数据包。

1   0.000000   fe80::a2b3:c4d5:e6f7 -> ff02::2  ICMPv6 110 Router Solicitation from a2:b3:c4:d5:e6:f7
2   0.002123   fe80::1:1 -> fe80::a2b3:c4d5:e6f7  ICMPv6 150 Router Advertisement from 00:11:22:33:44:55 (MTU: 1500)
3   0.004567   fe80::a2b3:c4d5:e6f7 -> fe80::1:1   TCP 86 51234->80 [SYN] Seq=0 Win=42800 Len=0 MSS=1440 WS=16 SACK_PERM=1
4   0.006789   fe80::1:1 -> fe80::a2b3:c4d5:e6f7   TCP 86 80->51234 [SYN, ACK] Seq=0 Ack=1 Win=64000 Len=0 MSS=1440 SACK_PERM=1 WS=16
5   0.006999   fe80::a2b3:c4d5:e6f7 -> fe80::1:1   TCP 66 51234->80 [ACK] Seq=1 Ack=1 Win=43000 Len=0

如果一切顺利,应该会看到与于上面看到的类似的内容。这表明主机首先请求路由器信息,并从路由器获得答复。之后,将看到建立 TCP 连接的步骤。

下一步计划是什么?如果您留意的话,您会注意到我们已经解决了 TCP 三向握手的最初两个步骤。当来自客户端的 SYN 到达我们这里时,服务器会立即用 SYN-ACK 进行回复。发送 SYN-ACK 后,服务器优雅地进入 SynRcvd 状态,准备好等待客户端的 ACK 来 完成握手协议。当捕获并处理这个 ACK​​ 时,理想情况下会将连接转换到 Established 状态,标志着一个成熟的 TCP 连接的诞生。然而,这里缺少一块拼图:我们的代码仍在等待中,尚未处理客户端的 ACK。这是我们下一个目标。

参考

  • 对应代码

原文地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1492607.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用GitOps自动化推动AI/ML工作流程

作为一名深耕自动化和人工智能领域的开发人员&#xff0c;我们逐渐认识到尖端工具和方法之间的显着协同作用&#xff0c;这些协同作用突破了可能性的界限。在这次探索中&#xff0c;我们想分享一个概念&#xff0c;它不仅彻底改变了我们的软件开发和基础设施管理方法&#xff0…

华为智慧教室3.0的晨光,点亮教育智能化变革

“教室外有更大的世界&#xff0c;但世界上没有比教室更伟大的地方。” 我们在求学阶段&#xff0c;都听说过这句话&#xff0c;但往往是在走出校园之后&#xff0c;才真正理解了这句话。为了让走出校园的孩子能够有能力&#xff0c;有勇气探索广阔的世界。我们应该准备最好的教…

JProfiler详解 JVM性能监测内存泄露分析工具

JProfiler详解 JProfiler简介主要功能特点使用场景注意事项使用案例使用步骤Could not verify ssh-ed25519 host key with fingerprint 问题解决内存泄露分析 JProfiler简介 JProfiler是一款业界领先的Java性能分析工具&#xff0c;由ej-technologies公司开发&#xff0c;专门…

Elasticsearch:使用 Streamlit、语义搜索和命名实体提取开发 Elastic Search 应用程序

作者&#xff1a;Camille Corti-Georgiou 介绍 一切都是一个搜索问题。 我在 Elastic 工作的第一周就听到有人说过这句话&#xff0c;从那时起&#xff0c;这句话就永久地印在了我的脑海中。 这篇博客的目的并不是我出色的同事对我所做的相关陈述进行分析&#xff0c;但我首先…

dolphinescheduler调用API

&#xff08;作者&#xff1a;陈玓玏&#xff09; 1. 打开api文档 api文档地址&#xff1a;http://{api server ip}:12345/dolphinscheduler/swagger-ui/index.html?languagezh_CN&langcn&#xff0c;我是用k8s部署的&#xff0c;所以ip和端口是由service决定的&#xf…

前端学习之HTML(第二天)--多媒体标签和表格标签

注&#xff1a;里面的注释是对各个标签的解释 多媒体标签 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title></title> </head> <body> <!-- audio是音频可以填写绝对路径也可填写相对路径 --> &l…

【Hadoop大数据技术】——Hadoop概述与搭建环境(学习笔记)

&#x1f4d6; 前言&#xff1a;随着大数据时代的到来&#xff0c;大数据已经在金融、交通、物流等各个行业领域得到广泛应用。而Hadoop就是一个用于处理海量数据的框架&#xff0c;它既可以为海量数据提供可靠的存储&#xff1b;也可以为海量数据提供高效的处理。 目录 &#…

CSS定位,web游戏开发

面试前的准备 在这部分&#xff0c;我将详细讲解面试前我们需要做哪些方面的工作&#xff0c;以保证我们在面试过程中更加顺利。 准备一份漂亮的简历 一份漂亮的简历就是你进入大厂的敲门砖。 网上有很多教程教大家如何写出一份漂亮的简历&#xff0c;这里我就不做重复劳动了…

【SpringBoot3.x教程02】SpringBoot配置文件详解

前言&#xff1a;什么是配置文件 SpringBoot的配置文件是指用于定义和管理SpringBoot应用程序配置的文件。这些配置文件允许开发者调整和控制应用程序的行为&#xff0c;而无需改变代码。主要有两种格式的配置文件&#xff1a; 1、application.properties&#xff1a;这是一种使…

JavaScript 闭包:让你更深入了解函数和作用域

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

修改表中某个字段等于另一个字段减去 2 小时的 SQL

需求&#xff1a;将表中到达时间按照客户要求改为比赛时间的提前 N 小时&#xff0c;具体如下&#xff1a; 表结构 update contestSchedule SET mainRefereeArrivalTimeDATE_FORMAT(CONCAT(2024-03-04 ,gameTime)- INTERVAL 2 HOUR, %H:%i), assistantRefereeArrivalTimeDAT…

streamlit初学-用streamlit实现云台控制界面

用streamlit实现云台控制界面 效果图PC上的效果手机上的效果 源码: 本文演示了,如何用streamlit做一个云台控制界面。功能包括:用户登录,事件的处理,图片的更新 版本信息: streamlit_authenticator: 下载链接streamlit : 1.31.1python: 3.11 修改点: streamlit_authenticato…

CSS实现选中卡片样式操作

图一默认自动选中&#xff0c;并且不可取消选中&#xff0c;当选择其他卡片才可点击下一步 在 “ src/assets ” 路径下存放 save.png&#xff0c;代表选中的状态 <div class"cards"><ul class"container"><li v-for"image in image…

什么是工业级物联网智能网关?如何远程控制PLC?

在这个信息爆炸的时代&#xff0c;物联网技术已经逐渐渗透到我们生活的方方面面&#xff0c;而工业级物联网智能网关作为连接工业设备和云端的重要桥梁&#xff0c;更是引领着工业4.0时代的浪潮。那么&#xff0c;究竟什么是工业级物联网智能网关呢&#xff1f;今天&#xff0c…

【学习笔记】开源计算机视觉库OPENCV学习方案

本文中&#xff0c;我们试图提供一些学习OpenCV的详细和实用资源&#xff0c;这些资源包括基础知识、进阶技巧以及实践项目&#xff0c;旨在帮助初学者和进阶学习者更好地掌握和使用OpenCV库。 一、学习资源 官方文档&#xff1a;OpenCV的官方文档是学习OpenCV的最佳起点。它包…

代码随想录算法训练营第九天

28. 实现 strStr() &#xff08;本题可以跳过&#xff09; 方法&#xff1a; 方法一&#xff1a; 暴力法 i 表示最多能移动到n-m位置&#xff0c; 超过则退出循环。j表示haystack 初始位置k表示needle的初始位置如果haystack [j] needle[k]且 k<m 则 j, k; 如果 km 则返…

P5076 【深基16.例7】普通二叉树(简化版)题解

题目 您需要写一种数据结构&#xff0c;来维护一些数&#xff08;都是绝对值以内的数&#xff09;的集合&#xff0c;最开始时集合是空的。其中需要提供以下操作&#xff0c;操作次数q不超过&#xff1a; 定义数x的排名为集合中小于x的数的个数1。查询数x的排名。注意x不一定…

【操作系统概念】 第3章:进程

文章目录 0.前言3.1进程概念3.1.1 进程3.1.2 进程状态3.1.3 进程控制块&#xff08;PCB&#xff09; 3.2、进程调度3.2.1 调度队列3.2.2 调度程序3.2.3 上下文切换 3.3 进程操作3.3.1 进程创建3.3.2 进程终止 3.4 进程间通信 0.前言 早期的计算机一次只能执行一个程序。这种程序…

c++复习

基础 内存分区 栈&#xff1a; 存放函数的局部变量、函数参数、返回地址等&#xff0c;由编译器自动分配和释放。 堆&#xff1a; 动态申请的内存空间&#xff0c;就是由 malloc 分配的内存块&#xff0c;由程序员控制它的分配和释放&#xff0c;如果程序执行结束还没有释放…

09. C语言内嵌汇编代码

C语言函数内可以自定义一段汇编代码&#xff0c;在GCC编译器中使用 asm 或 __asm__ 关键词定义一段汇编代码&#xff0c;并可选添加volatile关键字&#xff0c;表示不要让编译器优化这段汇编代码。 内嵌汇编代码格式如下&#xff1a; __asm__ ("汇编代码":输出描述…