Networking from Scratch

Updated on

Why learn Low-Level Networking?

Networking is one of the most complicated and practical standards 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, making it hard to pick up network debugging skills.

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.

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:

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: <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 noprefixroute
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST> 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: <BROADCAST,MULTICAST> 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 <BROADCAST,MULTICAST> 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:

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: <BROADCAST,UP,MULTICAST> 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