| # The Demultiplexing Principle | |
| Just as a kernel is responsible for isolating programs from one another, | |
| protecting each program's memory, CPU budget, etc from interference by other | |
| programs, a network stack is responsible for isolating programs' network | |
| resources, protecting them from interference by other programs. | |
| More generally, we can say that a network stack is responsible for isolating the | |
| resources of different *security domains*, where a security domain might be a | |
| process, a collection of processes (for example, a collection of processes owned | |
| by the same user on a UNIX system), a sandbox within a process (for example, a | |
| web assembly program run inside of a VM which runs multiple such programs at a | |
| time), etc. | |
| Isolation of network resources is a security property. Imagine a security | |
| domain, A, that initiates a TCP connection with a remote server, and accepts | |
| instructions over that connection. If another domain, B, on the system is able | |
| to inject data into the TCP stream, then an important security property of | |
| domain A has been violated. | |
| Since all interaction with the network stack (excepting management plane | |
| interaction, which is not relevant to this discussion) is via sockets, this | |
| implies that the stack is responsible for isolating sockets from each other. A | |
| given security domain may opt to allow two or more of its sockets to interfere | |
| with each other since that doesn't violate that domain's security, but this must | |
| not be the default, at least for sockets owned by different domains. | |
| We define "interference" here as one socket either receiving traffic that was | |
| meant for another one, or causing traffic that should be received by it to be | |
| received by a different socket instead. In other words, when a remote host | |
| receives a packet and then responds with a packet of its own, it intends for the | |
| response to be received by the original sender. Conversely, when a remote host | |
| sends a packet unprompted, it intends for that packet *not* to appear as though | |
| it is the response to some previously-sent packet. A socket has "interfered" | |
| with another socket if either of these properties don't hold. | |
| Outbound traffic from multiple sockets is multiplexed together into a single | |
| outbound stream of IP packets or link-layer frames. Inbound traffic - in the | |
| form of a single stream of IP packets or link-layer frames - is demultiplexed in | |
| order to deliver each packet to the appropriate socket. Different sockets may | |
| employ different demultiplexing schemes (for example, TCP and UDP use the | |
| four-tuple of remote and local IP address and remote and local port, while ICMP | |
| echoes use the three-tuple of remote and local IP address and ICMP ID), but the | |
| fundamental requirement of being able to demultiplex a single stream of inbound | |
| traffic into multiple different sockets remains the same. | |
| Together, these security requirements and the multiplexed nature of network | |
| traffic imply what we refer to as the **demultiplexing principle**. The | |
| demultiplexing principle states: | |
| 1. Every socket is associated with a set of packet addresses such that it | |
| would be valid for the socket to receive a packet with any of the | |
| addresses in that set. This is called the socket's "address set". | |
| 2. No two sockets may exist at the same time whose address sets overlap. | |
| 3. A socket must never send a packet that would prompt a response whose | |
| address is not in the socket's address set. | |
| If two sockets, A and B, are owned by the same security domain, then the | |
| rules are relaxed as follows: | |
| 4. Sockets A and B may exist at the same time even if their address sets | |
| overlap so long as the owning security domain has opted into it. | |
| Note that (3) depends on the semantics of the protocol in question. | |
| If we follow rules (1) through (3), then we can guarantee two things. First, we | |
| can guarantee that it can never be ambiguous which socket to deliver a packet to | |
| (if it were ambiguous, it would imply that rule (2) had been broken). Second, we | |
| can guarantee that, given sockets A and B, socket A can never interfere with | |
| soket B by sending traffic whose response will be delivered to socket B (if it | |
| could, this would imply either that the response traffic could be delivered to | |
| either A or B, violating rule (2), or that the traffic was unambiguously | |
| destined to socket B, violating rule (3)). | |
| Consider what might happen if we don't abide by this principle. We might have | |
| two UDP sockets - socket A with local address `1.1.1.1:1234` and remote address | |
| `*:*`, and socket B with local address `1.1.1.1:1234` and remote address | |
| `2.2.2.2:5678`. If an inbound packet is received from `2.2.2.2:5678` to | |
| `1.1.1.1:1234`, which socket should it be delivered to? It's possible that the | |
| remote host is attempting to communicate with socket A. It's also possible that | |
| socket B previously sent a packet, and the remote host is responding to it. | |
| There's no way to tell which is the case, and so it's ambiguous which socket | |
| should receive the packet. |