Important: continuăm cu aceeași configurație ca în capitolul3 și urmărim secțiunea introductivă de acolo.
cd computer-networks
# ștergem toate containerele create default
docker-compose down
# ștergem rețelele create anterior ca să nu se suprapună cu noile subnets
docker network prune
# lucrăm cu docker-compose.yml din capitolul3
cd capitolul3
docker-compose up -d
# sau din directorul computer-networks:
# docker-compose -f capitolul3/docker-compose.yml up -d
0 1 2 3 4 Offs.
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|Version| IHL | DSCP |ECN| Total Length | 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Identification |Flags| Fragment Offset | 2
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Time to Live | Protocol | Header Checksum | 3
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Source Address | 4
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Destination Address | 5
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Options (if IHL > 5) |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Application + TCP data |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
Prima specificație a protocolului IP a fost în RFC791 iar câmpurile sunt explicate foarte bine în aceste note de curs.
Ca să captați cu Wireshark IP datagrams care conțin opțiuni, puteți folosi filtrul care verifică ultimii 4 biți ai primului octet: ip[0] & 0xf != 5
. Veți putea observa pachete cu protocolul IGMP care are setată opțiunea Router Alert
Folosim datele ca octeti din exemplul cu UDP Raw Socket de mai sus:
import socket
import struct
data = b'E\x00\x00!\xc2\xd2@\x00@\x11\xeb\xe1\xc6\n\x00\x01\xc6\n\x00\x02\x08\xae\t\x1a\x00\r\x8c6salut'
# extragem headerul de baza de IP:
ip_header = struct.unpack('!BBHHHBBH4s4s', data[:20])
ip_ihl_ver, ip_dscp_ecn, ip_tot_len, ip_id, ip_frag, ip_ttl, ip_proto, ip_check, ip_saddr, ip_daddr = ip_header
print("Versiune IP: ", ip_ihl_ver >> 4)
print("Internet Header Length: ", ip_ihl_ver & 0b1111) # & cu 1111 pentru a extrage ultimii 4 biti
print("DSCP: ", ip_dscp_ecn >> 6)
print("ECN: ", ip_dscp_ecn & 0b11) # & cu 11 pt ultimii 2 biti
print("Total Length: ", ip_tot_len)
print("ID: ", ip_id)
print("Flags: ", bin(ip_frag >> 13))
print("Fragment Offset: ", ip_frag & 0b111) # & cu 111
print("Time to Live: ", ip_ttl)
print("Protocol nivel superior: ", ip_proto)
print("Checksum: ", ip_check)
print("Adresa sursa: ", socket.inet_ntoa(ip_saddr))
print("Adresa destinatie: ", socket.inet_ntoa(ip_daddr))
if ip_ihl_ver & (16 - 1) == 5:
print ("header-ul de IP nu are optiuni")
Versiune IP: 4
Internet Header Length: 5
DSCP: 0
ECN: 0
Total Length: 33
ID: 49874
Flags: 0b10
Fragment Offset: 0
Time to Live: 64
Protocol nivel superior: 17
Checksum: 60385
Adresa sursa: 198.10.0.1
Adresa destinatie: 198.10.0.2
ip = IP()
ip.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
# observăm că DSCP și ECN nu sunt înca implementate în scapy.
# daca vrem să le folosim, va trebui să setăm tos cu o valoare
# pe 8 biți care să reprezinte DSCP și ECN folosind: int('DSCP_BINARY_STR' + 'ECN_BINARY_STR', 2)
# pentru a seta DSCP cu cod AF32 pentru video streaming și ECN cu notificare de congestie: ip.tos = int('011100' + '11', 2)
Pe scurt, aici sunt link-urile cu informații esențiale despre rutare:
0 1 2 3 4 Offs.
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|Version| Traffic Class | Flow Label |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Payload Length | Next Header | Hop Limit |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| |
- -
| |
- Source Address -
| |
- -
| |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| |
- -
| |
- Destination Address -
| |
- -
| |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
Prima specificație a protocolului IPv6 a fost în 1998 rfc2460 iar detaliile despre semnificația câmpurilor se găsesc în aceste note de curs.
fe80:cd00:0000:0000:1257:0000:0000:729c
::1/128
::
este o variantă prin care se prescurtează secventele continue cele mai din stânga de 0
, adresa de mai sus este prescurtată: fe80:cd00::1257:0:0:729c
import socket
import sys
# try to detect whether IPv6 is supported at the present system and
# fetch the IPv6 address of localhost.
if not socket.has_ipv6:
print("Nu putem folosi IPv6")
sys.exit(1)
# "::0" este echivalent cu 0.0.0.0
infos = socket.getaddrinfo("::0", 8080, socket.AF_INET6, 0, socket.IPPROTO_TCP, socket.AI_CANONNAME)
# [(<AddressFamily.AF_INET6: 10>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('::', 8080, 0, 0))]
# info contine o lista de parametri, pentru fiecare interfata, cu care se poate instantia un socket
print (len(infos))
1
info = infos[0]
adress_family = info[0].value # AF_INET
socket_type = info[1].value # SOCK_STREAM
protocol = info[2].value # IPPTROTO_TCP == 6
cannonical_name = info[3] # tot ::0 adresa de echivalenta cu 0.0.0.0
adresa_pt_bind = info[4] # tuplu ('::', 8080, 0, 0):
'''
Metodele de setare a adreselor (bind, connect, sendto)
pentru socketul IPv6 sunt un tuplu cu urmatoarele valori:
- adresa_IPv6 ::0
- port 8080
- flow_label ca in header 0
- scope-id - id pt NIC 0
mai multe detalii: https://stackoverflow.com/a/11930859
'''
# instantiem socket TCP cu AF_INET6
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
# executam bind pe tuplu ('::', 8080, 0, 0)
s.bind(adresa_pt_bind)
# restul e la fel ca la IPv4
s.listen(1)
conn, addr = s.accept()
print(conn.recv(1400))
conn.send(b'am primit mesajul')
conn.close()
s.close()
import socket
import sys
# try to detect whether IPv6 is supported at the present system and
# fetch the IPv6 address of localhost.
if not socket.has_ipv6:
print("Nu putem folosi IPv6")
sys.exit(1)
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
adresa = ('::', 8080, 0, 0)
s.connect(adresa)
# restul e la fel ca la IPv4
s.send(b'Salut prin IPv6')
print (s.recv(1400))
s.close()
ip = IPv6()
ip.show()
###[ IPv6 ]###
version= 6
tc= 0
fl= 0
plen= None
nh= No Next Header
hlim= 64
src= ::1
dst= ::1
ip.dst = '::1' # localhost
# trimitem la un server UDP care asteapta pe (::0, 8081, 0, 0)
udp = UDP(sport=1234, dport=8081)
send(ip / udp / b'salut prin ipv6')
Am discutat despre ICMP și ping pentru a verifica dacă două device-uri pot comunica unul cu altul. Principala funcție a protocolului ICMP este de a raprota erori iar mesajele de control pot varia de la faptul că un host, port sau protocol este inaccesibil până la notificarea că TTL a expirat în tranzit.
ICMP().show()
###[ ICMP ]###
type= echo-request
code= 0
chksum= None
id= 0x0
seq= 0x0
# facem un pachet echo-request, ping
icmp = ICMP(type = 'echo-request')
ip = IP(dst = "137.254.16.101")
pachet = ip / icmp
# folosim sr1 pentru send și un reply
rec = sr1(pachet)
rec.show()
###[ IP ]###
version= 4
ihl= 5
tos= 0x0
len= 28
id= 48253
flags= DF
frag= 0
ttl= 242
proto= icmp
chksum= 0x23e7
src= 137.254.16.101
dst= 1.15.3.1
\options\
###[ ICMP ]###
type= echo-reply
code= 0
chksum= 0x0
id= 0x0
seq= 0x0