computer-networks

Capitolul 0 - Getting familiarized

Cuprins

Înainte de a începe

Instalați docker și docker compose urmărind pașii din documentația oficială.

Verificați că cele două comenzi funcționează executând:

docker ps
docker compose ps

# rezultat: CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Pe linux e posibil să primiți permission denied. Va trebui în acel caz să adăugați userul în grupul de docker:

# se adauga userul in grup
sudo usermod -aG docker $USER
# se face relod la setari in shellul curent
newgrep docker
# dupa un log out nu va mai fi nevoie sa rulati vreo comanda de mai sus

Introducere Docker

Un container (sau serviciu) docker poate fi pornit (în mod asemănător cu o mașină virtuală) cu o imagine cu un sistem de operare. Un container seamănă mai mult cu un proces decât cu o mașină virtuală. Acesta nu emulează componente hardware, ci execută apeluri sistem cu dependințele necesare rulării unei aplicații.

Microservices vs monolithic architecture: alt text (img credits)

Containerization: alt text(img credits)

Building a docker image

Pentru a construi imaginea explicit, putem folosi docker build. Comanda build utilizează fișierul ./docker/Dockerfile care definește ce sistem de operare va fi utilizat de container, ce aplicații vor fi pre-instalate și ce useri vor exista pe containerele care rulează acea imagine.

docker build -t retele:latest -f ./docker/Dockerfile .

# tag-ul imaginii (nume:versiune)
-t retele:latest

# calea catre fisierul dockerfile
-f ./docker/Dockerfile 

# contextul in care se executa docker build
.

Contextul este directorul în care se execută construcția imaginii. În fișierul Dockerfile se poate specifica copierea explicită a unor fișiere / date din directorul local, iar contextul reprezintă directorul în funcție de care se pot specifica căi relative în dockerfile.

Orchestrate containers with docker compose

Comanda docker compose up -d, va citi fișierul docker compose.yml din path-ul de unde rulăm comanda și va lansa containere după cum sunt definite în fișier în secțiunea services: rt1, rt2, etc.. Containere care sunt configurate să ruleze o imagine dată (în cazul nostru baseimage, imaginea construită la pasul anterior) sunt conectate la o rețea (în cazul nostru rețeaua dmz) sau și au definite un mount point local. Comanda docker compose pe linux nu se instalează default cu docker, ci trebuie să o instalăm separat. În cazul nostru, comanda se găsește chiar în directorul computer-networks, în acest repository.

Aplicația docker compose va descărca din registry imaginea corespunzătoare pentru acest capitol. Imaginea se numește retele și are tag-ul 2021, cu versiunea pentru acest an.

cd capitolul0
# start services defined in docker compose.yml
docker compose up -d

Basic docker compose commands

# stop services
docker compose down

# list images
docker compose ps

# attach using docker compose
docker compose exec rt1 bash

# attach as root to a container
docker compose exec --user root rt1 bash

Basic docker commands


# list your images
docker image ls
# sau
docker images


# see the containers running
docker ps

# kill a container
docker kill $CONTAINER_ID

# see the containers not running
docker ps --filter "status=exited"

# remove the container
docker rm $CONTAINER_ID

# list available networks
docker network ls

# inspect network
docker network inspect $NETWORK_ID

# inspect container
docker inspect $CONTAINER_ID

# see container ip
docker inspect -f '' $CONTAINER_ID

# attach to a container
docker exec -it $CONTAINER_ID bash

Remove images and containers

Ștergeți toate containerele create și resetați modificările efectuate în branch-ul local de git.

# pentru a opri toate containerele
docker stop $(docker ps -a -q)
# pentru a șterge toate containerele
docker rm $(docker ps -a -q)
# pentru a șterge toate rețelele care nu au containere alocate
docker network prune
# pentru a șterge containere si imagini
docker system prune

# pentru a șterge toate imaginile de docker (!!!rulați doar dacă știți ce face)
docker rmi $(docker images -a -q)

Docker References

NIC - Network Interface Controller (Placa de rețea)

# executați un shell în containerul rt1
docker compose exec rt1 bash

