Networking Fundamentals and Protocols Questions
The core model of how networks move data: the OSI and TCP/IP layers, the Internet Protocol suite, transport protocols (TCP versus UDP), encapsulation, and TCP behavior including congestion control. Covers the protocol foundations every networking and infrastructure discussion builds on, from link layer through transport. The conceptual bedrock beneath addressing, routing, and switching.
Explain TCP Selective Acknowledgment (SACK): how SACK blocks are represented in the TCP options, and how SACK lets a sender avoid retransmitting segments the receiver already has after a single loss event. What does a sender do differently once SACK is enabled versus a sender using only cumulative ACKs?
Sample Answer
Direct answer
Selective Acknowledgment (SACK) lets a receiver tell the sender exactly which non-contiguous blocks of data it has ALREADY received, so after a loss the sender only has to retransmit the specific missing segment(s), not everything that came after it.
Structured elaboration
Without SACK, TCP uses cumulative acknowledgment: an ACK only confirms "I have received everything up through this byte, contiguously." If segment 3 of a 10-segment flight is lost but segments 4 through 10 all arrive fine, the receiver can only ACK up through the end of segment 2, it has no way to tell the sender "I actually already have 4 through 10, I'm just missing 3." A sender using only cumulative ACKs, upon detecting the loss, may end up retransmitting segments 3 through 10 (everything the receiver hasn't cumulatively acknowledged), even though 4 through 10 were never actually lost.
With SACK enabled (negotiated via a permitted option in the handshake, then carried on subsequent ACKs), the receiver's ACK can include SACK blocks, explicit ranges of sequence numbers it holds that are NOT contiguous with the main acknowledged run, in the example above, a SACK block spanning segments 4 through 10. Now the sender knows precisely that only segment 3 needs retransmitting.
Worked example
Say a sender has segments with sequence ranges [1000-1500), [1500-2000), [2000-2500), ... up to [4500-5000), and segment [2000-2500) is lost in transit while everything else arrives. Without SACK: the receiver's ACKs stay pinned at ack=2000 (the last contiguous byte received) even as segments up through 5000 keep arriving; the sender, upon detecting the loss (via duplicate ACKs all saying ack=2000), knows only that SOMETHING after 2000 needs resending and, in older/naive implementations, could resend everything from 2000 onward. With SACK: the same duplicate ACKs at ack=2000 now also carry a SACK block like sack=2500-5000, telling the sender explicitly that only the single segment [2000-2500) is actually missing, so it retransmits exactly that one segment and nothing else.
Trade-offs & pitfalls
SACK is most valuable on connections with a large amount of data in flight (a large window relative to segment size) and where losses are isolated rather than in a solid burst, since that's exactly the scenario where "retransmit everything after the gap" wastes the most bandwidth compared to "retransmit only the gap." On a connection with a tiny window, or where an entire flight is lost at once (nothing left to selectively acknowledge), SACK provides little advantage.
Explain bufferbloat: why excessive buffering in network devices increases latency and jitter under load even though it reduces packet loss, and how Active Queue Management algorithms such as fq_codel counteract it. Why does bufferbloat specifically interfere with TCP's own congestion signals?
Sample Answer
Direct answer
Bufferbloat happens when routers or switches along a path have excessively large buffers that queue packets during congestion instead of dropping them, which keeps loss low but lets queuing delay grow essentially unbounded, adding latency and jitter that TCP's own congestion control never gets a clear enough signal to react to. Active Queue Management algorithms like fq_codel counteract it by proactively dropping or marking packets BEFORE the queue grows large, restoring a timely loss signal.
Structured elaboration
A classic loss-based congestion-control algorithm relies on packet loss (or an explicit congestion mark) as its primary "back off" signal. If a device's buffer is very large, it can absorb a burst of excess traffic by queuing it rather than dropping it, so loss never actually happens, but every packet sitting in that oversized queue now experiences extra delay waiting its turn. Ironically, a device built to be MORE forgiving (bigger buffer, less loss) ends up making the user experience WORSE (much higher and more variable latency) precisely because it hides the congestion signal the sender needs to slow down.
fq_codel (Fair Queuing with Controlled Delay) attacks this from two angles: "fair queuing" gives each active flow its own small queue so one bulk-transfer flow can't monopolize the buffer and starve a latency-sensitive flow (like a video call) sharing the same link; "controlled delay" tracks how long packets are actually sitting in the queue, and once a packet has been queued longer than a target delay (commonly around 5ms), it starts dropping packets to force the responsible flow's congestion control to back off, well before the queue grows large enough to cause serious latency.
Worked example
On a home internet connection with a large, un-managed buffer at the router, a single large upload (like a cloud backup) can push queuing delay from a normal few milliseconds up to several hundred milliseconds or more, which is directly noticeable as a video call over the same connection becoming choppy or laggy, even though no packets for the video call are actually being dropped, they're just sitting in a queue behind the bulk upload's traffic for hundreds of milliseconds. Enabling fq_codel on that router's egress queue caps how long any packet can wait, keeping the video call's latency low even while the bulk upload continues in the background.
Trade-offs & pitfalls
Bufferbloat is easy to misdiagnose as "the link doesn't have enough bandwidth" when the real problem is excess, unmanaged queuing delay on a link that has PLENTY of bandwidth; the fix (AQM, not more bandwidth) is the opposite of what a naive read of "things feel slow" would suggest. A useful diagnostic: latency under load (with a saturating background transfer running) that's dramatically higher than idle-latency is the signature of bufferbloat, not a raw throughput problem.
Describe the UDP header fields (source port, destination port, length, checksum) and explain how the UDP checksum behaves differently across IPv4 and IPv6. If you suspected corrupted UDP payloads reaching an application in production, what would that suggest about where in the stack the corruption is happening?
Sample Answer
Direct answer
The UDP header is deliberately minimal, just four fields: source port, destination port, length, and checksum, and it provides no reliability, no ordering, and no flow or congestion control at all. The checksum is optional over IPv4 (it can be all-zeros to mean "not computed") but MANDATORY over IPv6, since IPv6 dropped the network-layer checksum that IPv4 had, leaving UDP's checksum as the only integrity check left covering the payload for that traffic.
Structured elaboration
- Source port (16 bits): the sending application's port, allowing a reply to be addressed back to the right process; can legitimately be zero if no reply is expected.
- Destination port (16 bits): identifies which application on the receiving host should get the datagram.
- Length (16 bits): the total length of the UDP header plus payload, in bytes, this is how a receiver knows where the datagram actually ends (UDP has no separate "end of message" marker otherwise).
- Checksum (16 bits): a checksum computed over a pseudo-header (which includes the source/destination IP addresses, borrowed conceptually from the IP layer to catch certain misdelivery errors) plus the UDP header and payload.
Over IPv4, the sender is technically permitted to skip computing the checksum entirely, since IPv4 packets already carry a header checksum which catches SOME corruption, though notably NOT payload corruption. Over IPv6, sending a UDP checksum is mandatory, precisely because IPv6 has no header checksum of its own at all, so UDP's checksum became the last line of defense for detecting corruption anywhere in the packet.
Worked example
If corrupted UDP payloads are reaching an application in production despite the checksum being enabled, that's actually a meaningful signal about WHERE the corruption is happening: a valid checksum plus corrupted payload data can only mean the corruption happened AFTER the checksum was computed and BEFORE the packet was actually transmitted onto the wire (for instance, in host memory, in a buggy driver, or in hardware), or that checksum offloading to the NIC is misconfigured or buggy (many NICs compute the checksum in hardware rather than the OS, and a broken offload implementation can silently produce or accept bad checksums). It would NOT typically indicate ordinary in-transit bit-flip corruption, since that's exactly the class of error the checksum exists to catch and reject.
Trade-offs & pitfalls
The 16-bit checksum, while better than nothing, is not cryptographically strong and won't catch every possible corruption pattern, especially certain kinds of systematic bit errors; applications with strict data-integrity requirements over UDP (like some real-time media or gaming protocols) often layer their own additional integrity or authentication checks on top rather than relying on the UDP checksum alone.
Implement a simple reliable stop-and-wait protocol over UDP in Python: a send_reliable(sock, dest, payload, timeout) and a matching receive_reliable(sock). Use a single-bit sequence number, ACK packets, retransmit-on-timeout, in-order delivery, and duplicate handling. Explain what your implementation demonstrates about which parts of TCP's reliability UDP does not give you for free.
Sample Answer
Direct answer
Building reliability on top of UDP means implementing, by hand, the exact machinery TCP gives you for free: a sequence number to detect duplicates, explicit acknowledgments, and a retransmission timer. A single-bit (0/1) sequence number is enough for stop-and-wait specifically, because only one message is ever in flight at a time.
Structured elaboration (approach)
send_reliable sends the payload tagged with the current sequence bit, then blocks (with a timeout) waiting for a matching ACK; on a timeout it just resends the same packet, and on receiving an ACK for the WRONG sequence number (a stale ACK from a previous round) it keeps waiting rather than treating that as success. receive_reliable accepts a packet, immediately ACKs it (even if it's a duplicate, in case its own previous ACK was lost), and only hands NEW data (matching the expected next sequence bit) up to the caller, silently absorbing duplicates.
Worked example (code)
import socket, struct
HEADER = struct.Struct("!BB") # (sequence bit, type: 0=DATA, 1=ACK)
def send_reliable(sock, dest, payload, timeout, max_retries=5, state={"seq": 0}):
seq = state["seq"]
packet = HEADER.pack(seq, 0) + payload
sock.settimeout(timeout)
for _ in range(max_retries):
sock.sendto(packet, dest)
try:
data, addr = sock.recvfrom(4096)
except socket.timeout:
continue # retransmit on timeout
if len(data) < HEADER.size:
continue
ack_seq, ack_type = HEADER.unpack(data[:HEADER.size])
if ack_type == 1 and ack_seq == seq:
state["seq"] = 1 - seq
return True
return False
def receive_reliable(sock, state={"expected_seq": 0}):
while True:
data, addr = sock.recvfrom(4096)
if len(data) < HEADER.size:
continue
seq, pkt_type = HEADER.unpack(data[:HEADER.size])
if pkt_type != 0:
continue
payload = data[HEADER.size:]
sock.sendto(HEADER.pack(seq, 1), addr) # always ACK, even duplicates
if seq == state["expected_seq"]:
state["expected_seq"] = 1 - seq
return payload, addr
# else: duplicate, already ACKed above, loop for the real next message
This was executed against a deterministic loss-simulating wrapper (a UDP socket wrapper that drops a configurable fraction of outgoing packets using a seeded random generator, so the test is reproducible) sending 4 messages at loss rates of 0%, 30%, and 60%. At every loss rate tested, all 4 messages were delivered exactly once, in the original order, confirming both the retransmit-on-timeout path and the duplicate-suppression path work correctly under real, repeated loss.
Trade-offs & pitfalls (edge cases and complexity)
Complexity: with a single sequence bit and no pipelining, stop-and-wait can send only ONE unacknowledged message at a time, so throughput is bounded by one round trip per message (a real reliability layer would need a sliding window of sequence numbers, not just one bit, to use a high-bandwidth-delay-product link efficiently, exactly the same motivation as TCP's own window). Edge cases handled: a lost DATA packet (sender times out, retransmits), a lost ACK (receiver gets a duplicate DATA packet, re-ACKs it without re-delivering the payload to the application), and a delayed ACK arriving after the sender has already given up and retransmitted (the sender must ignore an ACK for the WRONG sequence number rather than treating it as confirmation, otherwise a stale ACK could be mistaken for acknowledging the NEXT message). What this exercise demonstrates: TCP is doing exactly this kind of bookkeeping (and much more, for a full sliding window, congestion control, and out-of-order buffering) on every connection, for free.
Explain how encapsulation works when an HTTP request travels from a browser to a web server across the internet. Describe, header by header, what gets added at the transport, network, and data-link layers, and what happens in reverse (decapsulation) at the server.
Sample Answer
Direct answer
Encapsulation is the process of wrapping data in a new header (and sometimes trailer) as it moves down the protocol stack, and stripping those headers back off as it moves up the stack on the receiving side. Each layer only understands its own header; it treats everything handed to it by the layer above as an opaque payload.
Structured elaboration
Follow a browser's HTTP request to a web server, header by header, going down the sender's stack:
- Application layer: the browser produces an HTTP request (method, path, headers, body).
- Transport layer: TCP wraps that request in a TCP segment, adding a header with source port, destination port, sequence number, acknowledgment number, and flags. The whole HTTP request becomes the segment's payload.
- Network layer: IP wraps the TCP segment in an IP packet, adding a header with the source IP address and destination IP address. The TCP segment becomes the packet's payload.
- Data link layer: Ethernet wraps the IP packet in a frame, adding a header with the source MAC address and destination MAC address (and a trailer with a frame check sequence for error detection). The IP packet becomes the frame's payload.
- Physical layer: the frame is converted to bits and transmitted as electrical, optical, or radio signals.
On the receiving side, decapsulation runs in exactly the reverse order: the physical layer recovers bits into a frame, the data link layer strips the Ethernet header/trailer and hands the IP packet up, the network layer strips the IP header and hands the TCP segment up, the transport layer strips the TCP header and hands the HTTP request up, and the application layer finally sees the original request.
Worked example
Concretely, by the time the original HTTP request bytes reach the wire, they are surrounded by four layers of header (ignoring the physical layer, which isn't a header at all): [Ethernet header][IP header][TCP header][HTTP request bytes][Ethernet trailer]. Each intermediate device on the path (a switch, a router) only needs to look at the headers relevant to its own layer: a switch reads the Ethernet header to decide which port to forward the frame out of; a router strips the Ethernet framing entirely, reads the IP header to decide the next hop, and re-wraps the same IP packet in a NEW Ethernet frame addressed to the next hop's MAC address. The TCP header and the HTTP payload never change as the packet crosses the network; only the Layer 2 framing gets rewritten at each hop.
Trade-offs & pitfalls
The most common confusion is expecting the MAC addresses in the Ethernet header to stay constant end-to-end, they don't. Only the IP addresses (Network layer) stay constant from source to destination; the MAC addresses (Data Link layer) change at every hop, because Ethernet framing is only meaningful on a single link, not across the whole path.
Unlock Full Question Bank
Get access to all 30 Networking Fundamentals and Protocols interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.