pythonsocketstcptcpdump

tcpkeep alive msg not seen in tcpdump


I am running below client server program which are communicating via local loopack address and try to capture the tcpdump output. Though program has specifically set for sending keep alive msg periodically, but I do not see it in tcpdump. Any idea why?

server.py

import socket

def start_server():
    server_host = '127.0.0.1'
    server_port = 12345
    backlog = 5  # Maximum number of queued connections

    # Create a TCP socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Enable TCP keep-alive on the server socket
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

    # Set TCP keep-alive options (optional, you can adjust these as needed)
    server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)
    server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10)
    server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)

    # Bind the socket to the server address and port
    server_socket.bind((server_host, server_port))

    # Listen for incoming connections
    server_socket.listen(backlog)
    print(f"Server is listening on {server_host}:{server_port}")

    while True:
        print("Waiting for a connection...")
        client_socket, client_address = server_socket.accept()
        print(f"Accepted connection from {client_address}")

        while True:
            data = client_socket.recv(1024)
            if not data:
                break

            print(f"Received: {data.decode('utf-8')}")
            client_socket.send(data)  # Echo the data back to the client

        client_socket.close()
        print(f"Connection with {client_address} closed")

if __name__ == "__main__":
    start_server()

client.py

import socket
import time

def start_client():
    server_host = '127.0.0.1'
    server_port = 12345

    # Create a TCP socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Enable TCP keep-alive on the client socket
    client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

    # Set TCP keep-alive options (optional, you can adjust these as needed)
    client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)
    client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10)
    client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)

    try:
        # Connect to the server
        client_socket.connect((server_host, server_port))
        print(f"Connected to {server_host}:{server_port}")

        while True:
            message = input("Enter a message to send (or 'exit' to quit): ")
            if message == 'exit':
                break

            client_socket.send(message.encode('utf-8'))
            response = client_socket.recv(1024)
            print(f"Server response: {response.decode('utf-8')}")

    finally:
        client_socket.close()
        print("Connection closed")

if __name__ == "__main__":
    start_client()

I kept socket channel open for 5 mins without any activity also but did not see any keep alive pkt however there are some pkt transfer still happening periodically but don't seem a keep alive

/home/ravi> tcpdump -i lo -n -vvv 'port 12345'
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
....
....
03:41:48.073820 IP (tos 0x0, ttl 64, id 50758, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.12345 > 127.0.0.1.49088: Flags [.], cksum 0xfe28 (incorrect -> 0x38ba), seq 4, ack 4, win 512, options [nop,nop,TS val 2055232093 ecr 2055170654], length 0
03:41:48.073832 IP (tos 0x0, ttl 64, id 27799, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.49088 > 127.0.0.1.12345: Flags [.], cksum 0xfe28 (incorrect -> 0x38ba), seq 3, ack 5, win 512, options [nop,nop,TS val 2055232093 ecr 2055170654], length 0
03:41:48.073868 IP (tos 0x0, ttl 64, id 27800, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.49088 > 127.0.0.1.12345: Flags [.], cksum 0xfe28 (incorrect -> 0x38b9), seq 4, ack 5, win 512, options [nop,nop,TS val 2055232093 ecr 2055170654], length 0
03:41:48.073876 IP (tos 0x0, ttl 64, id 50759, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.12345 > 127.0.0.1.49088: Flags [.], cksum 0xfe28 (incorrect -> 0x38b9), seq 5, ack 4, win 512, options [nop,nop,TS val 2055232093 ecr 2055170654], length 0
....
....

Solution

  • What you see are actually two keep-alive probes with their matching reply - only you did not realize this since tcpdump does not explicitly say so. To reduce the output from tcpdump to the essential parts:

    1  127.0.0.1.12345 > 127.0.0.1.49088: ... seq 4, ack 4, ... length 0
    3  127.0.0.1.49088 > 127.0.0.1.12345: ... seq 4, ack 5, ... length 0
    

    In packet #1 is the keep-alive probe from the (presumably) server. The sequence number (seq=4) is one less than the current one and the payload is empty, so this is a keep-alive probe. The peer (client) answers in packet 3 with an ack which contains the real sequence number seen from the server (ack=5).

    2  127.0.0.1.49088 > 127.0.0.1.12345: ... seq 3, ack 5, ... length 0
    4  127.0.0.1.12345 > 127.0.0.1.49088: ... seq 5, ack 4, ... length 0
    

    In packet #2 is the keep-alive probe from the (presumably) client. Again, sequence number (seq=3) is one less and the payload empty. The reply from the peer (server) acks as expected the real sequence number seen (ack=4).

    If you would view the same packet capture in Wireshark it would likely explicitly highlight the TCP keep-alive. tcpdump does not do this and interpretation of the packets is left to the one using the tool.