Networking Fundamentals
- Networking Fundamentals
Why networking comes first
You cannot secure, monitor, or attack what you don’t understand — and almost everything in security ultimately moves over a network. Before firewalls, capture tools, or intrusion detection make sense, you need the fundamentals: how IP, TCP, UDP, and ICMP carry data; how addresses, subnets, and routing get a packet from here to there; and how the layers stack up. This page builds that foundation — the core protocols, the hardware, the services (DNS, DHCP, NAT, VLANs), the sockets API programs actually use, and how to watch traffic on the wire.
TCP, UDP, IP, and ICMP
These protocols form the backbone of the modern Internet. But what are they?
IP: Internet Protocol
The internet protocol is the underlying protocol of the Internet. One of the fun aspects of IP is that it guarantees nothing. It simply provides an identification scheme for nodes on the Internet. It does not guarantee delivery, it does not guarantee order, it does not guarantee anything. It is a best-effort protocol, and that’s it.
There are two main variants of IP in use today: IPv4 and IPv6. IPv6 was created to accommodate the rising number of devices on the Internet – some estimates put the number of interconnected digital devices at well over 10 billion, with Cisco estimating 5.3 billion individual users. IPv4 only allows for 4.3 billion unique addresses, which is clearly insufficient (pigeon hole principle!!!). IPv6 allows for three hundred and forty undecillion, two hundred and eighty-two decillion, three hundred and sixty-six nonillion, nine hundred and twenty octillion, nine hundred and thirty-eight septillion, four hundred and sixty-three sextillion, four hundred and sixty-three quintillion, three hundred and seventy-four quadrillion, six hundred and seven trillion, four hundred and thirty-one billion, seven hundred and sixty-eight million, two hundred and eleven thousand, four hundred and fifty-six unique addresses, which should be enough for a while ($\approx 3.4e^{38}$). IPv4, by contrast, supports only 4.3 billion unique addresses ($\approx 4.3e^9$).
To compare, IPv6 has enough address to give roughly 100 addresses to each atom on the surface of the Earth. IPv4 barely has enough addresses to share one address per two people on Earth. IPv6 is a lot of addresses.
Assume that someone has invented nanites, small, self-controlled swarm robots on the nanometer scale. This puts them roughly the size of atoms. Next, assume that a swarm can replicate the functionality of arbitrary bacteria, at roughly 50 billion nanites per bacterium. The human body contains on the order of 50 trillion bacteria. There are roughly 8 billion people on Earth. If all the bacteria in all the humans on Earth were replaced by nanites, with each nanite assigned an IPv6 address, it would consume roughly $\frac{1}{10,000}$ of the available IPv6 addresses. In math: \(\left(\frac{50e12\ \mathrm{bacteria}}{\mathrm{human}}\right) \times \left(\frac{8e9\ \mathrm{humans}}{\mathrm{Earth}}\right) \times \left(\frac{50e9\ \mathrm{nanites}}{\mathrm{bacteria}}\right) \approx \left(\frac{2e^{34}\ \mathrm{nanites}}{\mathrm{Earth}}\right)\)
Since $\displaystyle \frac{3.4e^{38}}{2e^{34}}$ is roughly $10e^3$, each nanite could have $10,000$ IPv6 addresses.
While the above is mostly for amusement value, we won’t be discussing IPv6 much. While the Internet Society measures IPv6 adoption rate at 46%, the reality is that all of those systems also run IPv4. We will be focusing on IPv4, as it is the primary protocol in use today. IPv4 is also the default on most systems and networks.
IPv4
IPv4 addresses are 32 bits in length, and are typically represented in dotted decimal notation. Each segment is one octet (8 bits) of the address: $a.b.c.x$. The first three octets are the network address, and the last octet is the host address. The network address is used to identify the network, and the host address is used to identify the host on the network.
Each subnet, or collection of addresses contained within the network address, can be classified based on size.
- Class A: addresses of the form $a.x.y.z$, where $a$ is the network address and $x.y.z$ is the host address. Technically $a < 127$, but in practice, any value of $a$ was considered a class A address. These were for large networks, consisting of $2^{24}$ hosts.
- Class B: addresses of the form $a.b.x.y$, where $a.b$ is the network address. Contains $2^{16}$ hosts.
- Class C: addresses of the form $a.b.c.x$, where $a.b.c$ is the network address. Contains $2^8$ hosts.
This is great, as far as it goes, but it turned out this was simply too inflexible. Classless inter-domain routing, or CIDR (pronounced like cider), was created to increase flexibility in IP address block purchasing. CIDR notation is of the form $a.b.c.d/y$, where $a.b.c.d$ is the network address and $y$ is the number of bits in the network address. This allows for arbitrary network sizes, and is the standard notation used today.
How CIDR actually works is by defining a fixed portion (the network address) and a mask. For instance, a common private range in use is 10.0.0.0/8. This means that the first 8 bits of the address are fixed, and the remaining 24 bits are variable. “But wait”, you say, “this looks just like a Class A network from above!” And it does. But CIDR is much more flexible than the class system. For instance, another private range is 172.16.0.0/12. This means the first 12 bits are fixed, and all other bits are variable. This is midway between a Class A and a Class B network, containing $2^{20}$ hosts.
Any value of $y$ is valid, from 0 to 32. A value of 32 means that the entire address is fixed, and there are no hosts on the network. A value of 0 means that the entire address is variable, and the network is the Internet. A value of 31 is technically valid, but is not used in practice. A value of 31 would mean that there are only two hosts on the network, which is not enough to be useful. A value of 30 is the smallest useful network, containing 4 hosts. A value of 29 contains 8 hosts, and so on.
The two examples I chose were from the private address space defined in RFC6890. Specifically, those ranges marked for “Private-Use”. These ranges are
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
You’ve likely encountered the first and the third, while the second range, for reasons that are not entirely clear, is not used much in practice. These network ranges are sometimes called bogon networks.
Now, I know some of you are running a fairly large number of connected devices in your household. And if everyone was doing that, how can we possibly still be running on IPv4? The answer, such as it is, is NAT – Network Address Translation. Combined with private-use ranges above, NAT allows for a large private network to be behind a single globally routable IP address.
The way NAT works is to translate from the global IP range outside your router to the private IP range on the inside of your router. It does this by rewriting packets as they pass through the router, changing both the source IP address and the source port. It maintains a table of these mappings so that packets returning to the host inside the network can be translated back to the original source IP address and port. This is why you can have multiple devices on your home network all using the same IP address – the router is rewriting the packets to make it appear as if they are all coming from the same device. This is also why if you purchase a gift for your partner, they often start getting ads for that gift – since all devices on your private network are a single device outside your network. Ad networks are stupid.
ICMP: Internet Control Message Protocol
Weird stuff here. While ICMP is typically considered part of the Internet layer, it’s encapsulated within an IP packet…but it isn’t really the same type of protocol as TCP or UDP. It’s a control protocol rather than a transport protocol, and so is rightfully considered part of the
TCP: Transmission Control Protocol
TCP is what is known as a connection-oriented protocol. This means that it is designed to ensure that data is delivered to the destination in the order it was sent, and that the destination acknowledges receipt of the data. TCP is also designed to ensure that data is not corrupted in transit. TCP is used for a large variety of Internet traffic, including web browsing, email, and file transfers. While the proportional volume of Internet traffic that is TCP is shrinking, the absolute volume of TCP traffic is increasing, as the Internet grows.
Other services that use TCP as their underlying transport protocol include SSH, FTP, SMB/CIFS, and basically most things outside of streaming.
TCP is protocol number 0x6 in the IP suite.
UDP: User Datagram Protocol
UDP is a connectionless protocol – it’s very much a fire-and-forget protocol. Pretty much as simple as it gets in terms of transport protocols. UDP is often used for streaming media, where minor packet loss or mis-ordering is acceptable. UDP is also used for DNS, DHCP, and other services where a single packet is often sufficient.
UDP is protocol number 0x11 in the IP suite.
Networking Layers
Networking is a complex topic, requiring support across the entire computing stack. From physical hardware to drivers, to how to get packets from point A to point B, and who is listening for what data, there’s a lot to cover. Typically, networking is discussed in terms of layers.
OSI Model
Probably the least useful but weirdly pervasive models of the network is the 7-layer OSI model.
-
Physical
The physical layer is the electrical, mechanical, and procedural view of the network. This layer is concerned with number of wires, physical connectors used, voltage levels, transmission mode (simplex, half- or full-duplex), etc. of the networking technology in use. Other considerations include data encoding and modulation, frequency within the EM spectrum, and more.
This layer, in concrete terms, is the combination of the logic implemented on an ASIC and the PHY/transceiver where you can connect a cable. For ethernet, the PHY is the RJ-45 socket combined with the ethernet controller chip on the circuit board.
-
Data Link
The data link layer is concerned with the logical transmission of data between two nodes on a network. For an ethernet network, this layer consists of how to find other nodes on the network, how to put data on the wire, and how to interpret received data – in other words, the ethernet protocol, distinct from the ethernet electro-mechanical standard. Protocols that live at this layer include ARP.
This layer of the network is all about the movement of data between two nodes on the same network. This is the layer where MAC addresses are used to identify nodes on the network rather than IP addresses. Nodes are strictly NICs, not computers. A computer with 2 NICs is seen as 2 nodes on the data link layer.
Level 2 switches switch strictly on MAC addresses, and can only operate within the confines of a single network. They perform no routing, and are not aware of IP addresses.
-
Network
The network layer is where IP comes into play.
Level 3 switches switch on IP addresses, and can operate across multiple networks. They perform routing, and are aware of IP addresses. Level 3 switches are technically routers, but in common usage routers exist on the network boundary, rather than within the network. Level 3 switches are also known as multilayer switches. Notice any terminology conflation yet?
The primary distinction between hardware dubbed a router and hardware dubbed a level 3 switch, at the technical level, is one of provided services. A router typically runs a more general purpose OS and provides a variety of services, while a level 3 switch typically runs a more specialized OS and provides a more limited set of services. The distinction is not always clear, and the terms are often used interchangeably.
-
Transport
TCP and UDP are the primary protocols of the transport layer. This layer is concerned with the reliable delivery of data between two nodes on a network. This is the layer where ports come into play. Ports are used to identify services on a node, and are used to multiplex multiple services on a single node.
The 4-tuple of $(IP_{src}, port_{src}, IP_{dest}, port_{dest})$ is the unique identifier of a network conversation. The ports in question can be either UDP or TCP (both the same), depending on the application in use. Certain applications are so well known that they have defined names (
/etc/services) that are used interchangeably with the port number. For example, port 80 is the standard HTTP port, and port 443 is the standard HTTPS port. Other services are equally well-defined.Ports are divided into three ranges:
- 0-1023: well-known ports, reserved for system services
- 1024-49151: registered ports, reserved for user services
- 49152-65535: dynamic ports, used for ephemeral services
Administrative access is required to make use of a well-known port. The distinction between registered and dynamic ports is purely one of convention. It is neither enforced nor observed by most developers in practice, though IANA does maintain a list of registered ports.
The transport layer is pretty much where network engineering stops. Higher layers in the stack are primarily the domain of software engineers.
-
Session
Something, something, web application, something, something. Supposedly used for something. Ignore it.
-
Presentation
Some sort of translation layer between the application and the network. This is where things like encryption and compression supposedly live. In practice, this layer is often ignored.
-
Application
Everything above TCP really lives here. Application logic, user interfaces, everything. The Web is just an application living on the Internet, after all.
In tabular form, top down, OSI looks like:
| Application Layer (Layer 7) | |
| Determines the network services required. | |
| Examples: DNS, FTP, LDP, Telent, TFTP, SMTP and WWW | |
| Presentation Layer (Layer 6) | |
| Presents data to the application layer. Essentially functions as a translator from computer to human readable form. | |
| Examples: HTTP, TIFF, JPEG, MIDI and MPEG | |
| Session Layer (Layer 5) | |
| Establishes and maintains the connection between systems and formats the data for transfer between nodes. | |
| Examples: NFS, SQL, RPC | |
| Transport Layer (Layer 4) | |
| Defines how to address physical locations, how to make connections between nodes, and how to handle the network of messages. This layer is responsible for end-to end integrity and control of the session and handles the sequencing of packets. Changes data into segments. | |
| Examples: TCP, UDP, SPX | |
| Network Layer (Layer 3) | |
| Defines how packets of data are routed between end systems over interconnected networks. Routing error detection, and control of node data traffic are managed at this layer. Segments to packets. | |
| Examples: IP, OSPF, ICMP, RIP | |
| Data Link Layer (Layer 2) | |
| Defines the protocols that computers use in order to access the network for transmitting and receiving messages. Has two sub layers: Logical Link Control and Media Access Control. Packets to frame. | |
| Examples: ARP, SLIP, PPP | |
| Physical Layer (Layer 1) | |
| Defines the physical connection (RJ48, BNC, HSSI, etc…) between a host and a network and converts the bits into voltages or light impulses for transmission. Frame to bits. | |
| Examples: HSSI, X.21, EIA/TIA-232 and EIA/TIA-449 |
The OSI model isn’t really useful in reality, but for some reason is part of nearly every certification exam. It’s fine as an abstraction, but terribly for actually understanding how networks work. It’s also not how networks are actually implemented.
At least, not how the word “network” is used within the industry.
TCP/IP Model
The TCP/IP model is a 4-layer model, which is much more useful in practice. It is also the model used by the Linux kernel, and is the model we will use for this course. Basically, ignore layers 1, 5, and 6 of the OSI model, and you’re good to go.
Because of the way network technology is defined, the physical layer is really just the physical portion of the link layer. Ethernet, as a standard, defines both physical and link layers of the OSI model. So why separate them? The TCP/IP models calls this the link layer.
The OSI network layer is basically IP and ICMP, and is called the internet layer in TCP/IP. Same layer, different name.
The transport layer is basically TCP and UDP. The application layer is everything layered on top of TCP or UDP.
It’s worth pointing out here that from a networking perspective, there’s no distinction between HTTP, DNS, or SSH. They are just applications running on top of TCP/IP. At the networking level, I don’t care if you set up a DNS server or a gitlab instance. I just care what ports they are running on and what transport protocol they are using. Everything else is handled by the application logic.
| OSI | TCP/IP | ||||||
|---|---|---|---|---|---|---|---|
| Application | 7 | Application | |||||
| Presentation | 6 | ||||||
| Session | 5 | ||||||
| Transport | 4 | Transport (TCP) | |||||
| Network | 3 | Internet (Network) (IP) | |||||
| Data Link | 2 | Network Access(Data Link) | |||||
| Physical | 1 | Network Access(Data Link) |
Network Hardware
Switches
Technically operating purely at level 2 of the network stack, switches are used to connect multiple network nodes together. While historically networks could be a variety of topologies, the most common topology today is a star topology, where each node is connected to a central switch. A few variants exist:
- The hub-and-spoke topology, where multiple switches (each with many nodes) are connected to a central point, which is then connected to the rest of the network. This is the topology used in most enterprise networks.
- A sort of switch mesh, where multiple switches are connected to each other, and each switch is connected to multiple nodes. This is the topology used in most home networks. Topologically identical to the above, but with a different physical layout, mostly due to the needs of a home network.
- A ring topology (AKA a bus), where each switch is connected to two other switches, and the last switch is connected to the first switch. This is the topology used in most industrial networks.
- A mesh, where nodes are directly connected to other nodes, with each node acting as a switch. This is the topology used in most sensor networks.
Switches technically only operate on MAC addresses.
Routers
Routers are just switches that operate at level 3 of the network stack, and can be used to connect multiple networks together. While in common usage, router is used to denote a gateway between networks, any device that uses IP addresses to switch (or route) traffic is a router. What this means in context is that a router is aware of the greater network topology.
When used internally to a network, routers are typically called level 3 switches. When used to connect networks across logical entity boundaries, they are typically called routers. The primary difference is one of software, not hardware. Routers are usually running BGP, a more general purpose OS, and often offering additional services on the network. Switches are usually running OSPF or another internal routing protocol, a more specialized OS, and offering fewer services on the network.
The distinction between an internal routing algorithm and an external routing algorithm is based on policy, knowledge of the network, and visibility. External routing algorithms have more a sense of “the thing I want is that way” while internal routing algorithms know all the exact paths to travel between any two points. Scale is obviously of concern. We cover this more below.
Wireless Access Points (WAPs)
WAPs are just switches that operate over a wireless medium, typically some variant of IEEE 802.11. Topologies get really wild here due to the nature of RF communication, but the most common topology is a star topology, where each node is connected to a central WAP.
WAPs aren’t typically used within home networks. The added complexity simply isn’t worth it for most users. Instead, most home networks use a router with a built-in WAP. This is also the case for most small business networks. Larger businesses will typically use dedicated WAPs, as they can be more easily managed and scaled.
Most home routers are combination devices, with the functionality of a router, a switch, and a WAP all-in-one. Easy for the ISP to give to non-technical users, but terrible for anyone who wants to do anything more than the bare minimum.
Networking Fabrics
Networks take many forms. Odds are good you interact with at 3, possibly 4, primary network technologies in everyday life:
- Ethernet/WiFi for home, school, or work networks
- LTE/5G for mobile networks
- Bluetooth for personal device networks (e.g. headphones, smartwatches, etc.)
- Controller area networks (CAN) for any car built in this century. And many built last century, too!
Each of these technologies uses a different physical layer, each with its own mechanical and electrical properties. Each is standardized by a different standards body, and each has its own quirks, limitations, and advantages. In the case of mobile networks, the business needs drive its distinction from WiFi, despite the fact that they are both based on the same underlying technology (digital RF). Bluetooth differs from WiFi in terms of power, bandwidth, and range, but is otherwise very similar. CAN is a completely different beast, and is used for very different purposes.
Each physical layer used is a different fabric, or the physical object that provides interconnection between nodes. It can be fiber optic cabling, copper wire (as in ethernet), coaxial wire, RF waves, etc. Basically, if you can use it for some signaling, you can probably use it for networking in some fashion.
Then there are the more “exotic” networks, such as those based on RS-485, RS-232, or other serial protocols. BACnet – the building automation and control network – connects industrial systems such as HVAC and access control and runs over a variety of physical network media.
Oh, and then we have mesh networks operating over wireless (not WiFi) signals, such as LoRaWAN, Zigbee, Zwave, Thread, etc. These are typically used for IoT devices, and are often proprietary.
And let’s not forget the really unusual stuff, like digital modes over HAM radio. In many cases, these are based on the same precursors as the Internet, but are not the Internet. Some connect to the Internet. Most don’t. People often ignore this aspect of networking, but in an emergency, it might be the only thing we have left for area-wide communication.
Yes, we could have an entire term dedicated to this topic. Anyone interested?
Network Services
DNS
The Domain Name System is a distributed, hierarchical name service which provides translation from symbolic name to IP address. While there is much to be said about DNS, we mostly don’t worry about the details in this class. A few specific details:
- DNS is recursive. Yes, one of the only real-world applications of recursion, it is specifically tail recursive. What this means is that when I ask for a name resolution of my local resolver (typically my gateway), it returns the response of its request to its primary DNS server (typically its gateway). This recursive response return system is used all the way to the root servers for the given top-level domain (TLD).
- DNS runs in parallel. All locally defined servers are queried simultaneously. The first to respond is the one which is used. This is commonly misunderstood to be done sequentially, and only if previous servers fail. In some cases, this is an important detail.
- DNS is evil. It’s ALWAYS DNS.
- DNS uses port 53 on both UDP (default) and TCP (for larger queries or responses). This is known symbolically as “domain”.
DNS Record Types
A DNS zone contains resource records (RRs) that answer different kinds of questions:
| Type | Purpose | Example value |
|---|---|---|
| A | Maps a hostname to an IPv4 address | 192.0.2.1 |
| AAAA | Maps a hostname to an IPv6 address | 2001:db8::1 |
| CNAME | Aliases one name to another (canonical name) | www → webserver.example.com |
| MX | Mail exchanger — where to deliver email for a domain | 10 mail.example.com |
| NS | Authoritative name server for a zone | ns1.example.com |
| PTR | Reverse lookup: IP address → hostname (in-addr.arpa) |
host.example.com |
| TXT | Arbitrary text; used for SPF, DKIM, domain verification | "v=spf1 include:..." |
| SOA | Start of Authority — administrative data for the zone | serial, refresh, retry, expire, min-TTL |
| SRV | Service location record; specifies host + port for a service | _http._tcp 0 5 80 web.example.com |
TTL (Time to Live)
Every DNS record carries a TTL value (in seconds) that tells resolvers how long to cache the answer before re-querying. Short TTLs (≤ 60 s) cause frequent re-resolution; long TTLs (hours or days) let resolvers serve cached answers without hitting the authoritative server. This distinction matters significantly in security contexts — attackers use very short TTLs to rotate IP addresses rapidly, a technique known as fast flux (see Malware Network Behavior).
DNS Query Flow
Client → Recursive resolver (ISP / 8.8.8.8)
→ Root servers (.)
→ TLD servers (.com, .net, ...)
→ Authoritative server for the domain
← Returns the RR answer
← Caches for TTL seconds
← Returns answer to client
The client itself almost always speaks only to its recursive resolver (configured via DHCP or manually). The recursive resolver does all the walking of the DNS tree on the client’s behalf. This architecture means a single resolver sees queries for all clients behind it — a useful choke point for monitoring.
DHCP
The Dynamic Host Configuration Protocol is used to dynamically, and automatically, assign host configurations to endpoints on the network. This includes IP addresses, DNS servers, NTP servers, and a variety of other configuration parameters. This has been used in the past for malicious purposes (see Tunnel Vision Exploit).
- A DHCP server on the network is required to make use of DHCP.
- DHCP uses two ports, both UDP:
- 67 (bootps)
- 68 (bootpc)
- Typically, DHCP runs on the gateway server, but that isn’t required
NTP
The Network Time Protocol is used to synchronize endpoints with a network time server.
- Almost every endpoint uses NTP.
- Is nearly required in modern life.
- Runs on port 123 UDP (ntp).
- There are multiple tiers of time servers, but most systems use a Tier 1 server of some sort. These are primarily backed by some form of high precision clock (atomic clock, GPS signal, etc.)
ARP
The Address Resolution Protocol. Essentially DNS for ethernet. Maps IP addresses to MAC addresses.
SMB
The Server Message Block is a communication protocol for sharing various resources, primarily files and printers, over the network. More properly known now as SMB/CIFS (Common Internet File System), this is one of the two primary user-facing file sharing mechanism on modern networks, along with NFS. On Linux, this is implemented by the Samba project.
NFS
The Network File System is the second primary file sharing mechanism on a modern network. Originally developed by Sun, it provides a network-backed local-appearing file system interface. What this means, in context, is that the files appear to be local, and can be treated as if they are local, but they reside on a remote system. The CAT uses NFS for home directories on the Linux servers within the department.
LDAP
The Lightweight Directory Access Protocol is a distributed directory services protocol that underlies nearly account management system. This means you have centralized account management with distributed login.Usually implemented by the slapd daemon.
Routing
Routing is the movement of packets within a network, based on their endpoints. There are various classifications of routing algorithms, but the classification that will be relevant to us is internal vs. external. External routing moves packets between autonomous systems. Internal routing moves packets between hosts on a given subnet (or collection of subnets) within an AS.
Internal routing
Interior Gateway Protocol is the overarching name for a variety of internal routing protocols. The two primary classes of algorithms are
Please use the Wikipedia links for more details if you are interested. For our purposes, simply knowing they exist and are different from external routing algorithms like BGP is enough.
BGP
The Border Gateway Protocol is pretty much the external routing protocol. A contract-based, inter-AS routing protocol, it routes at the AS level, rather than at the device level. What this means is that a single hop in BGP terms can be a multi-node hop within an AS. Most major internet outages that weren’t cloud providers were caused by BGP flubs.Some fun history of that is covered in A Brief History of the Internet’s Biggest BGP Incidents.
Network Address Translation
NAT is a commonly used tool to cope with IPv4 address exhaustion, as mentioned above. While there are a variety of terms for types of NAT, the most common is a one-to-many – mapping a single public IP address to multiple privately addressed endpoints using the bogon networks defined for private use. This is sometimes called source NAT (SNAT). Many-to-one NAT is sort of the reverse of this, where you define port forwards or a DMZ host. This is sometimes called DNAT.
In the common usage, NAT takes a packet from an internal address, and rewrites portions of it. Specifically, for outbound traffic
- the source IP address is mapped from the internal to the external IP address,
- the source port is mapped to an unused port on the NAT device.
The public IP address and external port combination uniquely maps onto an internal connection, and is stored in the NAT table pointing to the original data. To reverse the mapping, for inbound connections, the IP,port pair is looked up in the NAT table, and the original data is restored. The upshot of this is that an upper limit exists on concurrent connections in flight. Since the upper limit is roughly 64k TCP and 64k UDP connections (ports are 16 bits), for the average home user NAT is essentially unlimited. For enterprise networks, care must be taken to avoid NAT exhaustion.
VLANs
A Virtual LAN (VLAN) is a logical subdivision of a physical network. Without VLANs, every device on a switch shares the same broadcast domain — any broadcast frame (ARP, DHCP, etc.) is forwarded to every port. VLANs partition that broadcast domain at the switch level, so traffic from one VLAN is invisible to devices in another VLAN even if they share the same physical switch.
VLANs are defined by IEEE 802.1Q, which specifies a 4-byte tag inserted into the Ethernet frame header. The tag carries a 12-bit VLAN ID (VID), allowing up to 4094 usable VLANs (0 and 4095 are reserved).
Ethernet frame with 802.1Q tag:
[Dst MAC][Src MAC][0x8100][PRI|DEI|VID][EtherType][Payload][FCS]
^^^^^^^^^^^^^^^^^^^^
802.1Q tag (4 bytes)
The 802.1Q tag fields:
- PRI (3 bits): 802.1p priority (0–7), used for QoS
- DEI (1 bit): Drop Eligible Indicator — frame may be dropped under congestion
- VID (12 bits): VLAN ID (1–4094)
Access Ports vs. Trunk Ports
Switch ports operate in one of two modes:
Access port: Assigned to a single VLAN. Frames leaving the port are untagged — the end device (PC, server) has no awareness of VLANs. The switch tags frames internally when they arrive, and strips the tag before sending.
Trunk port: Carries traffic for multiple VLANs simultaneously. Frames are tagged with their VID. Used for switch-to-switch links and switch-to-router links. A native VLAN is configured on trunk ports; frames in the native VLAN are sent untagged (default VLAN 1 on most switches).
Inter-VLAN Routing
VLANs are isolated at Layer 2. For hosts in different VLANs to communicate, traffic must be routed at Layer 3. Two common approaches:
Router-on-a-stick: A single physical link from the switch to a router carries tagged trunk traffic. The router creates subinterfaces, one per VLAN, each assigned an IP in the VLAN’s subnet.
# Linux router-on-a-stick: create VLAN subinterfaces
ip link add link eth0 name eth0.10 type vlan id 10
ip link add link eth0 name eth0.20 type vlan id 20
ip addr add 10.10.0.1/24 dev eth0.10
ip addr add 10.20.0.1/24 dev eth0.20
ip link set eth0.10 up
ip link set eth0.20 up
Layer 3 switch (SVIs): A managed switch with routing capability can route between VLANs internally using Switched Virtual Interfaces (SVIs) — one logical interface per VLAN acting as the default gateway.
Configuring VLANs on Linux
Linux supports 802.1Q natively via the 8021q kernel module.
# Load the module if needed
modprobe 8021q
# Create a VLAN interface on top of a physical interface
ip link add link eth0 name eth0.100 type vlan id 100
ip addr add 192.168.100.1/24 dev eth0.100
ip link set eth0.100 up
# Verify
ip -d link show eth0.100
# Remove the VLAN interface
ip link delete eth0.100
To make VLAN interfaces persistent, define them in your network configuration (Netplan, NetworkManager, or /etc/network/interfaces depending on your distro).
Netplan example (/etc/netplan/01-vlans.yaml):
network:
version: 2
ethernets:
eth0:
dhcp4: false
vlans:
eth0.10:
id: 10
link: eth0
addresses: [10.10.0.1/24]
eth0.20:
id: 20
link: eth0
addresses: [10.20.0.1/24]
Capturing VLAN Traffic
By default, tcpdump and tshark will see tagged frames on trunk interfaces. The 802.1Q tag appears in the capture.
# Capture tagged frames and show VLAN ID
tcpdump -i eth0 -e -nn vlan
# Filter to a specific VLAN
tcpdump -i eth0 vlan 100
# tshark: display VLAN ID field
tshark -i eth0 -T fields -e vlan.id -e ip.src -e ip.dst
Note: on access ports, frames are untagged by the time they reach the NIC, so you will not see 802.1Q tags when capturing on a host’s access port.
VLAN Security Considerations
VLAN hopping: An attacker on an access port can sometimes reach other VLANs by:
- Switch spoofing: Negotiating a trunk link with the switch (DTP). Mitigate by explicitly setting ports to access mode and disabling DTP.
- Double tagging: Crafting a frame with two 802.1Q tags. The outer tag matches the native VLAN and is stripped by the first switch; the inner tag routes the frame into the target VLAN on the next switch. Only works toward the attacker (one-way). Mitigate by changing the native VLAN to an unused VID.
VLANs provide segmentation, not security. A misconfigured switch can collapse VLAN boundaries. For strong isolation, use separate physical switches or enforce firewall rules at the routing layer between VLANs.
The Sockets API
A socket is the OS abstraction for a network endpoint. Both ends of a connection are sockets; data is read and written through them just like files. The sockets API originated in BSD Unix and is now standardized across POSIX systems. The same model underlies C, Python, Go, Rust, and virtually every other language’s networking stack.
Key Concepts
A socket is identified by a 5-tuple: protocol, source IP, source port, destination IP, destination port. The OS uses this tuple to demultiplex incoming packets to the correct socket.
Socket types:
SOCK_STREAM— reliable, ordered, connection-oriented (TCP)SOCK_DGRAM— unreliable, connectionless (UDP)SOCK_RAW— raw access to IP layer (requires root; used byping, packet crafting tools)
Address families:
AF_INET— IPv4AF_INET6— IPv6AF_UNIX— Unix domain sockets (local IPC, no network)
C Sockets API
The C API is the foundation everything else builds on. Understanding it makes higher-level APIs transparent.
TCP Server
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(void) {
/* 1. Create socket */
int fd = socket(AF_INET, SOCK_STREAM, 0);
/* Allow port reuse after restart */
int opt = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
/* 2. Bind to address and port */
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = INADDR_ANY, /* 0.0.0.0 */
};
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
/* 3. Listen for incoming connections */
listen(fd, /* backlog */ 5);
/* 4. Accept loop */
while (1) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
int conn = accept(fd, (struct sockaddr *)&client, &len);
char buf[1024];
ssize_t n = recv(conn, buf, sizeof(buf) - 1, 0);
buf[n] = '\0';
printf("received: %s\n", buf);
const char *resp = "HTTP/1.0 200 OK\r\n\r\nHello\r\n";
send(conn, resp, strlen(resp), 0);
close(conn);
}
close(fd);
}
TCP Client
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(void) {
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(8080),
};
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
connect(fd, (struct sockaddr *)&addr, sizeof(addr));
const char *msg = "GET / HTTP/1.0\r\n\r\n";
send(fd, msg, strlen(msg), 0);
char buf[4096];
ssize_t n = recv(fd, buf, sizeof(buf) - 1, 0);
buf[n] = '\0';
printf("%s\n", buf);
close(fd);
}
UDP
UDP skips connect/accept. Use sendto/recvfrom with explicit addresses.
/* UDP receiver */
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(9000),
.sin_addr.s_addr = INADDR_ANY };
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
char buf[1024];
struct sockaddr_in sender;
socklen_t slen = sizeof(sender);
recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sender, &slen);
/* UDP sender */
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in dst = { .sin_family = AF_INET, .sin_port = htons(9000) };
inet_pton(AF_INET, "127.0.0.1", &dst.sin_addr);
sendto(fd, "hello", 5, 0, (struct sockaddr *)&dst, sizeof(dst));
Key System Calls
| Call | Description |
|---|---|
socket(domain, type, proto) |
Create a socket fd |
bind(fd, addr, addrlen) |
Assign local address/port |
listen(fd, backlog) |
Mark socket as passive (server) |
accept(fd, addr, addrlen) |
Block until client connects; returns new fd |
connect(fd, addr, addrlen) |
Initiate connection (client) |
send/recv |
Send/receive on connected socket |
sendto/recvfrom |
Send/receive with explicit address (UDP) |
setsockopt |
Set socket options (reuse, timeouts, buffers) |
close |
Close socket fd |
getaddrinfo |
Resolve hostname to address (replaces gethostbyname) |
htons/ntohs |
Host ↔ network byte order (16-bit) |
htonl/ntohl |
Host ↔ network byte order (32-bit) |
inet_pton/inet_ntop |
Text ↔ binary IP address conversion |
Byte Order
Network byte order is big-endian. x86/ARM hosts are little-endian. Always convert multi-byte integers with htons/htonl before putting them in a sockaddr, and ntohs/ntohl after reading them out.
Non-blocking and Multiplexing
By default, accept, recv, and connect block. To handle multiple connections in one thread:
/* Make a socket non-blocking */
#include <fcntl.h>
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
/* poll: wait for events on multiple fds */
#include <poll.h>
struct pollfd fds[2] = {
{ .fd = server_fd, .events = POLLIN },
{ .fd = client_fd, .events = POLLIN },
};
int ready = poll(fds, 2, /* timeout ms */ 1000);
if (fds[0].revents & POLLIN) { /* server_fd readable — new connection */ }
if (fds[1].revents & POLLIN) { /* client_fd readable — data arrived */ }
epoll (Linux) is the scalable alternative to poll for servers handling thousands of connections simultaneously.
Python Sockets API
Python’s socket module is a thin wrapper around the C API, with the same calls and constants.
TCP Server
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv:
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('', 8080))
srv.listen(5)
print("listening on :8080")
while True:
conn, addr = srv.accept()
with conn:
print(f"connection from {addr}")
data = conn.recv(1024)
conn.sendall(b"HTTP/1.0 200 OK\r\n\r\nHello\r\n")
TCP Client
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('127.0.0.1', 8080))
s.sendall(b"GET / HTTP/1.0\r\n\r\n")
data = s.recv(4096)
print(data.decode())
UDP
import socket
# Receiver
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.bind(('', 9000))
data, addr = s.recvfrom(1024)
print(f"from {addr}: {data}")
# Sender
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.sendto(b"hello", ('127.0.0.1', 9000))
Hostname Resolution
import socket
# Resolve a hostname to IP(s)
infos = socket.getaddrinfo('example.com', 80, type=socket.SOCK_STREAM)
for family, type, proto, canonname, sockaddr in infos:
print(sockaddr) # ('93.184.216.34', 80)
# Simple lookup
ip = socket.gethostbyname('example.com')
Non-blocking and select
import socket, select
s.setblocking(False) # or s.settimeout(2.0) for a timeout
# select: portable multiplexing
readable, _, _ = select.select([srv, conn1, conn2], [], [], timeout=1.0)
for sock in readable:
if sock is srv:
conn, addr = srv.accept()
else:
data = sock.recv(1024)
For async servers in Python, prefer asyncio streams or selectors.DefaultSelector (which uses epoll on Linux automatically) over raw select.
socketserver — Higher-Level TCP/UDP Servers
import socketserver
class Handler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
self.request.sendall(data.upper()) # echo back uppercased
with socketserver.TCPServer(('', 8080), Handler) as srv:
srv.serve_forever()
socketserver.ThreadingTCPServer spawns a thread per connection; ForkingTCPServer forks a process.
Unix Domain Sockets
Unix domain sockets use AF_UNIX instead of AF_INET. They use filesystem paths as addresses rather than IP/port, and are faster than loopback TCP for local IPC (no network stack overhead).
/* C */
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strncpy(addr.sun_path, "/tmp/my.sock", sizeof(addr.sun_path) - 1);
# Python
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
s.bind('/tmp/my.sock')
s.listen()
conn, _ = s.accept()
Docker, systemd, and most daemons use Unix domain sockets for local communication (e.g., /var/run/docker.sock).
Monitoring a network
ip/ifconfig
One of the most basic things we need to know about a system on the network is its IP address. Assuming you have access to the host, the simplest way to find the IP address is with one of the following commands:
$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:82ff:fea4:7efd prefixlen 64 scopeid 0x20<link>
ether 02:42:82:a4:7e:fd txqueuelen 0 (Ethernet)
RX packets 29 bytes 1828 (1.8 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 66 bytes 7787 (7.7 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
enp0s1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.37.129.2 netmask 255.255.255.0 broadcast 10.37.129.255
inet6 fe80::746d:65ff:fe10:9ea1 prefixlen 64 scopeid 0x20<link>
ether 76:6d:65:10:9e:a1 txqueuelen 1000 (Ethernet)
RX packets 766 bytes 394784 (394.7 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 111 bytes 25678 (25.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 28672 bytes 2123134 (2.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 28672 bytes 2123134 (2.1 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth60197a2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::d8e9:e3ff:fe07:6b1c prefixlen 64 scopeid 0x20<link>
ether da:e9:e3:07:6b:1c txqueuelen 0 (Ethernet)
RX packets 29 bytes 2234 (2.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 101 bytes 11689 (11.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
$ ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 76:6d:65:10:9e:a1 brd ff:ff:ff:ff:ff:ff
inet 10.37.129.2/24 metric 100 brd 10.37.129.255 scope global dynamic enp0s1
valid_lft 85641sec preferred_lft 85641sec
inet6 fe80::746d:65ff:fe10:9ea1/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:82:a4:7e:fd brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:82ff:fea4:7efd/64 scope link
valid_lft forever preferred_lft forever
5: veth60197a2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether da:e9:e3:07:6b:1c brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::d8e9:e3ff:fe07:6b1c/64 scope link
valid_lft forever preferred_lft forever
That’s a lot of information to take in, and not always what we want. Sometimes, we just want the CIDR address of the system. For that, we can use a variety of commands:
$ #Option 1:
$ function sn () {
if [[ $# -eq 0 ]]
then
ip -o -f inet addr show | awk '{print $2":\t"$4}' | grep -E "enp|wlan"
else
ip -o -f inet addr show "$1" | awk '{print $4}'
fi
}
$ #option 2
$ function sn2 () {
if [[ $# -eq 0 ]]
then
for i in $(ifconfig | grep -E -o "^enp[0-9]s[0-9]|^wlan[0-9]|docker[0-9]")
do
k=$(ifconfig $i | grep "inet " | awk '{print $2}')
if [ ! -z $k ]
then
printf "%7s:\t%s/%s\n" $i $k $(sipcalc -i $(ifconfig $i | awk '/inet / {print $2" "$4}') | awk '/bits/ {print $5}')
fi
done
else
printf "%s/%s\n" $(ifconfig $1 | grep "inet " | awk '{print $2}') $(sipcalc -i $(ifconfig $1 | awk '/inet / {print $2" "$4}') | awk '/bits/ {print $5}')
fi
}
$ #if on a macos/bsd system
$ function sn () {
if [[ $# -eq 0 ]]
then
for i in $(ifconfig | grep -E -o "en\d{1,2}:" | tr -d ':')
do
k=$(ifconfig -f inet:cidr $i inet | awk '/inet / {print $2}')
if [ ! -z $k ]
then
echo "$i:\t$k"
fi
done
else
subnet $1
fi
}
netstat
netstat is an older tool, but has the benefit of being available on most systems. It can be used to display open sockets, routing tables, link statistics, and more. It is especially useful for determining what processes are listening on what ports.
$ #get numeric list of listening TCP and UDP sockets
$ sudo netstat -tunl
That said, netstat is deprecated in favor of ss, which is more modern and has more features.
See the netstat manpage for what tools replace which pieces of its functionality.
ss
The primary replacement for netstat, ss is a more modern tool which can display more detailed information about a variety of socket types, including internal kernel data regarding each socket.
Some useful flags include:
-l, --listening: list listening sockets-t, --tcp: list TCP sockets-u, --udp: list UDP sockets-x, --unix: list Unix sockets-p, --processes: show process information for each socket (requires admin privileges)-n, --numeric: don’t resolve hostnames or ports/services-r, --resolve: try to resolve hostnames-a, --all: show all sockets (including those not listening)-i, --info: show internal TCP information-e, --extended: show extended socket information
For example, to show the processes with open TCP listen sockets, resolved hostnames and ports, and extended information, you could run:
❯ sudo ss --processes --tcp --listening --extended --resolve
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 localhost:ipp 0.0.0.0:* users:(("cupsd",pid=48519,fd=7)) ino:191576 sk:300d cgroup:/system.slice/cups.service <->
LISTEN 0 4096 localhost:33273 0.0.0.0:* users:(("containerd",pid=1091,fd=10)) ino:25655 sk:220 cgroup:/system.slice/containerd.service <->
LISTEN 0 4096 localhost%lo:domain 0.0.0.0:* users:(("systemd-resolve",pid=993,fd=14)) uid:102 ino:18366 sk:221 cgroup:/system.slice/systemd-resolved.service <->
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:* users:(("sshd",pid=21897,fd=3)) ino:94802 sk:3011 cgroup:/system.slice/ssh.service <->
LISTEN 0 128 ip6-localhost:ipp [::]:* users:(("cupsd",pid=48519,fd=6)) ino:191575 sk:3012 cgroup:/system.slice/cups.service v6only:1 <->
LISTEN 0 50 *:1716 *:* users:(("kdeconnectd",pid=2235,fd=12)) uid:1000 ino:26074 sk:224 cgroup:/user.slice/user-1000.slice/session-3.scope v6only:0 <->
LISTEN 0 128 [::]:ssh [::]:* users:(("sshd",pid=21897,fd=4)) ino:94804 sk:3016 cgroup:/system.slice/ssh.service v6only:1 <->
tcpdump
See the tcpdump page.
Wireshark
See the wireshark page.
Other capture tools
- NIDS/NIPS
- Windows NDIS tools
- Darkstat
Key takeaways
- The Internet runs on IP (best-effort addressing and routing) carrying TCP (reliable, connection-oriented, with the 3-way handshake), UDP (fast, connectionless), and ICMP (control and diagnostics —
ping,traceroute, unreachable messages). - Reaching a host is addressing + routing: an IP plus a subnet mask (CIDR) defines the local network; ARP maps IP→MAC on a LAN; routers move packets between networks; NAT lets many private hosts share one public address.
- The services that make it usable: DNS (names→addresses), DHCP (automatic configuration), and VLANs (logically segmenting one switch) — each also an attack surface you’ll revisit in the security material.
- Programs speak all of this through the sockets API; everything you capture later with
tcpdump/Wireshark is these protocols on the wire. - Understanding the layers is the prerequisite for the rest of the course — you can’t filter, monitor, or defend traffic you can’t read.
References
- RFC 791 (IP), RFC 793 (TCP), RFC 768 (UDP), RFC 792 (ICMP) — the founding protocol specs. https://www.rfc-editor.org/rfc/rfc793
- Beej’s Guide to Network Programming — the classic sockets-API tutorial. https://beej.us/guide/bgnet/
- W. R. Stevens, TCP/IP Illustrated — the standard reference text. https://en.wikipedia.org/wiki/TCP/IP_Illustrated
- Cloudflare Learning Center — accessible explainers for DNS, TCP, BGP, and more. https://www.cloudflare.com/learning/
Related course pages: Capturing Packets with tcpdump · Wireshark · SSH Tunnel for Windows RDP
🛠️ Maintenance note: core networking protocols are decades-stable, but two things to refresh: the “Monitoring a network” section trails off into a stub (
NIDS/NIPS, Windows NDIS, Darkstat) and could be fleshed out or pointed at the capture pages, and IPv6 deserves more coverage as adoption grows. Verify theip/sscommand examples against the course VM (the oldifconfig/netstattools are deprecated).