--- title: 'Networking from Scratch' description: 'Setup your own simple network on Linux' updateDate: 'Apr 22 2024' --- # Why learn Low-Level Networking? Networking is one of the most complicated and practical standards for ever created for digital communication. This unfortunately means that when we connect to a network, it's quite hard to understand what's actually happening and knowing this is very helpful for debugging. In this article we'll go through setting up a simple wired network using the `ip` tool on Linux. We'll also do a quick overview of next steps. You only need one Linux computer, but it's much more fun if you have two. You'll also want an Ethernet cable connecting the two Linux machines. ## Setting Up By default, your Linux computer almost certainly uses a network manager. This is a program that does what we're about to do automatically. You should never have more than one network manager running at a time, so if we're acting as one, you'll need to turn yours off. ```bash systemctl disable NetworkManager.service systemctl disable systemd-networkd.service ``` It's okay if one of those errors. Next, to clear the configuration those network manager provided, you'll need to reboot with `systemctl reboot`. Further, put the following alias in your `~/.bashrc` or whichever file your shell uses: ```bash alias ip='ip -c' # Makes output colorful ``` # Background of Networking ## IP Addresses A computer can have multiple network interfaces. Most often these will be one of: - A WIFI card - An Ethernet port - A USB to Ethernet adapter Each interface can be assigned IP addresses. These addresses come in one of two flavours: IPv4 and IPv6. Version 4 is the old standard that's used almost universally. Version 6 came out in 1996 and improves on IPv4 by adding more addresses (IPv4 only has about 4.3 billion... these ran out in 2010). Unfortunately, IPv6 adoption is one of the slowest updates in the history of computing, so IPv4 remains more commonly used, with many home networks not even offering IPv6. An IPv4 address is 32bits, represented by decimal numbers in groups of 8bits, with dots in between. This means each of the four numbers range from 0-255 inclusive. Here are some examples: ``` 127.0.0.1 10.0.0.0 10.42.43.250 ``` An IPv6 address is 128bits, represented by hexadecimal numbers, in groups of 16bits, with colons in between. Two consecutive colons can be used to indicate filler zeros. Here are some examples: ``` fd00:1bac:c0ca:12a2:1a7e:b9ff:fe07:d7a2 2001:0db8:85a3:0000:0000:8a2e:0370:7334 2001:0db8:85a3:0:0:8a2e:0370:7334 2001:0db8:85a3::8a2e:0370:7334 ``` We will use IPv4 for the convenience, but the concepts transfer quite directly to IPv6. ## Subnet Masks A network mask is used to figure out which IP addresses belong to a network. This is specified as the number of bits in a slash after the IP address. For example `192.168.1.7/24` means the netmask is the first 24 bits of this IPv4 address. Remember, that each number represents 8 bits, so we can convert this address to: ``` 192 . 168 . 1 . 7 11000000 10101000 00000001 00000111 └─────────────┬────────────┘ First 24 bits ``` In this case, that means the last of the four numbers can be anything, and the IP address will be considered as part of this network. Some examples: - `192.168.1.0` - `192.168.1.255` - `192.168.1.127` - `192.168.1.100` In total, there are 256 addresses on this network. The largest "block" reserved for private use is `10.0.0.0/8`. This network has about 16.7 million addresses! ``` 10 . 0 . 0 . 0 00001010 00000000 00000000 00000000 └────┬───┘ First 8 bits ``` # The Address Table ## Parsing the Address Table Use `ip address` or `ip addr` or `ip a` to display your address table. It should look something like: ``` 1: lo: 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 noprefixroute valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 18:7e:a9:47:e2:c7 brd ff:ff:ff:ff:ff:ff 3: wlan0: mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 3c:22:ff:b8:27:a1 brd ff:ff:ff:ff:ff:ff ``` Let's go through this output. Firstly, we see there are three interfaces on this computer. Their names are `lo`, `eth0`, and `wlan0`. Typically, interfaces starting with an `e` are wired and those starting with `w` are wireless (WIFI). `lo` is the loopback interface. This isn't a real interface, but your computer uses it to send network messages to itself. We can see its IPv4 address in the `inet` line is `127.0.0.1` with a subnet mask of `8`. In the `inet6` line, we see it has an IPv6 of `::1` with subnet mask `128`. This is true for all Linux computers, so you should see the same thing. `eth0` is an Ethernet port on my computer. The lack of an `UP` in the `` means that the interface isn't currently communicating with anything. On the `link/ether` line, we see the MAC address of this interface. `eth0` currently doesn't have any IP addresses assigned to it, so the `inet` and `inet6` lines are missing. `wlan0` has a similar situation with `eth0`, in that it doesn't have any addresses yet. ## Adding an Address You will need `sudo` for any `ip` commands which modify the network tables. I will omit the `sudo` from here on. Let's add an IP address of `10.42.43.20/24` and `10.42.43.100/24` to `eth0`: ```bash ip a add 10.42.43.20/24 dev eth0 ip a add 10.42.43.100/24 dev eth0 ``` Now the address table should show these addresses: ``` ... 2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 18:7e:a9:47:e2:c7 brd ff:ff:ff:ff:ff:ff inet 10.42.43.20/24 brd 10.42.43.255 scope global eth0 valid_lft forever preferred_lft forever inet 10.42.43.100/24 brd 10.42.43.255 scope global eth0 valid_lft forever preferred_lft forever ... ``` As a challenge, try adding an IPv6 address! # The Routing Table Routing tables determine where your computer will send network messages (known as packets). This is very important in determining which interface to send a packet over and using the loopback when possible. Say we have two interfaces with the following addresses: - `eth0`: `10.42.43.20/24` - `wlan0`: `192.168.0.4/24` If we want to send the packet to IP address `10.42.43.30`, the computer will make sure to use `eth0`. In a more complicated case, consider: - `eth0`: `10.42.43.20/24` - `wlan0`: `10.42.43.21/8` Sending to IP `10.42.40.1` clearly must go through interface `wlan0`, but what about a packet to `10.42.43.1`? Both interfaces can legally send this packet, as their subnetworks both contain the address `10.42.43.1`. To determine which one to use, your computer will check the routing table. ## Parsing the Routing Table You can view your routing table with `ip route` or `ip r`. Your routing table might look like the this right now: ``` 10.42.43.0/24 dev eth0 kernel scope link src 10.42.43.100 10.42.43.0/24 dev eth0 kernel scope link src 10.42.43.20 ``` According to the routing table above, any packets sent to `10.42.43.0/24` will have the `10.42.43.100` IP address in their header, when sent from `eth0`. That's since it appears higher in the routing table. ## Setting routes Adding a route is very similar to typing exactly what you want to see in the routing table: ``` ip r add 10.42.43.0/24 dev eth0 ``` You can also remove routes, which is helpful if you don't want a certain interface sending packets: ``` ip r del 10.42.43.0/24 dev eth0 ``` The last important route is the "default" route. Often this is called the "default gateway" in network managers. This is the route used when the IP you're trying to reach isn't on one of the subnetworks you're connected to. It's really the "internet". ``` ip r add default via 10.42.42.1 dev eth0 ``` This means that when your computer can't find a matching subnetwork in the routing table, it'll send the packet over to `10.42.43.1` using interface `eth0`. Assuming `10.42.43.1` is setup for packet forwarding and has internet access, this will give your computer internet access as well! Default routes are confusingly listed at the top of the routing table, but they're used in order (top to bottom) only after all the subnets have been checked. ``` default via 10.42.43.1 dev eth0 10.42.43.0/24 dev eth0 kernel scope link src 10.42.43.100 10.42.43.0/24 dev eth0 kernel scope link src 10.42.43.20 ```