Linux operatsioonisüsteemis töötab võrguühendus
Sissejuhatus
TODO
Tööpõhimõte
Teenuse pakkumise poolelt vaadates toimub seoses võrguühenduse teenindamisega selline sündmuste käik
SOCKET() -> BIND() -> LISTEN() -> ACCEPT() -> READ/WRITE() -> CLOSE()
kus erinevad tarkvaralised lahendused saavad jagada seda järgnevust erinevalt lõikudeks, nt
- üks netcat protsess tegeleb kõigi teemadega ise
- postgres isand protsess tegeleb kuni accept juurde, edasi moodustatakse uus protsess ning see töötab lõpuks 'active connected socket' olukorraga
- sshd/init (ubuntu 24.04) - esialgu kuulab võrgus init protsess port 22/tcp, st on listen olekus, kui saabub esimene pöördumine tuuakse juurde sshd protsess, listen antakse üle (sd_listen_fds tehnika abil)
Väited
- toodud nähtusi saab omavahel kombineerida (nt nginx-reuseport režiimis kasutab lisaks 'sshd/init' lähenemise elemente)
- kui toimub võrguühendus, siis serveri poolelt osaleb kaks erinevat socketit: 1. listening socket, 2. active connected socket
- socket esineb kerneli mälu struktuuri üksusena (sinna on üles kirjutatud metaandmed, nt port number, aga ka andmetega seotud queue'd)
Listening-socket ja active-connected-socket objektidega toimub on sarnane protsessiga toimuvale forkimise olukorras
- pidavalt on süsteemis olemas nö parent nähtus: vastavalt 1. listen socket, 2. parent protsess
- teatud olukorras toimub uue objekti tekkimine: 1. saabuva võrguühenduse teenindamisega moodustatakse listen socket alusel koopia; 2. xxx
TODO
Väited
- haproxy - kasutatakse vaikimisi
- nginx - vaikimisi ei kasutata, aga soovitatakse kasutada jõudluse suurendamise eesmärgil
- zabbix-agent2 - ei ole võimalik kasutada
Võrguühendus vaatlemine
Serveri poolel on lähtepunktiks ainult nn 'listening socket', peale netcat protsessi käivitamist ütleb protsess kernelile kolm syscall'i
- socket()
- bind()
- listen()
tulemusena paistab (reaalset grep väljundis accept4 ei ole paista)
terminal 1 - server-01# netcat -l 6001 2>&1 | egrep "socket|setsockopt|bind|listen|accept"
..
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(6001), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1) = 0
accept4(...
...
terminal 2 - server-02# netstat -anpt | grep netcat
tcp 0 0 0.0.0.0:6001 0.0.0.0:* LISTEN 21473/netcat
root@zabbix-pub-01:~# lsof -n -P -p 21473
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
netcat 21473 root cwd DIR 252,2 4096 8193 /root
netcat 21473 root rtd DIR 252,2 4096 2 /
netcat 21473 root txt REG 252,2 39560 841 /usr/bin/nc.openbsd
netcat 21473 root mem REG 252,2 2125328 7621 /usr/lib/x86_64-linux-gnu/libc.so.6
netcat 21473 root mem REG 252,2 55536 15411 /usr/lib/x86_64-linux-gnu/libmd.so.0.1.0
netcat 21473 root mem REG 252,2 68104 8515 /usr/lib/x86_64-linux-gnu/libresolv.so.2
netcat 21473 root mem REG 252,2 80888 15290 /usr/lib/x86_64-linux-gnu/libbsd.so.0.12.1
netcat 21473 root mem REG 252,2 236616 7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
netcat 21473 root 0u CHR 136,7 0t0 10 /dev/pts/7
netcat 21473 root 1u CHR 136,7 0t0 10 /dev/pts/7
netcat 21473 root 2u CHR 136,7 0t0 10 /dev/pts/7
netcat 21473 root 3u IPv4 1514112 0t0 TCP *:6001 (LISTEN)
kus
- lsof käsk esitab userspace protsessiga seotud avatud failide nimekirja (muu hulgas kuulub nimekirja võrgusoketiga seotud võrguühenduse inode 1514112
- 1514112 on operatsioonisüsteemis unikaalne globaalne võrgusoketi inode number (ino)
- 3u on protsessi kontekstis unikaalne file descriptor väärus (3r või 4w oleksid põhimõtteliselt read ja write, u on universal st mõlemad)
- tegu on listening soketiga, talle vastab mälueraldis jms
- soket asub ise kernel space'is, aga userspace'i protsessil on temaga kontakt fd abil (selle suhtes teeb protess nt read() ja write() syscall'isid
Peale kliendi ühenduse alustamist st kui ühendus toimub, paistab kliendi arvutis
terminal 1 - klient-01# netstat 192.168.10.193 6001 terminal 2 - klient-01# netstat -anpt | grep netcat tcp 0 0 192.168.10.196:58384 192.168.10.193:6001 ESTABLISHED 345883/netcat # lsof -n -P -p 345883 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME netcat 345883 root cwd DIR 253,2 4096 14 /root netcat 345883 root rtd DIR 253,2 4096 2 / netcat 345883 root txt REG 253,2 35032 419036 /usr/bin/nc.traditional netcat 345883 root mem REG 253,2 1999312 403434 /usr/lib/x86_64-linux-gnu/libc.so.6 netcat 345883 root mem REG 253,2 225600 403431 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 netcat 345883 root 0u CHR 136,2 0t0 5 /dev/pts/2 netcat 345883 root 1u CHR 136,2 0t0 5 /dev/pts/2 netcat 345883 root 2u CHR 136,2 0t0 5 /dev/pts/2 netcat 345883 root 3u IPv4 29390854 0t0 TCP 192.168.10.196:58384->192.168.10.193:6001 (ESTABLISHED)
kus
- üks active connected socket oma mälueraldisega kernelis
- 29390854 on operatsioonisüsteemi jaoks globaalselt unikaalne inode väärtus (ino)
ja serveris kaks võrgusoketit
root@zabbix-pub-01:~# netstat -anpt | grep netcat tcp 0 0 0.0.0.0:6001 0.0.0.0:* LISTEN 21473/netcat tcp 0 0 192.168.10.193:6001 192.168.10.196:58384 ESTABLISHED 21473/netcat root@zabbix-pub-01:~# lsof -n -P -p 21473 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME netcat 21473 root cwd DIR 252,2 4096 8193 /root netcat 21473 root rtd DIR 252,2 4096 2 / netcat 21473 root txt REG 252,2 39560 841 /usr/bin/nc.openbsd netcat 21473 root mem REG 252,2 2125328 7621 /usr/lib/x86_64-linux-gnu/libc.so.6 netcat 21473 root mem REG 252,2 55536 15411 /usr/lib/x86_64-linux-gnu/libmd.so.0.1.0 netcat 21473 root mem REG 252,2 68104 8515 /usr/lib/x86_64-linux-gnu/libresolv.so.2 netcat 21473 root mem REG 252,2 80888 15290 /usr/lib/x86_64-linux-gnu/libbsd.so.0.12.1 netcat 21473 root mem REG 252,2 236616 7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 netcat 21473 root 0u CHR 136,7 0t0 10 /dev/pts/7 netcat 21473 root 1u CHR 136,7 0t0 10 /dev/pts/7 netcat 21473 root 2u CHR 136,7 0t0 10 /dev/pts/7 netcat 21473 root 3u IPv4 1514112 0t0 TCP *:6001 (LISTEN) netcat 21473 root 4u IPv4 1514113 0t0 TCP 192.168.10.193:6001->192.168.10.196:58384 (ESTABLISHED)
- listening socket oma mälueraldisega jms
- active connected socket oma mälueraldisega
- võrgusoketi inode numbrid on soketitel erinevad - 1514112 ja 1514113
Reuseport
Osutub, et tcp/ip network stack Linux, Windows, BSD jt implementatsioonid võimaldavad teatud tingimustel mitmel protsessil korraga sama porti kasutada. Seda võimalust kontrollib 'so_reuseport' socket option ('so_').
Python skript
# cat socket-01-server.py
import socket
import os
import sys
PORT = 8083
# Fetch the Process ID so we can easily tell which instance answers the client
PID = os.getpid()
# 1. Create a standard TCP IPv4 Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. THE MAGIC OPTIONS: Enable Reuse Address and Reuse Port
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
# 3. Bind and Listen (Standard blocking mode)
server_socket.bind(('0.0.0.0', PORT))
server_socket.listen(10)
print(f"[Worker PID: {PID}] Multi-core listening gate opened on port {PORT}...")
try:
while True:
# This call BLOCKS completely. The script sleeps right here in the kernel
# until the Linux 4-tuple hash decides to route a client to THIS specific socket instance.
client_socket, client_address = server_socket.accept()
# Read the raw incoming HTTP request payload (up to 4KB)
request = client_socket.recv(4096)
if request:
# Construct a basic HTTP response containing the answering process ID
body = f"Hello from Worker Process ID: {PID}\n"
response = (
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
f"Content-Length: {len(body)}\r\n"
"Connection: close\r\n"
"\r\n"
f"{body}"
).encode('utf-8')
# Fire the response back to the client
client_socket.sendall(response)
# Close the socket immediately to return the client line
client_socket.close()
except KeyboardInterrupt:
print(f"\n[Worker {PID}] Shutting down down gracefully.")
finally:
server_socket.close()
Nginx
TODO
haproxy
TODO
Kasulikud lisamaterjalid
- TODO