# listați configurațiile de rețea
ifconfig

### eth0 - Ethernet device to communicate with the outside  ###
# eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
#        inet 172.27.0.3  netmask 255.255.0.0  broadcast 0.0.0.0
#        ether 02:42:ac:1b:00:03  txqueuelen 0  (Ethernet)
# lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
#        inet 127.0.0.1  netmask 255.0.0.0
#        loop  txqueuelen 1000  (Local Loopback)

ifconfig

Comanda ifconfig este UNIX-speciffic și ne indică două device-uri care rulează pe containerul rt1:

ip

Comenzile pe bază de iproute2 au fost introduse în linux pentru a înlocui ifconfig.

Prin comanda ip putem vedea:

Adresele

ip addr 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
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.4/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever

Rutele

ip route show
default via 172.18.0.1 dev eth0 
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.4
ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0

Vecini

ping google.com
ip neigh show
172.18.0.1 dev eth0 lladdr 02:42:d7:56:fa:c8 REACHABLE

Exercițiul 1

Modificați docker compose.yml pentru a adaugă încă o rețea și încă 3 containere atașate la rețeaua respectivă. Modificați definiția container-ului rt1 pentru a face parte din ambele rețele. Exemplu de rețele:

networks:
    dmz:
        ipam:
            driver: default
            config:
                - subnet: 172.111.111.0/16 
                  gateway: 172.111.111.1
    net:
        ipam:
            driver: default
            config:
                - subnet: 198.13.13.0/16
                  gateway: 198.13.13.1

Ce se intamplă dacă constrângeți subnet-ul definit pentru a nu putea permite mai mult de 4 ip-uri într-o rețea.

Este corect să definim subrețele cu IP-uri: 172.111.111.0/16 și 198.13.13.0/16?

Ping

Este un tool de networking care se foloseste de ICMP pentru a verifica dacă un host este conectat la o rețea prin IP.

# ping localhost and loopback
ping localhost

ping 127.0.0.1

ping 127.0.2.12

# ping neighbour from network dmz
ping 172.111.0.3

# ping broadcast from network dmz
ping -b 172.111.255.255

# ping neighbour from network net
ping 198.13.13.1
  1. Când rulați ping, utilizați opțiunea -R pentru a vedea și calea pe care o efectuează pachetul.

  2. Ce reprezintă adresa 127.0.2.12? De ce funcționează ping către aceasta?

  3. Într-un terminal nou, rulați comanda docker network inspect computernetworks_dmz pentru a vedea ce adrese au celelalte containere. Încercați să trimiteți un ping către adresele IP ale celorlalte containere.

  4. Folosiți docker stop pentru a opri un container, cum arată rezultatul comenzii ping către adresa IP a containerului care tocmai a fost oprit?

  5. Rețelele dmz și net au în comun containerul rt1. Un container din rețeaua dmz primește răspunsuri la ping de la containere din rețeaua net?

  6. Folosiți opțiunea -c 10 pentru a trimite un număr fix de pachete.

  7. Folosiți opțiunea -s 1000 pentru a schimba dimensiunea pachetului ICMP

  8. Reporniți toate containerele. Cum arată rezultatele pentru ping -M do -s 30000 172.111.0.4? Care este rezultatul dacă selectați dimensiunea 1500?

  9. Opțiunea -f este folosită pentru a face un flood de ping-uri. Rulați un shell cu user root, apoi ping -f 172.111.0.4. Separat, într-un alt terminal rulați docker stats. Ce observați?

De multe ori răspunsurile la ping sunt dezactivate pe servere. Pentru a dezactiva răspunsul la ping rulați userul root: echo "1" > /proc/sys/net/ipv4/icmp_echo_ignore_all. Într-un container de docker nu aveți dreptul să modificați acel fișier și veți primi o eroare. Putem, în schimb, modifica structura containerului din docker compose.yml și-i putem adăuga pe lângă image, networks, volumes, tty, o opțiune de sysctls:

    rt1:
        ..........
        sysctls:
          - net.ipv4.icmp_echo_ignore_all=1

tcpdump

