Fundamentals 14 min read

When Do SO_REUSEADDR and SO_REUSEPORT Actually Take Effect? A Deep Dive

This article explains the differences and interactions between the SO_REUSEADDR and SO_REUSEPORT socket options, shows how they behave on BSD and Linux kernels across versions, and provides code examples and tables to illustrate binding rules, TIME_WAIT handling, and the evolution of reuse‑port support.

Liangxu Linux
Liangxu Linux
Liangxu Linux
When Do SO_REUSEADDR and SO_REUSEPORT Actually Take Effect? A Deep Dive

Overview of SO_REUSEADDR and SO_REUSEPORT

Both socket options affect how a socket can reuse an address/port pair during the bind() call, which determines the source address and source port of a socket.

5‑tuple and binding rules

A TCP/UDP connection is uniquely identified by the 5‑tuple {protocol, src addr, src port, dest addr, dest port}. Two connections cannot share an identical 5‑tuple because the kernel would be unable to decide which socket should receive an incoming packet.

The source address and port are set by bind(); the destination address and port are set by connect(). This summary focuses on the explicit bind() case.

Behavior of SO_REUSEADDR (BSD semantics)

When SO_REUSEADDR is enabled, a socket bound to the wildcard address 0.0.0.0 can coexist with another socket bound to a specific address on the same port. It also permits rebinding to an address that is in TIME_WAIT after the previous owner closed the connection.

SO_REUSEADDR   socketA        socketB       Result
-----------------------------------------------------
ON/OFF   192.168.0.1:21   192.168.0.1:21   Error (EADDRINUSE)
ON/OFF   192.168.0.1:21   10.0.0.1:21      OK
ON/OFF   10.0.0.1:21      192.168.0.1:21   OK
OFF      0.0.0.0:21      192.168.1.0:21   Error (EADDRINUSE)
ON       0.0.0.0:21      192.168.1.0:21   OK

Behavior of SO_REUSEPORT (BSD semantics)

SO_REUSEPORT

allows two sockets to bind to exactly the same <IP:Port>. The bind always succeeds on BSD.

SO_REUSEPORT   socketA        socketB       Result
-----------------------------------------------------
ON            192.168.0.1:21   192.168.0.1:21   OK

Linux kernel evolution

Before kernel 3.9 only SO_REUSEADDR existed. For client sockets it behaved like BSD's SO_REUSEPORT.

Kernel 3.9 introduced native SO_REUSEPORT. Server sockets that called listen() could bind to the same IP:Port only if the option was set.

From 3.9 to 4.5 the kernel split the original skc_reuse field into separate address‑reuse and port‑reuse bits and added a fastreuseport flag.

Since 4.5 Linux introduced reuseport groups , which collect sockets that share the same IP:Port and have SO_REUSEPORT set, allowing the kernel to pick a socket without scanning the entire hash bucket.

From 4.6 onward reuseport groups support both UDP and TCP.

Key structure changes (kernel ≥3.9)

struct sock_common {
    unsigned short skc_family;
    volatile unsigned char skc_state;
    unsigned char skc_reuse:4;      // address‑reuse flag
    unsigned char skc_reuseport:4;  // port‑reuse flag
};

struct inet_bind_bucket {
    unsigned short port;
    signed char fastreuse;
    signed char fastreuseport;
    kuid_t fastuid;
};

During bind() the kernel checks both skc_reuse and skc_reuseport together with fastreuse and fastreuseport to decide whether the bind can succeed.

Selection algorithm for listening sockets (kernel ≤4.4)

When a SYN arrives, the kernel computes a hash based on the destination port, walks the corresponding collision chain, scores each socket by 5‑tuple match, and if multiple sockets have the highest score and have SO_REUSEPORT set, one is chosen randomly.

struct sock *__inet_lookup_listener(...){
    ...
    if (reuseport) {
        // pick from reuseport group (newer kernels) or random (older kernels)
    }
    ...
}

Illustrative example

Assume four listening sockets A, B, C, D share the same port. A and B have SO_REUSEPORT enabled. When a SYN for 192.168.10.1:21 arrives, the kernel scores each socket; B, which listens on the exact address, receives a higher score and is selected.

hash collision chain example
hash collision chain example

Reuseport groups (kernel ≥4.5)

Sockets that share the same IP:Port and have SO_REUSEPORT set are placed into a reuseport group. When a new connection arrives, the kernel can directly select a socket from this group without scanning the whole hash bucket.

reuseport group diagram
reuseport group diagram

Linux implementation details (pre‑3.9)

Before 3.9 the kernel used a single skc_reuse flag (stored in sock_common.sk_reuse) to represent address reuse. The inet_bind_bucket.fastreuse field indicated whether the port could be shared. The bind logic roughly performed:

if (tb->fastreuse > 0 && sk->sk_reuse && sk->sk_state != TCP_LISTEN)
    goto success; // allow reuse for non‑LISTEN sockets

Linux implementation details (3.9 – 4.5)

Kernel 3.9 split the reuse flag into skc_reuse (address) and skc_reuseport (port). The inet_bind_bucket structure gained fastreuseport and fastuid. The bind check became:

if ((tb->fastreuse > 0 && sk->sk_reuse && sk->sk_state != TCP_LISTEN) ||
    (tb->fastreuseport > 0 && sk->sk_reuseport && uid_eq(tb->fastuid, uid)))
    goto success;

When a SYN is processed, the kernel walks the hash bucket, scores sockets, and if reuseport is true it may select a socket from the reuseport group:

struct sock *__inet_lookup_listener(...){
    ...
    if (reuseport) {
        sk2 = reuseport_select_sock(sk, phash, skb, doff);
        if (sk2) {
            result = sk2;
            goto found;
        }
    }
    ...
}

Linux implementation details (≥4.5)

Reuseport groups were introduced to avoid scanning the entire collision chain. The sock structure now contains a pointer to the group:

struct sock {
    ...
    struct sock_reuseport __rcu *sk_reuseport_cb;
    ...
};

During lookup, if a socket belongs to a reuseport group the kernel selects one member directly, which reduces lookup latency for high‑concurrency servers.

Summary

SO_REUSEADDR

primarily enables wildcard bindings and allows rebinding to addresses in TIME_WAIT. SO_REUSEPORT enables true port sharing. Linux has progressively aligned its implementation with BSD, adding native support in kernel 3.9, separating address‑reuse and port‑reuse flags, and finally introducing reuseport groups (kernel 4.5+) to improve scalability for both UDP and TCP.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Linux kernelsocket programmingSO_REUSEPORTSO_REUSEADDR
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.