Este un tool care vă permite monitorizarea traficului de pe containerul/mașina pe care vă aflați. Vom folosi tcpdump pentru a monitoriza traficul generat de comanda ping. Pentru a rula tcpdump, trebuie să ne atașam unui container cu user root apoi putem rula:

tcpdump -Sntv

Dacă în urma rulării acestei comenzi nu apare nimic, înseamnă că în momentul acesta interfața dată pe containerul respectiv nu execută operații pe rețea. Pentru a vedea ce interfețe (device-uri) putem folosi pentru a capta pachete, putem rula:

tcpdump -D

Exerciții

  1. În containerul rt1 rulați tcpdump -n. În containerul rt2 rulați ping -c 1 rt1. Ce trasături observați la pachetul ICMP? Ce observați dacă rulați ping -c 1 -s 2000 rt1?

  2. Rulați aceleasi ping-uri dar acum monitorizați pachetele cu tcpdump -nvtS. Ce detalii observați în plus? Dar dacă adăugați opțiunea tcpdump -nvtSXX?

  3. Pentru a vedea și header-ul de ethernet, adăugați opțiunea -e la tcpdump.

  4. În rt1 monitorizați traficul cu tcpdump -nevtSXX Într-un alt terminal, rulați un shell tot pe containerul rt1 apoi dați ping -c 1 yahoo.com. Ce adrese MAC și IP sunt folosite pentru a trimite requestul ICMP? Câte pachete sunt captate în total?

  5. În loc de ultimul ping, generați trafic la nivelul aplicație folosind wget https://github.com/senisioi/computer-networks/. Comparați continutul pachetului cu un request HTTP: wget http://moodle.fmi.unibuc.ro. Observați diferența dintre HTTP si HTTPS la nivel de pachete.

  6. Puteți deduce din output-ul lui tcpdump care este adresa IP a site-ului github.com sau moodle.fmi.unibuc.ro? Ce reprezintă adresa MAC din cadrul acelor request-uri?

  7. Captând pachete, ați putut observa requesturi la o adresă de tipul 239.255.255.255? Mai multe detalii aici.

TCP/IP stack
                     ----------------------------
                     |    Application (HTTP+S)  |
                     |                          |
                     |...  \ | /  ..  \ | /  ...|
                     |     -----      -----     |
                     |     |TCP|      |UDP|     |
                     |     -----      -----     |
                     |         \      /         |
                     |         --------         |
                     |         |  IP  |         |
                     |  -----  -*------         |
                     |  |ARP|   |               |
                     |  -----   |               |
                     |      \   |               |
                     |      ------              |
                     |      |ENET|              |
                     |      ---@--              |
                     ----------|-----------------
                               |
         ----------------------o---------
             Ethernet Cable

                  Basic TCP/IP Network Node

Diferite opțiuni pentru tcpdump:

# -c pentru a capta un numar fix de pachete
tcpdump -c 20

# -w pentru a salva pachetele într-un fișier și -r pentru a citi fișierul
tcpdump -w pachete.pcap
tcpdump -r pachete.pcap

# pentru a afișa doar pachetele care vin sau pleacă cu adresa google.com
tcpdump host google.com

# folosiți -XX pentru a afișa și conținutul în HEX și ASCII
tcpdump -XX

# pentru un timestamp normal
tcpdump -t

# pentru a capta pachete circula verbose
tcpdump -vvv

# indică interfața pe care o folosim pentru a capta pachete, în cazul acesta eth0 
tcpdump -i

# afișarea headerului de ethernet
tcpdump -e

# afișarea valorilor numerice ale ip-urilo în loc de valorile date de nameserver
tcpdump -n

# numărul de secvență al pachetului 
tcpdump -S

Wireshark

Wireshark este o aplicație similară cu tcpdump care în plus are interfață grafică și mai multe funcționalități de analiză a pachetelor captate. Puteți să o instalați pe calculatoarele voastre personale și să o utilizați la laboratoare. În multe situații, nu avem acces la un server prin interfață grafică și atunci tcpdump ar fi de preferat.

Tutorial

Puteți urmări un tutorial cu wireshark din laboratoarele cărții A Top-down Approach.