PROTOCOLUL SCTP – STUDIU ŞI IMPLEMENTARE






PROTOCOLUL SCTP – STUDIU ŞI IMPLEMENTARE










Introducere

Scopul lucrării 3


Capitolul 1 – Prezentare teoretică

1.1 Prezentarea protocolului UDP 4

1.2 Prezentarea protocolului TCP 5

1.3 Prezentarea protocolului SCTP 8 1.3.1 Limitările TCP şi UDP 9

1.3.2 Arhitectura SCTP 10

1.3.3 Caracteristicile SCTP 18

1.3.4 Tratarea erorilor în SCTP 26

1.3.5 Securitatea în SCTP 27

1.4 Comparaţie teoretică între UDP, TCP şi SCTP 28

1.5 Prezentarea protocolului HTTP 29

1.5.1 Modul de funcţionare 30

1.5.2 Terminolgie 30

1.5.3 Mesaje HTTP 31

1.5.4 De ce HTTP peste SCTP? 36

1.6 Prezentare generală RRDtool 38


Capitolul 2 – Explicarea aplicaţiei

2.1 Scopul aplicaţiei 40

2.2 Conţinutul aplicaţiei 40

Capitolul 3 – Explicarea testelor

3.1 Rularea testelor

3.1.1 Mediul de testare 43

3.1.2 Cazurile testate 43

3.1.3 Rezultate 44

3.2 Analiza rezultatelor 50


Capitolul 4 – Concluzii finale 51


Anexe

Anexa A – Tabel abrevieri 52

Anexa B – Programe si scrip-uri folosite 53

Bibliografie 75




















Introducere


Scopul lucrării



Această lucrare descrie performanţele protocolului HTTP (Hypertext Transfer Protocol) utilizat peste TCP (Transmission Control Protocol) în comparaţie cu performanţele acestui protocol utilizat peste SCTP (Stream Control Transmission Protocol). SCTP este un protocol de transport modern, care acoperă, în primul rând, o parte din neajunsurile protocolului TCP, dar şi ale protocolului UDP (User Datagram Protocol).


În primul capitol vor fi prezentate cele trei protocoale de transport şi va fi descris motivul utilizării protocolului SCTP ca alternativă pentru TCP în servirea web.


Capitolele al doilea şi al treilea oferă o descriere a programului folosit şi a testelor efectuate. De asemenea, sunt prezentate si verificate doua din cracteristicile protocolului SCTP: inchiderea asocierii in 3 pasi, si mecanismul de tratare a congestiilor.


Capitolul final prezinta concluziile personale referitoare la statisticile obţinute.




Capitolul 1


1.1 Prezentarea protocolului UDP



Protocolul UDP este cel mai simplu dintre protocoalele de transport. Întrebarea unora este: care ar fi avantajul folosirii protocolui UDP, când există deja TCP? Protocolul TCP, deşi prezintă unele avantaje fiind mai complex, nu este cel mai potrivit pentru anumite aplicaţii, cum ar fi cele în timp real. Protocolul TCP retransmite pachetele neconfirmate, ceea ce ar însemna mult timp pierdut, şi totodată utilizarea inadecvată a resurselor de bandă, în condiţiile în care retransmiterea pachetelor învechite este reduntantă.

Totodata, mecanismul de control al fluxului şi de utilizare a numerelor de secvenţă pentru reordonarea pachetelor sunt lipsite de utilitate pentru transmisiunile în timp real.

De asemenea, UDP este un protocol fără conexiune. Entitatea emitentă începe transmisiune pachetelor adresate fără a se asigura de disponibilitatea receptorului, şi fără a avea loc un schimb de mesaje anterior, pentru a se stabili o conexiune.

Structura antetului UDP este foarte simplă, dupa cum se poate vedea în figura de mai jos:


Port Sursă (16 biti)

Port Destinaţie (16 biti)

Lungime(16 biti)

Suma de control (16 biti)


Figura 1.1


Portul sursă

Indică portul procesului emitent. Este portul către care trebuie trimise răspunsurile.

Portul destinaţie

Precizează portul procesului destinaţie de pe hostul destinaţie.

Lungime

Este lungimea în octeţi a datagramei utilizatorulu, şi inclusiv a antetului.

Suma de control

Este un câmp opţional de 16 biţi.  Reprezintă complementul faţă de unu al sumei în complement faţă de unu a antetului pseudo-IP , antetului UDP şi datelor UDP. Antetul pseudo-IP conţine adresele IP sursă şi destinaţie, protocolul şi lungimea UDP.



Adresa IP Sursă

Adresa IP Destinaţie

Zero

Protocol

Lungime UDP


Figura 1.2




Antetul pseudo-IP extinde efectiv suma de control pentru a include datagrama IP originală (nefragmentată).

UDP este în esenţă o interfaţă de aplicaţie către IP. El nu adaugă nici un fel de siguranţă, control al transmisiei sau recuperare de erori la IP, acestea fiind asigurate la nivelul aplicaţie. El foloseşte portul drept multiplexor/demultiplexor pentru trimiterea şi primirea datagramelor. Numărul portului este cel care face diferenţa între aplicaţii, peste aceeaşi entitate de protocol.

Aceste considerente fac protocolul UDP rapid şi potrivit următoarelor aplicaţii:


1.2 Prezentarea protocolului TCP



Principalul scop al TCP este de a asigura un circuit logic sigur sau un serviciu de conexiune între două procese pereche. El nu se bazează pe siguranţa altor protocoale de nivel inferior (cum este IP), aşa că TCP trebuie să garanteze el însuşi siguranţa transmisiei.

TCP poate fi caracterizat prin următoarele facilităţi pe care le asigură pentru aplicaţiile care îl utilizează:


Transfer de date în şir (Stream Data Transfer)

Din punctul de vedere al aplicaţiei, TCP transferă un şir (stream) continuu de octeţi prin reţea. Aplicaţia nu trebuie să-şi facă griji cu segmentarea datelor în blocuri de bază sau datagrame. O face TCP prin gruparea octeţilor în fragmente TCP, care sunt transferate nivelui reţea (IP) pentru a fi transmise la destinaţie. De asemenea, însuşi TCP decide cum să segmenteze datele şi poate trimite datele mai departe într-un mod convenabil.

Uneori, o aplicaţie are nevoie să fie asigurată că toate datele transmise către TCP au fost în realitate transmise la destinaţie. Pentru aceasta, o funcţie push este definită. Ea va forţa transmiterea tuturor segmentelor TCP rămase în memorie către destinaţie. Funcţia de închidere (close) normală forţeaza şi ea transmiterea datelor la destinaţie.

Siguranţă

TCP atribuie un număr de secvenţă fiecărui octet transmis şi aşteaptă o confirmare (acknowledgement - ACK) de la aplicaţia care recepţioneaza datele TCP. Dacă confirmarea nu vine într-un interval de timp prestabilit, datele sunt transmise din nou. Deoarece datele sunt transmise în blocuri (segmente TCP) numai numărul de secvenţă al primului octet este trimis calculatorului destinaţie. Aplicaţia TCP destinaţie foloseşte numerele de secvenţă pentru a le ordona atunci când sosesc neordonate şi să elimine segmentele duplicate.

Controlul transmisiei (Flow Control)

Aplicaţia TCP destinaţie, când transmite o confirmare (ACK) către entitatea emitentă, indică de asemenea numărul de octeţi pe care îi poate recepţiona pe langă ultimul segment TCP primit, fără să se suprîncarce sau să se confrunte cu depăşirea memoriilor tampon (internal 

buffers) ale sale. Acesta este trimis în ACK sub forma numărului cel mai mare de secvenţă pe care îl poate recepţiona fără probleme. Acest mecanism este cunoscut sub numele de fereastră.

Multiplexarea

Este realizată prin utilizarea porturilor, la fel ca şi la UDP.

Conexiunile logice

Siguranţa şi mecanismele de control ale transmisiei descrise mai înainte necesită ca TCP să iniţializeze şi menţină câteva informaţii referitoare la starea fiecărui flux de date (data stream). Această informaţie de stare ce include socketurile, numerele de secvenţă, dimensiunile ferestrelor se numeşte conexiune logică. Fiecare conexiune este unic identificată de către o pereche de socketuri utilizate de către procesele emitente şi receptoare.

Full Duplex

TCP asigură două fluxuri simultane de date în ambele sensuri.

Antetul TCP este mult mai complex decat cel UDP:



Portul Sursă

Port Destinaţie

Număr de secvenţă

Număr de secvenţă confirmat

Data

Offset

Rezervat

U

R

G

A

C

K

P

S

H

R

S

T

S

Y

N

F

I

N

Fereastră

Sumă de control

Pointer-ul de urgenţă

Opţiuni

Umplutură


Figura 1.3



Portul sursă

Numărul portului sursă pe 16 biţi folosit de receptor pentru a raspunde.

Portul destinaţie

Numărul portului destinaţie pe 16 biţi.

Numărul de secvenţă

Numărul de secvenţă al primului octet de date din acest segment. Dacă bitul de control SYN este setat, numărul de secvenţă este numărul de secvenţă iniţial (n) şi primul octet de date este n+1.

Numărul de confirmare

Dacă bitul de control ACK este setat, acest câmp conţine valoarea următorului număr de secvenţă pe care receptorul se aşteaptă să-l primească.

Data Offset

Numărul de cuvinte de 32 biţi din antetul TCP. El indică unde încep datele.

Reserved

Şase biţi rezervaţi pentru utilizare în viitor; trebuie să fie zero.

URG

Precizează că articolul pointer de urgenţă are semnificaţie în acest segment.



ACK

Precizează că articolul de "Număr de secvenţă confirmat" are semnificaţie în acest segment.

PSH

Funcţia push.

RST

Resetează conexiunea.

SYN

Sincronizează numerele de secvenţă.

FIN

Semnalează că nu mai sunt date de la emitent.

Fereastră

Folosit în segmentele ACK. El specifică numărul de octeţi de date începand cu acela indicat în "numărul de secvenţă confirmat" pe care receptorul (adică emitentul acestui segment) doreşte (şi poate) să-l accepte.


Suma de control

Complementul faţă de unu al sumei în complement faţă de unu al tuturor cuvintelor de 16 biţi din pseudo-antet, antetul TCP şi datele TCP. La calcularea sumei de control, câmpul "Sumă de control" este considerat zero.

Pseudo-antetul este acelaşi ca cel folosit de către UDP pentru calculul sumei sale de control. Este un antet pseudo-IP folosit doar pentru calculul sumei de control cu formatul similar, prezentat în figura 1.2.

Pointerul de urgenţă

Indică primul octet de date ce urmează datelor urgente. Are semnificaţie numai atunci când bitul de control URG este setat.

Opţiuni

La fel ca în cazul opţiunilor datagramei IP, opţiunile pot avea doua formate:

-Un singur octet care conţine numărul opţiunii, sau

-O opţiune de lungime variabilă cu următorul format:


Opţiune (16 biţi)

Lungime (16 biţi)

Date (lungime -2 octeţi)


Figura 1.4


Umplutura

Toţi octeţii sunt setaţi zero şi au rol de completare a antetului TCP la lungimea reală care este multiplu de 32 de biţi.


Stabilirea unei conexiuni TCP

Înainte ca orice dată să fie transmisă, trebuie stabilită o conexiune între cele două procese. Unul dintre procese (de regulă serverul) lansează un apel OPEN pasiv, celălalt un apel OPEN activ. Apelul OPEN pasiv aşteaptă până când un alt proces încearca să se conecteze la el printr-un apel OPEN activ.




Prin reţea sunt schimbate trei segmente:


proces 1


proces 2



Apelul OPEN pasiv,
aşteaptă cereri active

Apelul OPEN activ trimite SYN, secv = n





Recepţionează SYN
trimite SYN, secv = m, ACK n+1

Receptioneaza SYN + ACK. Trimite ACK m+1




Figura 1.5


Conexiunea este acum stabilita şi două fluxuri (streamuri) de date (câte unul pentru fiecare sens) au fost iniţializate (numerele de secvenţă).

Întregul proces este cunoscut ca un "three-way handshake". 

De reţinut că segmentele TCP schimbate includ numere de secvenţă iniţiale de la ambele procese pentru a fi folosite la transferurile de date ulterioare. Închiderea conexiunii este facută implicit prin trimiterea unui segment TCP cu bitul FIN setat (cu semnificaţia: nu mai sunt date). Deoarece conexiunea este full-duplex (adică există două fluxuri de date, câte unul pentru fiecare sens), segmentul FIN închide transferul numai într-o direcţie. Celălat proces va trimite acum restul de date pe care-l mai are de transmis şi termină şi el cu un segment în care bitul  FIN este setat. Conexiunea este ştearsă (informaţiile despre stare ale ambelor părti) odată ce fluxul de date este închis în ambele direcţii.


Protocolul TCP este folosit în urmatoarele aplicaţii:




1.3 Prezentarea protocolului SCTP


Pentru a întelege nevoia dezvoltării unui nou protocol şi a utilităţii acestuia corespunzatoare dinamicii aplicaţiilor moderne, prima parte a acestui capitol va descrie limitările protocoalelor mai vechi: TCP şi UDP.



1.3.1 Limitările TCP şi UDP


Protocoalele TCP şi UDP sunt cele mai folosite protocoale de transport. Totuşi, seviciile de transfer de date oferite de acestea sunt inadecvate cererilor unei mari plaje de aplicaţii comerciale, cum ar fi cele media în timp real. Aceastea au nevoie de un protocol robust, care să combine flexibilitatea UDP-ului şi siguranţa TCP-ului, pentru transmiterea datelor între două capete. Aceste limitări au condus la dezvoltarea SCTP-ului.



Limitările TCP-ului


Limitările TCP-ului sunt urmatoarele:







Limitările UDP


Limitările UDP sunt urmatoarele:




1.3.2 Arhitectura SCTP


SCTP a fost proiectat să întâmpine limitările TCP. Utilizează un mecanism, numit “four-way handshake” pentru a preveni atacurile DoS. Arhitectura SCTP defineşte pachete cu un nou format, cu o serie de câmpuri adiţionale, cum ar fi cookie şi eticheta de verificare pentru a preveni flood-ul cu pachete SYN. Arhitectura include algoritmi îmbunataţiţi de control al congestiilor, potriviţi mediilor de transmisiune instabile.



SCTP în stiva IP


Figura următoare prezintă stiva IP şi localizarea protocolului SCTP:








Figura 1.6


Stiva IP prezintă mai multe nivele, fiecare oferind funcţionalitaţi diferite:

Nivelul fizic stabileşte condiţiile pentru trasmiterea datelor prin mediul fizic

Nivelul legătură de date transmite datele între entităţile de reţea, realizând şi detectarea şi corectarea erorilor apărute la nivelul fizic

Nivelul reţea ruteaza pachetele de date de la emiţător până la receptor, prin reţea. Cel mai utilizat protocol de reţea este IP.

Nivelul transport realizează transferul datelor între capete, folosindu-se de serviciile nivelului reţea. Acest nivel are doua protocoale primare: TCP şi UDP. TCP ofera suport pentru transmisiunile ordonate şi sigure, prin mecanisme de control al erorilor şi fluxului. UDP este un protocol simplu bazat pe mesaj, fără conexiune. SCTP este tot un protocol de transport, pe care dezvoltatorii de aplicaţii îl pot folosi pentru transmiterea între capete a datelor.

Nivelul socket oferă nivelului transport o interfaţă prin care acesta poate interacţiona cu nivelul aplicaţie. Nivelul socket conţine un set de API-uri (application programming interface) care facilitează interacţiuni între nivelul transport şi aplicaţie.



Nivelul aplicaţie furnizează aplicaţiilor o interfaţă de comunicare şi transfer al datelor prin reţea. Toate aplicaţiile de la acest nivel se folosesc de nivelul socket pentru a interacţiona mai departe cu nivelul transport.


Stabilirea unei conexiuni SCTP


Acest subcapitol va descrie modalitatea de conectare între doua capete în TCP şi SCTP. Ambele protocoale iniţiaza o conexiune cu ajutorul unui pachet care declanşează un mecanism handshake. TCP foloseşte un astfel de mecanism în 3 paşi, descris în capitolul anterior, iar SCTP foloseşte un mecanism asemanator, dar în 4 paşi, pentru a stabili o nouă conexiune.

Figura următoare descrie mecanismul handshake în 3 paşi:



Figura 1.7

Paşii sunt următorii:

Host-ul A trimite un pachet SYN catre host-ul B

După primirea pachetului SYN, host-ul B alocă resurse pentru a stabili conexiunea şi trimite un pachet SYN-ACK (synchronize-acknowledge) host-ului A

Host-ul A trimite un pachet ACK pentru a confirma primirea pachetului SYN-ACK

Conexiunea a fost stabilită între cele doua host-uri, iar host-ul A poate începe transmiterea datelor.





Următoarea figură descrie mecanismul handshake în 4 paşi pentru SCTP.



Figura 1.8

Următorii paşi descriu acest mecanism:

Host-ul A iniţiaza o asociere prin trimiterea pachetului INIT către host B

Host-ul B răspunde cu un pachet INIT-ACK care conţine următoarele câmpuri:

O etichetă de verificare

Un cookie

Un pachet SYN-ACK TCP nu conţine aceste câmpuri. Câmpul cookie conţine informaţiile de stare necesare, pe care serverul le foloseşte pentru a aloca resurse necesare asocierii. Câmpul cookie include o amprentă pentru autentificare şi un indicator de timp pentru a preveni atacurile cu alte cookie-uri mai vechi. Spre deosebire de TCP, host-ul B în cazul SCTP nu alocă resurse în acest punct al conexiunii. Eticheta de verificare generează o cheie care forţează host-ul A să verifice că pachetul SCTP aparţine asocierii curente.

Host-ul A trimite un pachet INIT-ECHO host-ului B. Dacă host-ul A are un IP alterat, el nu primeşte niciodată INIT-ACK. Aceasta previne host-ul A de la a trimite INIT-ECHO. Ca rezultat, schimbul de mesaje se termină înainte ca serverul să aloce resurse pentru conexiunea în cauză.



Host-ul B raspunde cu o secvenţă (chunck) COOKIE-ACK şi alocă resurse pentru noua asociere.

Conexiunea e acum stabilită între cele doua capete. Host-ul A poate începe acum transmiterea datelor.

În cazul SCTP-ului, transferul datelor poate fi întarziat datorită pasului suplimentar. Din aceasta cauză, mecanismul handshake în 4 paşi pare mai puţin eficient decât cel în 3 paşi. Pentru a depăşi acest neajuns, SCTP permite schimbul datelor în secvenţele COOKIE-ECHO şi COOKIE-ACK.

Pachetul SCTP

SCTP transmite datele sub formă de mesaje, şi fiecare mesaj conţine unul sau mai multe pachete.

Port sursă

Port destinaţie

Eticheta de verificare

Suma de verificare

Câmpul tipului secvenţei (chunk)

Fanionul secvenţei

Lungimea secvenţei

Secvenţa de date

. . .

. . .

. . .

Câmpul tipului secvenţei (chunk)

Fanionul secvenţei

Lungimea secvenţei

Secvenţa de date

Figura 1.9

Primele 4 câmpuri (port sursă, port destinaţie, eticheta de verificare şi suma de verificare) formează antetul SCTP, iar urmatoarele grupări de câte 4, secvenţa de control sau date.

În ceea ce priveşte antetul SCTP, porturile sursă şi destinaţie folosesc la multiplexarea diferitelor asocieri SCTP la aceeaşi adresă, eticheta de vericare de 32 de biţi previne inserarea unor mesaje false, alterate sau vechi în asocierea SCTP, iar suma de control de 32 de biţi realizeaza detectarea de erori. Aceasta poate fi fie CRC pe 16 biţi sau Adler pe 32 de biţi.

O secvenţă (chunk) poate fi ori de control, ori de date. Un chunk de control include diferite fanioane şi parametrii, în funcţie de tipul chunk-ului. Chunk-ul de tip DATA include fanioane pentru controlul segmentării şi reasamblării, şi parametrii pentru transmiterea numărului de secvenţă (TSN – transmission sequence number), identificatorului de şir (SID – stream identifier), numărul de secvenţă al şirului (SSN – stream sequence number), şi un 

identificator pentru încărcătura de protocol (payload protocol ID). Chunk-ul de date conţine chiar încărcătura în sine.

Fiecare chunk, indiferent de tipul său (date sau control) conţine următoarele informaţii:

Tipul chunk-ului:

Acest câmp identifică tipul de informaţie conţinut în câmpul secvenţei de date. Valoarea câmpului tipului de chunk are valori între 0 şi 254. Valorea 255 este rezervată pentru o utilizare viitoare, ca un câmp extins. SCTP constă în chunk-ul de date şi 12 chunk-uri de control.

Tabelul următor conţine lista şi definiţiile parametrilor diferitelor tipuri de chunk:


Chunk

Definiţie

Payload Data (DATA)

Folosit pentru transferul datelor

Initiation (INIT)

Iniţiază o asociere SCTP între doua capete.

Initiation Acknowledgement (INIT ACK)

Confirmă primirea unui chunk INIT. Receptarea unui chunk INIT-ACK stabileşte o asociere

Selective Acknowledgement (SACK)

Confirmă primirea unor chunk-uri de date şi de asemenea raportează lipsuri în cadrul datelor.

Cookie Echo (COOKIE ECHO)

Folosit în timpul procesului de iniţializare. Capătul iniţiator al unei asocieri trimite COOKIE ECHO capătului pereche.

Cookie Acknowledgement (COOKIE ACK)

Confirmă receptarea unui chunk COOKIE ECHO. COOKIE ACK trebuie obligatoriu trimis înaintea oricărui chunk de date sau chunk SACK trimis în asociere, însa poate fi grupat cu acestea, cu condiţia să le preceadă.

Heartbeat Request (HEARTBEAT)

Testează conectivitatea cu o destinaţie anume din asociere.

Heartbeat Acknowledgement (HEARTBEAT ACK)

Confirmă primirea unui chunk HEARTBEAT.

Abort Association (ABORT)

Informează capătul pereche de închiderea asocierii. Chunk-ul ABORT informează de asemenea receptorul de motivele închiderii asocierii.

Operation Error (ERROR)

Raportează erorile şi tipul acestora.

Shutdown Association (SHUTDOWN)

Generează o închidere a asocierii cu un capăt pereche.

Shutdown Acknowledgement 

(SHUTDOWN ACK)



Este o confirmare pentru iniţiatorul închiderii asocierii.



Shutdown Complete (SHUTDOWN COMPLETE)

Finiseaza procedura de închidere a asocierii.

Tabelul 1.1


Fanionul secvenţei:

Acest câmp conţine fanioanele, precum U(unordered bit), B(beginning fragment bit – bit care delimitează începutul unui fragment) şi E (ending fragment bit – bit care desemnează sfârşitul unui fragment). Utilizarea acestui bit depinde de tipul chunk-ului specificat în câmpul destinat tipului. Daca nu este specificat, SCTP seteaza acest camp la 0 cât realizeaza transmisiunea şi ignora fanionul la receptarea pachetului.


Lungimea secvenţei:

Acest câmp reprezintă lungimea câmpurilor: tip, fanion, lungime şi valoare a chunk-ului în octeţi.


Datele secvenţei:

Acest câmp conţine chiar informaţia care va fi transferată în cadrul chunk-ului. Modul de utilizare şi formatul acestui câmp depinde de tipul chunk-ului.


Numărul de chunk-uri dintr-un pachet SCTP este determinat de dimensiunea MTU (Maximum Transmission Unit) a căii de transmisiune. Mai multe chunk-uri pot fi grupate într-un pachet SCTP, mai puţin INIT, INIT ACK şi SHUTDOWN COMPLETE. Dimensiunea pachetului SCTP nu trebuie să o depăşească pe cea a MTU.

Formatul pachetului SCTP suportă gruparea mai multor chunk-uri de date şi control în acelaşi pachet, pentru îmbunătăţirea eficienţei transportului. O aplicaţie poate controla această grupare pentru a preveni acest procedeu în timpul transmisiei iniţiale. Deobicei gruparea apare în cazul retransmisiei chunk-urilor de date, pentru a reduce riscurile unei congestii. Dacă datele depăşesc capacitatea unui pachet, SCTP fragmenteaza datele în mai multe chunk-uri.


Controlarea congestiilor in SCTP

SCTP foloseşte o variaţiune de algoritmi pentru controlul congestiilor pentru a trata eficient defecţiunile din reţea sau supraaglomerarea neaşteptata a reţelei, şi pentru a asigura o revenire rapidă dintr-o congestie. SCTP şi TCP folosesc acelaşi set de algoritmi de control. Cei patru algoritmi sunt:

Slow start şi Congestion Control

Fast Retransmit şi Fat Recovery

Totuşi, în SCTP, aceşti algoritmi sunt adaptaţi cerinţelor şi caracteristicilor protocolului.

Algoritmii Slow Start şi Congestion Avoidance

Aceşti doi algoritmi sunt folosiţi pentru a controla cantitatea de informaţie neprelucrată care este transmisă. SCTP foloseste slow start la începutul transmisiunii, când conditiile de mediu sunt necunoscute, şi de asemenea în a recupera pierderile detectate de ceasul de retransmisie. SCTP testeaza întai mediul de transmisiune pentru a determina capacitatea disponibilă a mediului, pentru a evita congestiile. În cazul în care detectează un mediu congestionat, intră în funcţiune algoritmul de evitare, pentru a trata corespunzator congestia.

Aceşti algoritmi folosesc următorele variabile de control:

Fereastra de congestie (cwnd):

Specifică limitele cantităţii de date pe care emitorul le poate transmite prin reţea, înainte de a primi o confimare. Această variabila este menţinută pentru fiecare adresă destinaţie.


Fereastra de recepţie (rwnd):

Specifică limita receptorului în ceea ce priveşte datele primite. Valoarea minima dintre cwnd şi rwnd determină cantitatea de date transmise.


Pragul slow start (ssthresh):

Determină care dintre cei doi algorimi (slow start sau avoidance) trebuie utilizat pentru a controla transmisiunea.


Partial Bytes Acknowledged (partial_byte_acked):

Ajusteaza parametrul cwnd.


Într-o conexiune SCTP, emitorul foloseşte algoritmul slow start dacă valoarea cwnd este mai mică decat ssthresh. Dacă este mai mare, foloseşte algotitmul de evitare a congestiilor. Dacă cele două valori sunt egale, emitorul poate utiliza oricare din cei doi algoritmi. Spre deosebire de TCP, un emitor SCTP trebuie să memoreze variabilele cwnd, ssthresh şi partial_byte_acked pentru fiecare adresă destinaţie pereche. Totuşi, este suficient să reţina o singură valoare rwnd pentru întreaga asociere, indiferent dacă prechea conţine o singură adresă sau este o asociere multi-home.

Fast Retransmit şi Fast Recovery

Algoritmul de control fast retransmit este utilizat pentru a retransmite cât mai optim segmente lipsă într-o asociere SCTP. Când un receptor într-o asociere SCTP primeste un chunk de date neordonat, acesta trimite un pachet SACK cu un TSN neordonat emitorului. Algoritmul fast retransmit foloseşte patru pachete SACK pentru a indica pierderi de date şi retransmite date fără a aştepta ca ceasul de retransmitere să expire. Dupa ce se transmit datele care par să lipseasca, algoritmul de revenire rapidă controleaza transmisiunea noilor date până când orice segment pierdut este retransmis.



1.3.3 Caracteristicile SCTP

Multihoming

Multihomingul este abilitatea ca un singur capăt dintr-o asociere SCTP să conţină mai multe interfeţe cu adrese IP diferite. Într-o conexiune single-homed, un capăt conţine o singură interfaţa de reţea şi o singură adresa IP.

În figura de mai jos este prezentată o conexiune single-homed:



Figura 1.10

În figura 1.10, host-ul A conţine o singura interfaţa de reţea (network interface NI), şi la fel şi host-ul B. Doar aceste două interfeţe vor interacţiona.

Când are loc o căderea în reţea, captele sunt complet izolate de reţea. Multihomingul asigură mai multe şanse de menţinere a conexiunii în cazul unei căderi de reţea, comparativ cu TCP-ul. Suportul pentru multihoming în SCTP permite unei singure asocieri SCTP să creeze mai multe legături şi căi, pentru a obţine redundanţă. Aceasta permite unei asocieri SCTP o revenire mai rapidă de la o legătura la alta sau de la o cale la alta, cu un minimum de întreruperi în transmisiune.


Figura următoare prezintă o asociere multi-homed.


Figura 1.11



În această figură, host-ul A are mai multe interfeţe de reţea care pot interacţiona cu host-ul B, care la rândul sau are mai multe interfeţe.

SCTP alege o singură adresă ca fiind primară, şi o foloseşte ca destinaţie pentru toate transmisiunile normale de chunk-uri de date. Toate celălalate adrese sunt alternative. SCTP foloseşte aceste adrese pentru a retransmite date şi pentru a îmbunătăţi probabilitatea de a ajunge la capăt. Retransmiterea poate avea loc ca urmare a eşecurilor repetate de a transmite datele la adresa primară. Ca urmare, toate datele sunt transmise adreselor alternative pana cand un chunk HEARTBEAT stabileşte o legătură cu adresa primară.

În timpul iniţierii unei asocieri, capetele schimbă o listă de adrese IP, astfel încat fiecare capăt să poată primi mesaje de la oricare adresă asociată cu capatul pereche. Din motive de securitate, SCTP trimite mesaje de raspuns adresei sursă aflate în mesajul care cere raspuns.

Un capat poate primi mesaje care nu sunt ordonate sau cu perechi de adrese diferite, pentru că multi-homing suportă mai multe adrese IP. Pentru a preveni această problemă, SCTP include proceduri care tratează încercările simultane de iniţiere a unei singure asocieri.


Multistreaming


Multistreaming-ul face posibil ca datele să fie transmise în mai multe fluxuri, paralele, independente, astfel încat pierderile de date dintr-un flux să nu afecteze sau să opreasca livrarea datelor pe celălalte fluxuri. Fiecare flux dintr-o asociere SCTP foloseşte două seturi de numere de secvenţă: transmission sequence number (TSN) care adminstrează transmiterea mesajelor şi detectarea de mesaje pierdute, şi perechea SID/SSN care determină secvenţa datelor primite.

TCP transmite datele secvenţial în formă de octeţi într-un singur flux şi se asigură că toţi octeţii au fost livraţi într-o anume ordine. Ca urmare, un al doilea octet este trimis numai dupa ce primul octet a ajuns la destinaţie. Livrarea ordonată a datelor cauzează întarzieri când au loc pierderi de mesaje sau erori de secvenţă într-o reţea. O întarziere adiţionala are loc când TCP se opreşte din transmisie până când ordinea pachetelor este restabilita, în urma primirii unui mesaj neorodonat, sau când retransmite mesaje pierdute.

Respectarea strictă a ordinii mesajelor în TCP determină limitări importante pentru anumite aplicaţii. Aceste aplicaţii necesită o ordonare a mesajelor care afectează aceeaşi resursă (cum ar fi acelaşi apel sau acelaşi canal), astfel că mesajele sunt parţial corelate şi pot fi livrate fără a menţine întreaga succesiune a mesajelor.

Multistreaming-ul în SCTP, unde transmisiunea sigură şi livrarea datelor sunt idependente una de cealaltă, poate preveni aceasta problemă. Această caracteristică evită de asemenea blocajele de tip HOL (head-of-line). Această independenţă subliniaza flexibilitatea unei aplicaţii, permiţându-i să defineasca semantic diferite şiruri de date în cadrul aceluiasi flux SCTP, şi forţând ordonarea mesajelor numai în cadrul fiecărui şir în parte. Ca urmare, pierderile de date pe un şir nu afecteaza alte şiruri. Receptorul poate verifica imediat dacă au existat pierderi în secvenţa de transmisiune (cauzate de exemplu de pierderile de mesaje), şi de asemenea poate determina dacă mesajele primite după dectarea pierderilor fac parte din şirul afectat. Dacă SCTP primeşte un mesaj aparţinând unui şir afectat, un gol corespondent este înregistrat în SSN. Emitorul poate continua livrarea mesajelor şirurilor neafectate, în timp ce memoreaza mesajele din şirul afectat până în momentul retransmiterii.



Figura următoare ilustrează funcţionarea multi-homing-ului într-o asociere SCTP.




Figura 1.12


În mod implicit, SCTP conţine două şiruri. SCTP foloseşte şirul 0 ca şir implicit pentru transmiterea datelor. Aplicaţiile pot modifica numărul de şiruri folosite pentru transmiterea datelor.




Conservarea limitelor datelor


În SCTP, o aplicaţie emitentă poate contrui un mesaj dintr-un bloc de date, şi poate instrui SCTP-ul să transporte mesajele unei aplicaţii receptoare. SCTP-ul garantează livrarea acestor mesaje (a blocului) în întregime. De asemenea, informează receptorul atât despre începutul cât şi sfârşitul blocului. Aceasta se numeşte conservarea limitelor blocului. TCP nu păstrează aceste limite. El tratează toate datele care îi sunt pasate de aplicaţie ca pe un şir de octeţi de date, iar mai apoi le transmite receptorului în aceeaşi ordine în care le-a primit. TCP nu păstrează limitele nici în cazul în care pachetele sosesc neordonat. Ca urmare, receptorul nu poate rearanja pachetele. Trebuie să aştepte până pachetele sosesc în ordine, începand de la ultimul pachet nereceptat până la pachetul primit neordonat.


Închiderea unei asocieri SCTP


SCTP nu oferă suport pentru o conexiune “semi-deschisă”, cum poate avea loc în TCP. Într-o conexiune semi-deschisă, chiar daca un capăt indică finalul transmisiunii, celălalt capăt continuă să trimită date pentru un timp nedefinit. SCTP, pe de alta parte, presupune că atunci când procedura de închidere a asocierii a început, amandouă captele vor stopa transmisia. De asemenea presupune că este necesară numai eliminarea confirmărilor datelor trimise anterior.



Procedura de închidere foloseşte un mechanism din 3 mesaje, în care fiecare capăt a confirmat chunk-ul de date înainte de completarea procedurii de închidere. Când o terminare imediata este necesară, SCTP trimite un mesaj ABORT unui capăt.

Figura următoare ilustrează închiderea unei conexiuni pentru TCP şi SCTP.





  1. (b)

Figura 1.13


Figura 2.13 (a) reprezintă starea semi-deschisă specifică TCP, iar (b) procedeul de inchidere prin 3 mesaje, specific SCTP.



Suport SCTP pentru adrese IPv4 şi IPv6


SCTP oferă suport atât pentru adrese IPv4, cât şi pentru IPv6, pe un socket SCTP. Când negocierile pentru o asociere sunt începute, capetele fac schimb de liste de adrese în chunk-urile INIT şi INIT ACK. Adresele capetelor sunt prezentate de următorii parametrii: o adresă IPv4 are asociat un parametru cu valoarea 5, şi o adresă IPv6 un parametru cu valoarea 6. Un chunk INIT poate conţine mai multe adrese, care pot fi atât versiunea 4, cât şi 6.



Caracteristicile schimburilor de date în SCTP




Suport pentru reconfigurarea dinamică a adreselor



SCTP seteaza un capăt să reconfigureze dinamic informaţiile legate de adresele IP, pentru o anumită conexiune. Când capatele schimbă informaţii în timpul iniţializării asocierii, şi utilizarea SCTP este îmbunătăţită, fără a face efectiv modificări în protocol. Această caracteristica este utilă pentru aplicaţiile de reţea şi computaţionale, care adaugă sau întrerup sau reconfigureaza dinamic setări de reţea pentru anumite interfeţe fizice şi au nevoie ca modificarile să fie acceptate dinamic. Această caracteristică de asemenea setează ca un capăt sa-şi stabileasca o adresă primară destinaţie, ca atunci când propria sa adresă primară este ştearsă, capătul pereche este informat de noua adresă unde să trimita datele.

Pentru a seta SCTP să reconfigureze adresele IP dinamic, un pachet SCTP conţine următoarele tipuri de chunk:



Address Configuration Change Chunk (ASCONF):

Chunk-ul ASCONF comunică modificările de configuraţie care interesează, capătului pereche.



Address Configuration Acknowledgment (ASCONF-ACK):

Acest chunk este folosit de receptorul unui ASCONF pentru a confirma primirea unui chunk ASCONF.






Raportarea pachetelor pierdute



Când un pachet este pierdut sau aruncat din cauza unei erori, alta decât o congestie, un capăt poate interpreta greşit erorare cu pricina, şi să presupună că este chiar o congestie în mediu. Această interpretare greşita poate determina capătul să stopeze transmisiunea datelor, ceea ce ar duce la utilizarea inadecvată şi ineficientă a mediului. În funcţie de severitatea erorii, emitorul poate ramane într-o stare de congestie, ceea ce afectează starea ascocierii.

SCTP include chunk-ul PKTDROP care descoperă astfel de pachete, aruncate din motive de eroare, altele decat congestiile. După primirea unui chunk PKTDROP, un capat işi poate informa perechea că a primit un pachet cu o suma de control CRC32c sau Adler-32 incorectă. Atunci perechea poate retransmite pachetul fără a modifica fereastra de congestie.



Suport pentru ECN-Nonces în SCTP



Odată cu creşterea interesului şi dezvoltării aplicaţiilor în timp real şi a serviciilor de transport sensibile la întârzieri şi pierderi de pachete, raportarea la pachetele pierdute pentru a presupune existenţa unei congestii nu este suficientă. Algoritmii de management al congestiilor ai SCTP-ului au tehnici deja implementate, cum ar fi retransmiterea rapidă sau revenirea rapidă, care minimalizeaza impactul pierderilor.

Aceste mecanisme văd reţeaua ca pe un black box, şi continuă să trimită pachete până când pachetele sunt aruncate din cauza congestiilor. Totuşi, aceste mecanisme nu au ca scop să ajute aplicaţiile sensibile la întârzieri sau pierderi de pachete.


Odată cu implementarea tehnicilor de management al cozilor (queues) active în infrastructura Internetului, routerele pot porticipa la tratarea congestiilor.

Când are loc o congestie şi emitorul continuă să trimită pachete, numărul de pachete din coada de aşteptare a routerului creşte semnificativ, producând sugrumări ale traficului la nivelul echipamentului. În acest caz, routerul marcheaza pachetele cu biţi care să indice că au trecut printr-o congestie (CE – congestion experienced), şi le trimite mai departe receptorului pentru a indica formarea unei congestii, în loc să arunce aceste pachete.

Notificarea explicită a congestiilor (ECN – explicit congestion notification) este un algoritm de management al congestiilor care utilizează o metodă directă de tratare a lor. ECN foloseste câmpul ECN şi câmpul CE în antetul IP pentru a marca pachetele. Câmpul ECN include şi câmpul ECT (ECN-Capable Trasport), care este setat de emitor pentru a indica faptul că cele 2 capete cunosc şi pot utiliza ECN. Bitul CE este setat de router pentru a indica congestiile. Emitorii folosesc ECT (0) sau ECT (1) pentru a indica ECT pentru fiecare punct.

ECN foloseste următoarele informaţii pentru a genera notificările legate de congestii:



Neajunsul ECN este că o implementare neadecvată a receptorului sau un element de reţea precum router, firewal, sisteme de detectare a intruziunilor pot şterge fanionul ECNE care oferă semnalul de congestie emitorului.

Acest lucru se întamplă deoarece ECN nu are mecanisme de prevenire împotriva ştergerii acestui fanion de către echipamentele de reţea. Mai mult, ECN se bazează pe cooperarea receptorului de a returna emitorului semnale specifice întalnirii unor congestii. Dacă receptorul şterge aceste semnale şi nu le întoarce emitorului, emitorul nu-şi adaptează comportarea condiţiilor de mediu.


SCTP ofera suport pentru ECN şi este expus de asemenea riscurilor metionate mai sus. Acestea includ necunoaşterea semnalelor ECNE care pot cauza ca un emitor SCTP să fie agresiv faţa de fluxurile permise pe reţea. În schimb, SCTP oferă suport pentru ECN-nonce pentru a evita comportamentul deviant al receptorilor faţa de semnalele de congestie. De asemenea ECN-nonce protejează emitorii de alte forme de comportament neadecvate, cum ar fi notificările false şi duplicate incorecte ale notificărilor TSN.

ECN-nonce este o modificare a mecanismului de semnalizare ECN. Îmbunataţeşte controlul congestiilor prin împiedicarea receptorilor de a exploata ECN-ul şi de a câştiga pe nedrept resurse de bandă din reţea. Practic, receptorii nu mai pot modifica şi pachetele aruncate sau marcate. Ca şi în cazul ECN, ECN-nonce utilizează punctele de cod ECN(0) şi ECN (1), antetul IP, fereastra cwr şi biţii ECNE.

ECN-nonce foloseşte 2 biţi din antetul IP numiţi biţi ECN. Emitorul generează aleator un singur bit nonce şi îl codează în punctele de cod ECT(0) şi ECT(1). Pentru a indica congestiile din mediul de transmisiune, routerele suprascriu punctele de cod ECT cu biţi CE. Suma nonce NS este adunarea pe un bit cu transport a nonce-urilor primite de la receptor. Receptorul calculează suma nonce şi o returnează în fanionul NS al chunk-ului SACK, iar emitorul îi verifică valoarea. O sumă nonce incorectă arată că unul sau mai multe nonce-uri lipsesc la recepţie, deorece pentru o sumă corectă sunt necesari toţi nonce. De asemenea, dacă o sumă nonce incorecta este raportată de emitor fără semnale ECNE, emitorul poate presupune ca receptorul se confruntă cu probleme de notificare.

Suportul pentru ECN-nonce din SCTP include urmatoarele:




Suport pentru transmisiunile parţial sigure



SCTP oferă suport pentru transmisiunile de date parţial sigure (PR-SCTP) care setează un emitor SCTP să semnaleze un receptor că nu trebuie să aştepte date de la emitor. PR-SCTP 

realizează un serviciu de transmisiuni ordonate, dar nesigure, între două capete, comparativ cu transmisiunile UDP neordonate şi nesigure. PR-SCTP oferă algoritmi similari de evitare şi control al congestiilor ca şi SCTP, atât pentru traficul de date sigur şi partial sigur.


Capacitaţile SCTP-ului de detecţie şi protecţie împotriva picajelor de reţea sunt aplicabile de asemenea traficului parţial sigur. PR-SCTP setează un capăt să detecteze rapid o adresă detinaţie picată şi să treacă la urmatoarea disponibilă. De asemnea notifică când adresa destinaţie devine inaccesibilă.

Capacitatea de grupare a SCTP-ului face posibilă multiplexarea mesajelor printr-o asociere PR-SCTP. Multiplexarea face posibilă transmisia mai multor tipuri de mesaje prin acelaşi protocol (SCTP), fără a necesita şi alte protocoale separate.

SCTP include următorii parametrii şi chunk-uri pentru a face posibil serviciul de transmisiune parţial sigur:


Parametrul Forward-TSN-Supported:

Acesta este un parametru opţional din chunk-urile INIT şi INIT-ACK. Când o asociere este iniţializată, emitorul SCTP trebuie să includă acest parametru în chunk-urile menţionate pentru a anunţa capătul pereche de faptul că ofera suport pentru transmisiunile parţial sigure.


Chunk-ul Forward Cumulative TSN (FORWARD TSN):

Receptorul trimite acest chunk emitorului pentru a-l informa de suportul pentru PR-SCTP. Un emitor SCTP foloseşte acest chunk pentru a determina receptorul să deplaseze suma TSN-urilor primite, pentru că TSN-urile lipsă sunt asociate cu chunk-uri de date care nu pot fi transmise sau retransmise emitorului.


Serviciul de fiabilitate temporală este un exemplu de serviciu parţial sigur pe care SCTP îl oferă nivelurilor superioare folosind PR-SCTP. Acest serviciu setează aplicaţia utilizatorului să indice o limită a duratei în timp în care emitorul trebuie să transmită sau retransmită un mesaj.

Dacă un capăt SCTP oferă suport pentru chunk-ul FORWARD TSN, poate include parametrul Forward-TSN-supported în chunk-ul INIT pentru a indica suportul oferit. Dacă un capăt alege să nu includă parametrul Forward-TSN-supported, nu poate trimite sau procesa un chunk FORWARD TSN niciodată pe timpul duratei de viaţă a unei asocieri. În schimb, trebuie să se comporte de parcă nu ar oferi un astfel de suport, şi să genereze o eroare de câte ori primeşte un chunk FORWARD TSN.

Când receptorul unui chunk INIT sau INIT-ACK detecteaza un parametru Forward-TSN-Supported, dar nu oferă suport pentru Forward-TSN, receptorul poate, opţional, să raspunda cu un parametru Unsupported Parameters parameter, definit în RFC 2960, sectiune 3.3.3.


Un receptor poate efectua următoarele sarcini în cazul în care primeşte un chunk INIT care nu include parametrul Forward-TSN-Supported:




1.3.4 Tratarea erorilor în SCTP



Traficul în reţele este imprevizibil. Căderi de reţea neasteptate sau burst-uri de trafic pot avea loc, ceea ce poate duce la inaccesibilitatea unui punct. O asemenea reţea este predispusă la erori şi o aplicaţie emitoare trebuie să fie prudentă când transmite şi retransmite date, deorece capătul receptor ar putea să nu fie disponibil. Această indisponibilitate a receptorului poate fi cauzată atât de o eroare de cale sau o cădere chiar a receptorului.

SCTP oferă metode de tratare a erorilor adecvate pentru a depaşi aceste probleme. Înainte de a transmite date, SCTP trimite chunk-uri de informaţie pentru a verifica dacă o destinaţie este activă. Chiar înainte de a alege o nouă cale pentru a atinge o destinaţie sau inainte de a inchide o asociere, SCTP se asigură că adresa destinaţie este inaccesibilă sau inactivă.

SCTP foloseşte următoarele metode de tratare a erorilor:



Retransmiterea chunk-urilor de date


SCTP foloseste chunk-urile de date pentru a schimba informaţie între două adrese. După primirea unui chunk DATA, adresa receptoare trimite o confirmare adresei emitoare. Dacă adresa receptoare nu primeşte chunk-ul de date corect, trimite un pachet SACK care determină adresa emitoare să retransmită chunk-ul de date. De asemenea, emitorul va retransmite datele dacă ceasul de retransmisie expiră.

SCTP limitează rata de retransmisie a chunk-urilor de date pentru a reduce şansele de congestie. Modifică valoarea ceasului de retransmisie (RTO – retransmission time out), bazat pe estimarea round trip delay, şi reduce rata de transmisie exponential, când pierderile de mesaj cresc.

Într-o asociere SCTP activă, cu transmisiuni constante de date, pachetele SACK au o probabilitate mai mare de cauzare a retransmisiei, decât expirarea ceasului de retransmisie. Pentru a reduce cât mai mult cantitatea datelor retransmise, SCTP foloseşte regula celor patru pachete SACK, astfel încat SCTP să retransmită un chunk de date numai dupa primirea celor patru SACK, care se vor referi la un chunk de date lipsă. De asemenea, SCTP foloseşte cele patru pachete SACK pentru a evita retansmiterea unor pachete primite neordonat.







HEARTBEAT-uri pentru identificarea erorilor de cale


SCTP trimite periodic chunk-uri HEARTBEAT către destinaţii inactive, sau adreselor alternative pentru a identifica o eroare de cale. SCTP păstreaza un counter pentru a reţine numărul de heartbeat-uri care sunt trimise destinaţiilor inactive, fără a primi confirmarea normală Heatbeat ACK. Când counterul atinge o valoare maximă specificată, şi protocolul declară adresa destinaţie inactivă. Apoi anunţa aplicaţia de situaţia aparută şi începe să folosească adresele alternative pentru a continua transimisia chunk-urilor de date.

Totuşi, SCTP continuă să trimită heartbeat-uri destinaţiei inactive până primeşte o confirmare. La primirea unui ACK, SCTP consideră destinaţia activă din nou.

Rata cu care SCTP emite aceste heartbeat-uri depinde de suma valorilor RTO şi a parametrului de întarziere, care permite ca traficul de heartbeat-uri să fie ajustat nevoilor aplicaţiei.


HEARTBEAT-uri pentru identificarea erorilor la nivelul endpoint-ului


Metoda de determinare a acestor căderi sau erori este similară cu cea prezentată anterior, pentru cale.

SCTP păstrează un counter pentru toate adresele destinaţie, pentru a reţine numarul de heartbeat-uri transmise fără a primi confirmare. Când, pentru toate adresele, s-a ajuns la o valoare maximă, prestabilită, SCTP declară capătul pereche picat şi închide asocierea.


1.3.5 Securitatea în SCTP


SCTP foloseste doua mecanisme de securitate:

- mecanismul cookie

- eticheta de verificare.


Macanismul COOKIE



Un mecanism cookie este declanşat în timpul iniţializării unei asocieri, pentru a asigura protecţie împotriva atacurilor. Acest mecanism foloseşte un handshake în patru paşi, ultimii doi paţi putând purta şi date pentru un start mai rapid.


Mecanismul cookie pazeşte împotriva atacurilor nedetctabile care generează chunk-uri INIT, care vor suprasolicita resursele serverului SCTP, forţându-l pe acesta să aloce memorie şi resurse pentru a trata noul chunk INIT. În loc să aloce memorie pentru un TCB (transmission Control Block), server-ul creeaza un parametru cookie cu informaţie TCB, împreuna cu un timp de viaţă valid şi o amprenta pentru antentificare, şi trimite acestea înapoi într-un chunk INIT-ACK. Atacatorul nu va primi niciodată acest chunk, deorece el este directat către adresa destinaţie, adică adresa sursă a chunk-ului INIT. Un client SCTP valid va primi cookie-ul şi va întoarce un chunk COOKIE ECHO, pe care serverul va putea să-l preia şi să-l folosească pentru 

recontruirea TCB-ului. Cookie-ul este creat de server, iar formatul şi cheia secretă raman pe server, deorece nu schimbă aceste informaţii cu clientul.


Ethicheta de verificare

Eticheta de verificare este un întreg unasigned de 32 de biţi (U32) care este generat aleator pentru a verifica dacă pachetul SCTP analizat aparţine asocierii curente, sau este un pachet rătăcit, corespunzător unei asocieri anterioare. SCTP aruncă pachetele primite fără eticheta de verificare aşteptată, pentru a proteja de atacuri mascate sau pachete din asocieri anterioare.

Eticheta de verificare se aplică în cazul pachetelor SCTP care nu conţin următoarele chunk-uri: INIT, SHUTDOWN COMLETE, COOKIE ECHO, ABORT sau SHUTDOWN ACK.

În timp ce trimite un pachet SCTP, un terminal trebuie să completeze eticheta de verificare a pachetului de ieşire, cu valoarea parametrului Initiate Tag din INIT sau INIT ACK primite de la terminalul pereche.

După primirea unui pachet SCTP, terminalul trebuie să se asigure că valoarea din câmpul etichetei de verificare a pachetului se potriveşte cu propria etichetă. Dacă această potrivire nu există, receptorul aruncă pachetul şi nu îl mai proceseaza în niciun fel.

Eticheta de verificare este aleasă de fiecare terminal în parte, în etapa de stabilire a unei asocieri.


1.4 Comparaţie teoretică între UDP, TCP şi SCTP


Deşi capitolele anterioare au subliniat şi detaliat diferenţele fundamentale între cele trei protocoale de trasport, o rezumare şi o categorisire a acestor diferenţe este necesară.


Tabelul urmator compara caracteristicile SCTP, TCP si UDP


Caracteristica

SCTP

TCP

UDP

Cunoaşterea stării pentru fiecare terminal

Da

Da

Nu[1]

Orientat pe conexiune

Da

Da

Nu

Transfer de date sigur

Da

Da

Nu

Controlul şi evitarea congestiilor

Da

Da

Nu

Conservarea limitelor mesajelor

Da

Nu[2]

Da

Fragmentarea mesajelor şi descoperirea MTU unei căi

Da

Da[2]

Nu

Gruparea mesajelor

Da

Da[2]

Nu

Suport pentru multi-home

Da

No

Nu

Suport pentru multi-stream

Da

Nu

Nu

Livrare neordonată a datelor

Da

Nu

Da



Protecţie împotriva atacurilor cu pachete SYN

Da

Nu

Nu

Folosirea heartbeat-urilor (verificarea disponibilităţii)

Da

Nu[3]

 

[1] În UDP, un nod poate cumunica cu un alt nod fără a trece printr-o procedură de pregătire sau fără a schimba informaţii de stare. Totuşi, fiecare pachet UDP conţine informaţiile de stare necesare pentru formarea unei conexiuni, astfel încat să nu fie necesară menţinerea unei stări anume la nivelul fiecărui terminal.

[2] TCP nu păstrează nici o limită a mesajelor. Tratează toate datele primite de la nivele superioare ca pe şiruri de octeţi neformataţi. Totuşi, deoarece TCP transferă datele în secvenţe de octeţi, poate automat să redimensioneze toate datele în noi segmente TCP potrivite noiului MTU, înainte de transmiterea lor.

[3] TCP implemenentează un mecanism de keep-alive, similar cu chunk-ul HEARTBEAT din SCTP. În TCP, totuşi, intervalul de keep-alive este, în mod implicit, la două ore, pentru a reseta starea. În SCTP, chunk-ul HEARTBEAT este folosit pentru a facilita detectarea rapida a unor căderi.


Tabelul 1.2


 Deşi protocolul SCTP pare să aibă multe in comun cu UDP-ul, el de asemenea este un protocol de încredere, ca TCP-ul. Este un protocol bine gândit, care uneşte mai multe caracteristici ale TCP-ului şi UDP-ului, făcându-l, cel puţin în teorie, mai bun decât cele două.


1.5 Prezentarea protocolului HTTP



Protocolul de transport al hiper-textelor HTTP (Hyper-Text Transport Protocol) este un protocol bazat pe stiva de protocoale TCP/IP, destinat sistemelor hipermedia conlucrând în medii distribuite. HTTP a început să fie proiectat şi utilizat din anul 1990, dezvoltîndu-se împreuna cu spaţiul WWW.

Prima versiune, referită ca HTTP/0.9, a reprezentat un simplu protocol de transfer de date prin Internet. Urmatoarea versiune, HTTP/1.0, definită de RFC 1945, a îmbunătăţit transferul de mesaje, permiţându-se mesaje în format MIME (Multipurpose Internet Mail Extensions), conţinând meta-informaţii despre datele transmise şi despre semantica dialogului cererilor şi raspunsurilor dintre clienţii şi serverele Web.

A urmat HTTP/1.1 care oferă mai multe funcţionalităţi suplimentare, precum mecanisme de căutare, adnotare şi actualizare, având suport pentru URI (Uniform Resource Identifier), specificând adresele ca locaţie (prin URL) sau prin nume (via URN). HTTP de asemeni este utilizat ca protocol generic pentru comunicarea între agenţii utilizator şi porţile (gateways) către alte sisteme Internet, incluzând suport pentru protocoale ca SMTP (Simple Mail Transfer Protocol), NNTP (Network News Transfer Protocol), FTP (File Transfer Protocol), Gopher. În acest mod, HTTP permite accesul hipermedia la resurse disponibile din diverse aplicaţii.

Protocolul HTTP este un protocol sigur, de tip cerere/raspuns, comunicaţiile decurgând peste conexiunile TCP/IP, portul standard de acces fiind portul 80.


1.5.1 Modul de funcţionare


HTTP oferă o tehnică de comunicare prin care paginile web se pot transmite de la un terminal aflat la distanţă spre propriul terminal. Dacă se apelează un link sau o adresă de web cum ar fi http://www.example.com, atunci se cere calculatorului host să afişeze o pagină web (index.html sau altele). În prima fază numele (adresa) www.example.com este convertit de protocolul DNS într-o adresă IP. Urmează transferul prin protocolul TCP pe portul standard 80 al serverului HTTP, ca răspuns la cererea HTTP-GET. Informaţii suplimentare, cum ar fi indicaţii pentru browser, limba dorită şi altele se pot adăuga în header-ul (antetul) pachetului HTTP. În urma cererii HTTP-GET urmează, din partea serverului, răspunsul cu datele cerute, ca de exemplu: pagini în (X)HTML, cu fişiere ataşate ca imagini, fişiere de stil (CSS), scripturi (Javascript), dar pot fi şi pagini generate dinamic (SSI, JSP, PHP şi ASP.NET). Dacă dintr-un anumit motiv informaţiile nu pot fi transmise, atunci serverul trimite înapoi un mesaj de eroare.


1.5.2 Terminologie


Un circuit virtual la nivelul transport stabilit între doua programe care doresc să comunice.

Unitatea de baza a unei comunicaţii HTTP, constând dintr-o secvenţă structurată de octeţi, transmisă printr-o conexiune.

Un mesaj de cerere HTTP.

Un mesaj de raspuns HTTP.

Un obiect de date sau serviciu care poate fi identificat de un URI. Resursele pot avea reprezentări multiple (e.g. formate, mărimi, rezoluţii etc.).

Informaţia transferată într-o operaţie de cerere sau de raspuns. O entitate cuprinde atît meta-informaţii, cît şi conţinutul propriu-zis.

O entitate inclusă într-un raspuns ce reprezintă o negociere între client şi server.

Mecanism de selectare a reprezentării potrivite a unei date solicitate.

Un program care stabileşte conexiuni cu scopul de a trimite cereri.

Clientul ce iniţiaza o cerere (navigator, editor, robot de traversare Web). Navigatoarele cele mai cunoscute sunt Internet Explorer, Mozilla, Opera.



O aplicaţie care acceptă conexiuni, raspunzând la anumite cereri transmise de clienţi. Un program poate juca rol atât de server, cât şi de client. Un server se poate comporta ca server iniţial, poartă, tunel sau proxy Web în funcţie de natura cererii. Cele mai cunoscute servere Web sînt: Apache, Netscape Enterprise Server, Sun Web Server, Windows NT Web Server, Stronghold.

Un program intermediar care ruleaza ca server, cât şi drept client pentru a transmite cereri altor servere. Cererile trimise unui proxy pot fi rezolvate intern sau transmise mai departe, către alte servere (posibil translatate). Un proxy trebuie să implementeze cerinţele de server şi de client ale specificaţiei HTTP.

Un server care lucrează ca intermediar pentru alte servere, în mod transparent, fiind şi un translator de protocoale.

Un program intermediar funcţionând ca mijlocitor între două conexiuni.

Un depozit local de memorare a mesajelor (datelor) de raspuns şi un subsistem de control al acestuia. Memoria cache reduce timpul de raspuns şi congestia reţelei. Orice client şi server poate include un cache.


1.5.3 Mesaje HTTP

Un mesaj HTTP este divizat într-o parte de antet şi o parte corp. Antetul cuprinde o serie de câmpuri (unele dintre ele obligatorii) oferind informaţii despre versiunea de protocol folosit, codificarea datelor, tipul de medii, lungimea şi tipul mesajului etc. Sintaxa unui antet de mesaj este:

message-header ::= field-name ":" [ field-value ] CRLF

Formatul unei cereri este următorul:

Request ::= Method Request-URI ProtocolVersion CRLF

[ message-header ] [ CRLF data ]

Method ::= string

data ::= MIME-data

Raspunsul la o cerere are următoarea sintaxa:

status-line ::= HTTP-version status-code reason CRLF

status-code ::= digit digit digit

reason ::= string

Structura

Orice mesaj HTTP trebuie să debuteze cu un câmp indicând versiunea protocolului în prima linie a mesajului:



HTTP-Version ::= "HTTP-Version" ":" "HTTP" "/" digit "." digit

În prezent este operaţional protocolul 1.1, deci toate mesajele de cerere şi de raspuns vor începe cu linia HTTP/1.1.

Mesajele pot fi codificate conform autorităţii IANA (Internet Assigned Numbers Authority) fiind permise codificarile:

Aceste codificări sunt specificate de câmpul Content-Transfer-Encoding.

Pentru MIME, se specifică tipul şi subtipul mediului de informaţii (de exemplu: text/html, text/plain, image/jpeg, video/mpeg etc.) în câmpul Content-Type. Un mesaj poate fi transmis în format multipart, constând din mai multe entităţi, toate având o sintaxă comună. Dacă o aplicaţie recepţioneaza un subtip nerecunoscut, în mod automat îl va trata ca multipart/mixed.

Câmpul Accept dintr-un mesaj de cerere poate specifica mulţimea de tipuri de date returnate de server ca răspuns:

Accept ::= "Accept" ":" (media-range [ accept-params ])+

media-range ::= ("*/*" | type "/*" | type "/" subtype)*

accept-params ::= ";" "q=" qvalue accept-extension*

accept-extension ::= ";" token [ "=" (token | string) ]

type ::= string

subtype ::= string

qvalue ::= digit "." digit

Simbolul "*" specifică toate tipurile/subtipurile de medii dintr-o anumită categorie. De exemplu, pentru a accepta doar imagini, indiferent de format, se va transmite Accept: image/*.

Pot fi specificaţi unul sau mai mulţi factori de calitate relativă. De pildă, cererea Accept: audio/*; q=0.2, audio/basic este interpretată astfel: "se prefera tipul audio/basic dar serverul va trebui sa trimita toate tipurile audio având calitatea de cel putin 80%".

Locaţia unei resurse HTTP va fi dată de cîmpul Content-Location în formatul URI, conform schemei http:

Content-Location ::= "Content-Location" ":" http-URL

http-URL ::= "http://" host [ ":" port ] [ abs_path ]

De exemplu: http://www.infoiasi.ro/~busaco/egon/http.html

Dacă portul nu apare, se ia prin definiţie portul 80, rezervat protocolului HTTP. Adresele host vor fi tratate indiferent de scrierea cu caractere majuscule sau nu (case-insensitive), dar calea de directoare abs_path este tratată case-sensitive (majusculele sunt diferite de minuscule). 

Dacă abs_path nu apare, se va considera "/", iar caracterele rezervate vor fi echivalente cu codul lor în hexa, precedat de "%" (în forma "% hex hex").

De exemplu, aceste trei URI sunt echivalente:

http://www.infoiasi.ro:80/~busaco/poems.html

http://www.infoiasi.ro/%7Ebusaco/poems.html

http://WWW.InfoIasi.Ro/%7ebusaco/poems.html

Corpul mesajului va conţine informaţiile propriu-zise ale unei cereri sau raspuns, specificate de o entitate. O entitate-corp diferă de corpul mesajului numai dacă sunt aplicate codificări.

Formatul mesajelor de cerere

Linia de cerere a unui mesaj HTTP este definită astfel:

Request-Line ::= Method Separator Request-URI Separator HTTP-Version CRLF

Method ::= "OPTIONS" | "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE"

Request-URI ::= "*" | absolute-URI | abs_path

Serverul va returna codul 405 (Method Not Allowed) sau 501 (Not Implemented) dacă se va trimite numele unei metode inexistente.

Descrierea metodelor permise urmează mai jos:

OPTIONS

Reprezintă o cerere de informaţii despre opţiunile de comunicare disponibile într-un dialog cerere/raspuns.

GET

Reprezintă o cerere de accesare a unor informaţii (entităţi) identificate de Request-URI. Semantica metodei GET se schimbă în cerere conditionata dacă mesajul de cerere include cîmpuri antet If-Modified-Since, If-Match, If-Range etc. Dacă se specifică un cîmp Range, atunci GET va specifica o cerere parţiala.

HEAD

Este similară cu GET, dar serverul va returna un mesaj având informaţii doar în antetul lui. Meta-informaţiile din antetele HTTP din raspunsul la o cerere HEAD vor fi identice cu cele din raspunsul la o cerere GET. Metoda HEAD este folosită deseori pentru testarea validităţii, accesibilităţii şi modificărilor recente ale unei legaturi hipertext. Pentru documente HTML, o cerere HEAD va avea ca raspuns 

doar antetul paginii, adică informaţiile cuprinse între marcatorii <head>...</head>.

POST

Metoda e utilizată să identifice dacă serverul acceptă o entitate înglobata în cadrul cererii. POST este proiectată să implementeze o metodă uniformă pentru funcţiile: adnotarea resurselor, trimiterea unui mesaj într-o lista de ştiri, trimiterea datelor unui formular Web, extinderea unei baze de date printr-o operaţiune de adăugare. Semantica exactă a metodei este definită de server. Raspunsul serverului poate fi 200 (OK), 201 (Created) sau 204 (No Content).

PUT

Specifică faptul ca o entitate inclusă în mesaj să fie stocată la adresa dată de Request-URI. Dacă resursa deja există, se consideră o actualizare a ei. Diferenţa fundamentală între PUT şi POST este reflectată de modul diferit de manipulare a adresei REquest-URI. Într-o cerere POST, URI identifică resursa care va prelucra entitatea înglobată de mesaj. Acea resursă poate fi un proces de prelucrare a datelor, o poartă către alt protocol, o entitate separată acceptând adnotări. Prin contrast, URI dintr-un PUT identifică entitatea inclusă în mesajul de cerere. O unică resursă poate fi identificată de URI-uri multiple.

DELETE

Cere ca serverul să şteargă resursa identificată de Request-URI.

TRACE

Invocă o cerere de diagnosticare (trasaj).

Coduri de stare

Pentru fiecare cerere a unui client, serverul HTTP raspunde cu o serie de coduri de stare a operaţiei solicitate, dintre care menţionăm:

Coduri de informare (1xx)

Acestea dau informaţii despre o anumită acţiune:

100 Continue - clientul poate continua cererea, trebuind să trimită următoarea parte a unui mesaj parţial;

101 Switching Protocols - serverul înţelege cererea, dar necesită recepţionarea unui câmp Upgrade pentru a şti ce tip de protocol va fi folosit la nivelul aplicaţiei (e.g. pentru transmiterea de informaţii multimedia, când poate fi utilizat un protocol sincron, în timp-real);


Coduri de succes (2xx)

Acestea raportează efectuarea cu succes a unei operaţiuni:

200 Ok - cererea a fost rezolvată cu succes;

201 Created - o nouă resursă a fost creată cu succes. Resursa creată poate fi identificată de URI-ul returnat de entitatea-raspuns, trebuind a fi generată înainte de returnarea codului (în caz contrar se trimite 202);

202 Accepted - cererea a fost acceptata spre procesare, dar înca n-a fost satisfacută în întregime. Nu există nici o facilitate pentru retransmiterea unui cod de stare în urma execuţiei unei operaţii asincrone;

204 No Content - serverul a rezolvat cererea, dar nu are ce returna;

Coduri de redirectare (3xx)

Indică o redirectare, către altă locaţie/server a cererii (de exemplu, la apariţia marcatorului <meta name="refresh" content="..."> în cadrul unui document HTML):

300 Multiple Choices - resursa cerută corespunde uneia dintre reprezentarile multiple pe care le are;

301 Moved Permanently - resursa cerută a fost asignată unui URI nou şi orice referinţa viitoare la ea trebuie sa se realizeze prin acest URI returnat;

302 Moved Temporarily - resursa cerută are un alt URI, pentru o perioadă temporara;

303 See Other - raspunsul la cerere poate fi găsit la o altă locaţie URI şi trebuie accesat folosind o metodă GET;

305 Use Proxy - resursa cerută trebuie accesată printr-un proxy desemnat de câmpul Location;

Coduri de eroare client (4xx)

Indică apariţia unei erori pe partea clientului:

400 Bad Request - cererea n-a putut fi înţeleasă de server din cauza unei sintaxe eronate a metodei;

401 Unauthorized - cererea necesită autentificarea utilizatorilor, prin transmiterea unui câmp Authorization;

403 Forbidden - serverul întelege cererea, dar refuză s-o satisfacă din diverse metode;

404 Not found - serverul nu gaseşte resursa specificată de Request-URI;

406 Not Acceptable - resursa este incompatibilă cu antetul cererii;


Coduri de eroare server (5xx)

Sunt coduri semnificând o eroare pe partea serverului:

501 Not Implemented - serverul nu are implementată o funcţionalitate necesară satisfacerii unei cereri;

502 Bad Gateway - serverul, lucrând ca poartă sau proxy, a recepţionat un răspuns invalid de la alt server căruia i-a trimis cererea;

503 Service Unavailable - serverul nu poate satisface cererea, din cauza supraîncărcării temporare sau a unor raţiuni de administrare;

504 Gateway Timeout - timpul de aşteptare a raspunsului a expirat.

Pot fi transmise şi coduri de avertisment, cu valori cuprinse între 0 şi 99.

1.5.4 De ce HTTP peste SCTP?


Este importantă înţelegerea mijlocului prin care un client descarcă informaţie de pe o pagină web. Folosind HTTP 1.0, un client întâi deschide o conexiune TCP, descarcă codul care descrie pagina, apoi deschide noi conexiuni pentru a descărca imagini şi alte informaţii.


Figura 1.14 – o pagina web tipică






Figura 1.15 – pagina web numai cu imagini



Pentru prima figură corespund patru conexiuni, iar pentru cea de-a doua zece.

Este evident că paginile care conţin informaţii suplimentare bogate pe lângă text suprasolicită resursele de reţea.

Versiune 1.1 de HTTP încearcă o rezolvare a acestei situaţii încercând să minimizeze numărul de conexiuni TCP folosite. În particular poate folosi ceea ce se cheamă conexiuni persistente pentru a atinge acest scop. În aceste conexiuni persistente se pot folosi stive sau cozi, care permit clienţilor HTTP să genereze mai multe cereri fără a mai aştepta raspunsurile pentru cererile deja lansate şi aflate în aşteptare. Aceasta poate îmbunataţi preformanţele HTTP peste TCP, dar are un dezavantaj important. În HTTP 1.0 fiecare cerere este separată, fiind pe conexiuni separate, însa în HTTP 1.1, această caracteristică este distrusă, prin folosirea cozilor de aşteptare, şi rămane la latitudinea browser-ului să trateze răspunsurile asincrone.

HTTP-ul nu este limitat la TCP în ceea ce priveşte protocol de transport. RFC-ul subliniază faptul că orice protocol de reţea şi de transport poate fi folosit, atâta timp cât oferă o conexiune sigură şi stabilă.

SCTP este un astfel de protocol de transport. Dupa cum am precizat deja, SCTP include o caracteristică numită “multi-stream”. Scopul acestei lucrări este de a verifica dacă această caracteristică de multistreaming poate fi folosită pentru a creşte throughput-ul HTTP-ului şi dacă performanţele SCTP-ul îl fac pe acesta comparabil sau chiar mai bun decat TCP-ul, şi dacă multistreaming-ul poate fi de ajutor în reţelele predispuse la aruncarea pachetelor, aşa cum presupune teoria.





1.6 Prezentare generală RRDtool


RRDtool se referă la Round Robin Database tool. Round robin este o tehnică care lucrează cu o cantitate fixă de date şi un pointer la elementul curent. Analogia ar fi cu un cerc cu niste puncte schiţate pe margine, reprezentând locurile în care pot fi stocate datele. Pointer-ul ar fi o săgeată cu orginea în centrul cerului şi vârful indicând punctele schiţate. Când datele curente sunt citite sau scrise, pointer-ul se mută la urmatoarea locaţie, până parcurge toate locaţiile, deci practic tot cercul. În acest moment, cele mai vechi locaţii vor fi refolosite. În acest fel, baza de date nu va creşte în dimensiune, şi ca urmare nu necesită întreţinere. RRDtool lucrează cu RRDs (Round Robin Databases). Primeşte şi memorează date de la acestea.

Practic, RRDtool permite memorarea şi analizarea datelor obţinute de la tot felul de surse de date (DS – data sources). Partea de analiză se bazează pe capacitatea RRDtool de a genera rapid reprezentări grafice ale valorilor datelor colectatd într-un timp predefinit.

Funcţii

În general man pages descriu comenzile care pot fi folosite pentru a genera statistici cu RRDtool, dar acestea poate fi organizate într-o stivă (scrip), când este nevoie să execute multe operaţiuni într-un timp scurt, şi atunci folosim noţiunea de “telecomandă” (remote control) .


Create:

Crearea unei noi baze de date Round Robin

Update:

Înregistrarea unor noi date în RRD.

Graph:

Generarea unor grafice pe baza datelor aflate în una sau mai multe RRD. În afară de generarea graficelor, datele mai pot fi extrase către stdout (standard output)

Dump:

Depozitarea conţinutului unei RRD într-un fişier ASCII. În raport cu restore, dump poate fi folosit şi pentru a transporta o RRD de la o arhitectura la alta

Restore:

Refacerea unei RRD din format XML într-o RRD binară.

Fetch:

Aducerea datelor pentru o anumită perioada de timp din RRD. Funcţia graph foloseşte fetch pentru a-şi obţine datele din RRD.

Tune:

Modificarea unei RRD.

Last:

Găsirea ultimului update dintr-o RRD.

Info:

Afişarea unor informaţii despre RRD.

Rrdresize:

Schimbarea dimensiunii unei RRD (nerecomandat).

Xport:

Exportarea datelor primite de la una sau mai multe RRD.

Rrdcgi:



Aceasta este o unealta independentă pentru generarea graficelor pe loc.


Funcţionalitate


Achiziţionarea datelor:

Când se monitorizează starea unui sistem, este convenabil ca datele să fie disponibile la intervale de timp constante. Din pacate nu este tocmai posibil lucrul acesta, aşa că fişierele cu log-uri pot fi updatate oricând. RRDtool va insera automat valorile de la DS la ultima etichetă de timp oficială.


Consolidarea:

Să presupunem că utlizatorul doreşte să memoreze un set de date la fiecare minut, dar vrea acest lucru pentru un an de zile. Evident, acest lucru este realizabil, dar consumator de resurse, iar apoi datele devin imposibil de analizat. Prin caracteristica sa de consolidare, RRDtool oferă o soluţie acestei probleme. Când se defineşte o nouă RRD, se poate stabili la ce interval să se facă consolidarea şi ce funcţie de consolidare (CF) să se folosească (media, minimum, maximum, ultimul) pentru a consolida datele. Se pot defini oricâte moduri de consolidare pentru o RRD. Se va updata pe măsura ce noi date vor fi introduse.


Arhivele Round Robin:

Valorile datelor din cadrul aceleiaşi consolidări sunt păstrate într-o arhiva (RRA – Round Robin Archive). Acesta este un mod foarte eficient de păstrare a datelor pentru o anumită perioada de timp.

Funcţionează în felul urmator: dacă dorim memorarea a 1000 de valori la fiecare 5 minute, RRDtool va aloca spaţiu pentru cele 1000 de valori şi pentru un antet. În antet va stoca un pointer către ultima valoare adaugată. Noile valori vor fi scrise precum s-a descris mai sus, limitând automat history-ul la ultimele 1000 de valori. Pentru că pot fi definite mai multe RRA pentru o singura RRD, se mai poate crea o RRA de 750 de valori scrise la fiecare 2 ore, şi se pot păstra datele ultimelor două luni la o rezoluţie mult mai mică.

Folosirea RRA garantează că RRD nu creşte în timp şi că datele vechi sunt automat eliminate. Folosit împreună cu nişte funcţii de consolidare, se pot păstra datele pentru perioade mult mai lungi.


Date necunoscute:

Precum s-a menţionat mai devreme, RRD memorează date la intervale de timp constante. Să presupunem că la momentul înregistrării datele nu sunt disponibile. RRDtool rezolvă aceste situaţii prin stocarea unor date necunoscute marcate cu valoarea *UNKNOWN*. Această valoare este tolerată de toate funcţiile de consolidare, iar în momentul stocării în RRA, se realizează un calcul de validitate pentru a verifica că numărul datelor necunoscute nu depăşeşte o limită.


Trasarea graficelor:

RRDtool permite generarea de rapoarte numerice şi grafice bazat pe datele din RRD. Toate detaliile graficelor pot fi definite de utlizator: dimensiune, culoare, conţinut.



Capitolul 2 – Explicarea aplicaţiei


2.1 Scopul aplicaţiei


Având în vedere caracteristicile deosebite ale protocolului SCTP, aplicaţia îşi propune să verifice o parte dintre acestea, şi de asemenea să ofere o comparaţie cu protocolul TCP din punct de vedere al comportării vis-a-vis de protocolul HTTP.


Se va verifica comportarea SCTP-ului în medii congestionate, generând 100 de pachete ICMP per secundă, de dimensiuni mult mai mari decât cea implicită de 56 de biţi, folosind comanda:


ping –f –S 100000 10.10.10.10

Se va verifica procedeul de închidere a ascocierii, atât pe server, cât şi pe client, observând că odată iniţiat procesul de închidere pe unul dintre acestea, socket-ul va fi închis pe ambele instanţe. Aceasta va fi realizată folosind aplicaţia tshark. Comanda folosită este:


tshark –i eth0

Pentru a compara SCTP cu TCP, aplicaţia sctp_client.c se conecteaza la server.c printr-o singură adresă, folosind unul şi respectiv 50 de stream-uri, apoi se conectează pe patru adrese, şi foloseşte în prima etapă un singur stream, apoi, într-o a doua etapă 50 de stream-uri.

Aplicatia sctp_client.c este adaptată şi cazul TCP, astfel că se observă şi comportarea TCP-ului în aceleaşi condiţii impuse protocolului SCTP.

Script-ul care realizează testarea automată impune clientului să genereze cereri HTTP-GET o dată la fiecare minut, iar RRDtool înregistrează numărul de kilobiţi şi timpul în care se realizeaza transferul în microsecunde. Apoi, folosind comanda ./graph_both se generează grafice de viteză a reţelei, în cele 3 cazuri.


2.2 Conţinutul aplicaţiei


Aplicaţia este compusă din 10 fişiere.


SERVER.C


Acest fişier este un server web, care foloseşte ca protocol de transport SCTP. În prima parte a programului sunt incluse librăriile folosite, apoi sunt declarate constantele: PORT, BUFSIZE, INSTREAMS şi OUTSTREAMS care reprezintă portu-ul pe care se vor face asocierile între server şi client, dimensiunea unui buffer folosit pentru stocarea temporară a diferitelor date folosite pe parcursul programului şi numarul maxim de stream-uri folosite.

A doua parte a programului se ocupă de comportarea HTTP-ului. Întai sunt incluse răspunsul cu versiunea şi antetul HTTP pentru indica o cerere reuşită. Apoi, un raspuns, antent şi 

conţinut care vor indica faptul că o cerere nu a fost inţeleasă, că documentul nu a fost găsit sau că metoda folosită nu este cunoscută.

Apoi este definit un pointer la structura de tip addrinfo, pentru a stabili adresele folosite pentru multihoming. De asemenea este realizată o verificare a lor, folosind funcţia getaddrinfo.

Întai este stabilită o referenţiere la structura addrinfo, hints, în care se stabilesc fanioanele, familia de adrese şi tripul protocolului.

Funcţia getaddrinfo întoarce -1 în caz de eroare, şi programul execută exit(1) (se iese din program). Structura hints din aceasta funcţie va fi apelată în următoarea funcţie, cu argumentele: AF_UNSPEC si SOCK_SEQPACKET, adică orice familie de adrese de reţea şi protocol SCTP.

În urma fiecărei alocări sau folosiri a buffer-elor acestea sunt eliberate cu funcţia free().

Toate procesele child terminate sunt închise şi resursele eliberate, pentru ca aceste procese să nu devină orpahns, când procesul părinte este oprit. Aceasta se realizează folosind un handler al semnalului SIGCHLD.

Următoarea parte a programului realizează partea de GET a unei pagini PAGE şi întoarce rezultatul unui descriptor, CONNECTION_FD. Pentru aceasta se verifică întâi dacă pagina cerută începe cu un slash (/) şi nu conţine alte slash-uri, deorece nu este suportată transmisiunea subdirectoarelor. Dacă această condiţie este verificată, se contruieşte un modul, definit într-o structură de tip server_module, prin adăugarea extensiei “.so”. Dacă rezultatul este NULL, fie pagina conţinea erori, fie modulul nu a fost gasit. În oricare din aceste cazuri, este generat mesajul de eroare 404, Not Found, apoi este trimis folosind funcţia sctp_sendmsg().

Dacă pagina a fost încarcată cu succes, se generează mesajul de ok_response, apoi se trimite modulul.

Următoarea parte a programului tratează conexiunile dintre server şi client. Pentru aceasta se foloseşte o functie read, care citeşte datele primite de la client. În cazul în care datele au fost citite cu succes, buffer-ul în care au fost stocate datele este eliberat, şi se trece la procesarea mesajelor primite de la client, pentru a le verifica validitatea. Prima linie este cererea HTTP, care trebuie să conţină: metoda, pagina cerută şi versiunea protocolului. Este posibil ca un client să trimită şi date în plus, cum ar fi diferite antete. Server-ul citeşte toate datele până primeşte o linie necompletată, pe care o consideră sfârşit de antet.

Pentru citire, server-ul foloseşte funcţia sctp_recvmsg().

Dacă ultima citire nu a fost efectuată cu succes, programul consideră că este o problemă de conexiune, şi închide asocierea folosind funcţia close().

Dacă până în acest punct toate condiţiile au fost verificate, începe verificarea datelor din prima linie a cererii HTTP. Dacă versiunea sau metoda nu sunt valide, se trimite un mesaj de eroare folosind funcţia sctp_sendmsg(). Dacă acestea sunt valide, se apelează funcţia handle_get().

Până în acest punct a fost partea de definire a funcţiilor. Funcţia principală, server_run(), stabileşte apelarea şi comportarea acestora.

Funcţia începe cu declararea globală a interfeţelor şi structurilor folosite. Apoi iniţializează handler-ul care termină procesele child.

Apoi se deschid socket-urile SCTP. Funcţiile folosite sunt cele specifice SCTP-ului: socket(), sctp_bind_arg_list() care include sctp_bindx(), listen() şi accept().

Funcţiile setsockopt() şi getsockop() setează şi întorc parametrii socket-ului. Acestea sunt folosite pentru a stabili numărul de sream-uri folosite şi pentru a verifica construirea corectă a socket-ului.



După ce conexiunea este terminată, tot în funcţia principală, se generează un mesaj prin care se stabileşte că a fost acceptată o conexiune, şi sunt date datele clientului. Aceasta este facută cu ajutorul funcţiei getpeername().

Ultima parte a funcţiei principale se ocupă cu crearea unui proces child, pentru a trata conexiunile cu un server concurent.

SCTP_CLIENT.C


Acest program este un browser web care funcţionează pe protocolul SCTP. El foloseşte metoda GET pentru a cere date server-ului web.

Partea de început descrie librăriile folosite şi defineşte constantele folosite.

Următoarele doua funcţii, sctpstr_cli() şi sctpstr_cli_multistream() tratează datele primite de la server pe unul sau mai multe stream-uri. Se folosesc funcţiile sctp_sendmsg() şi stp_recvmsg() specifice SCTP-ului:

Funcţia main() deschide socket-ii pentru stabilire unei asocieri SCTP, folosind funcţia socket(). Apoi alocă resurse de memorie pentru datele primite de la server.

MAIN.C


Acest program încarca modulele necesare tratării cererilor HTTP către server.

FILES.C


În prima parte, acest program defineşte o serie de buffere pentru răspunsurile la cererile HTTP.


Directoarele care trebuiesc trimise clientului sunt deschise, apoi parcurse, şi trimise clientului HTTP.


COMPILE_CLIENT


Acest script compliează sursa clientului.


Makefile

Acest script compliează sursa server-ului.


Sctp_client_*_int_*_streams.rrd


Aceste fişiere reprezintă baza de date de tip RRD în care sunt stocate rezultatele testelor.


SCRIPT.SH


Acest script porneşte simultan 4 instanţe de client.




RRD.C


Asigură updatarea bazelor de date RRD.


/etc/init.d/crontab


Acest fişier asigură generarea cererilor HTTP-GET o data la 60 de secunde.

Capitolul 3 – Explicarea testelor


3.1 Rularea testelor


      1. Mediul de testare


Testele au fost efectuate pe două maşini rulând sub Linux Debian 5.0.1,având versiunea compilatorului gcc 2.6.26 şi bibliotecile sctp versiunea 1. Maşinile au fost conectate în reţea print-un comutator (switch) 10/100Mbps.

Pentru demonstrarea multihoming-ului s-au folosit interfaţa fizica eth0 şi 3 subinterfeţe virtuale ale acesteia. Din păcate, acesta se observa numai în pachetele HEARTBEAT şi arp trimise de clienţi, deorece maşinile folosite pentru testare nu dispun decât de o singură interfaţa fizică de reţea.


3.2.1 Cazurile testate


CAZ 1

În primul caz au fost folosite două instanţe simultane ale clientului, funcţionând pe port-uri diferite, folosind un stream de date, pentru o interfaţa şi pentru 4 interfeţe.

Pentru a putea realiza o comparaţie între cele două protocoale, TCP şi SCTP, aplicaţia a fost întâi adaptată cazului TCP, adică protocolul a fost adaptat unui singur stream de date, şi o singură interfaţă de conectare.

Fişierul transferat a fost de aproximativ 30 MB.


CAZ 2


În al doilea caz, s-au folosit tot două instanţe ale clientului, pentru una şi respectiv 4 interfeţe, dar folosind 50 de stream-uri de date. De această dată cei doi clienţi au lucrat separat, pentru a se putea observa performanţele SCTP-ului independent, şi intr-o conexiune concurentă, cum este cazul 1.





Datele au fost prelevate pentru cel puţin 20 minute în fiecare caz, timp în care au fost generate următoarele două evenimente:


Congestionarea reţelei folosind comanda ping cu 100 de pachete pe secundă, de dimensiune 100000 biţi.

Deconectarea cablului, şi reintroducerea sa, urmărind reînceperea conexiunii, datorată pachetelor HEARTBEAT.


Într-o ultimă etapă se va observa modul diferit de închidere a unei asocieri între TCP şi SCTP.


3.1.3 Rezultate


CAZ 1


S-au pornit cele două instanţe ale clientului şi cele două instanţe corespunzatoare ale server-ului pe două porturi diferite.




Grafic 3.1













In dreptul orei 3:45 a avut loc primul eveniment:




Printscreen 3.1


In drepul orei 3:55 a avut loc al doilea eveniment, de cable out. Cât timp cablul este deconectat, putem observa mesajele HEARTBEAT trimise catre lista de adrese pe care clientul multihomed a făcut bind.:








Printscreen 3.2


Apoi, în momentul în care cablul este reconectat, 4:00, sunt trimise pachete SCTP INIT, pentru a se relua conexiunea.




CAZ 2



Se deschid instanţele pentru cei doi clienti şi server-ele corespunzatoare:


Pentru 4 interfeţe rezultatele sunt următoarele:




Grafic 3.2



Cableout/cablein




Printscreen 3.3








Printscreen 3.4







-pentru o interfaţă rezultatele sunt următoarele:






Grafic 3.3


Cableout/cablein



Printscreen 3.5




Printscreen-ul de mai jos prezintă schimbul de date între client şi server. Server-ul trimite pachete SCTP DATA, iar clientul le confirmă cu pachetele SCTP SACK.


Printscreen 3.6

Închiderea asocierii SCTP:




Printscreen 3.





3.2 Analiza rezultatelor



CAZ 1


În primul caz, reprezentat de graficul 3.1, putem observa că cei doi clienţi realizează în condiţii de mediu optime un transfer de 9,5 MBps pentru SCTP si 10MBps pentru TCP. Aceste valori mari se poat explica şi datorită faptului că singurul echipament dintre cele două terminale este un switch, destul de rapid, care permite un astfel de transfer.

Când reţeaua este forţată să răspundă cu 100 de pachete ICMP pe secunda, transferul scade numai în cazul clientului SCTP cu maximum 1MBps, dar pierderile de date sunt nule.

La puţin timp după trimiterea pachetelor ICMP, cablul este scos din switch, şi putem observa în Printscreen 3.1 cum clientul multihomed timite pachete HEARBEAT pentru a detecta momentul în care legatura este restabilită.

Aceste pachete sunt trimise pe toate interfeţele pe care s-a facut bind.

În momentul în care cablul este reintrodus în switch, clientul multihomed detectează restabilirea legăturii şi trimite pachetele SCTP INIT, pentru a redeschide asocierea. Transferul de date este reluat.

Acest caz este comparabil cu TCP. Singura diferentă notabilă se înregistrează în generarea pachetelor HEARTBEAT de către SCTP, dar având în vedere caracterul semi-deschis al unei conexiuni TCP, cele doua protocoale detectează într-un anumit intreval de timp posibilitatea unei reconectări



CAZ 2


4 interfeţe


În momentul 4:20, reţeaua este deja afectata de pachetele ICMP. Observăm că viteza de transfer este afectată, scanzând de la 9.5 MBps pana la 8,5MBps. Apoi cablul este deconectat în momentul 4:25.

În Printscreen 3.3, observăm cum protocolul încearcă să determine adresele MAC ale interfeţelor pe care a facut bind. În momentul în care cablul este reconectat se transmit pachetele ICMP, conexiunea este restabilită, şi se ajunge la un transfer de aproximativ 10MBps.

Diferenţele dintre un stream de date şi 50 nu sunt foarte relevante, perfomanţele fiind foarte asemănătoare în cazul SCTP-ului.


1 interfaţă

Porţiunea de grafic care interesează este între momentul 4:45 si 5:05. Observam o viteză de 8-9MBps.

În momentul 4:45 este dată comanda ping, observând aceeaşi o scădere mai accentuata a traficului, însa fără pierderi de date. În momentul opririi comenzii, viteza de transfer revine la aproximativ 8MBps.



La fel ca în cazurile precedente, când cablul este deconectat, SCTP-ul verifică reluarea legăturii prin pachete SCTP HEARTBEAT. Când cablul este reconectat, conexiunea este reluată imediat, şi se ajunge la aproximativ aceeaşi viteză de transfer: 8-9 MBps.

Inchiderea asocierii SCTP


Se observă că în cazul SCTP-ului, după generarea unui număr de pachete SHUTDOWN SACK, se iniţiază procedura de închidere a unei asocieri, asupra căreia ambele entităţi de reţea acţionează, prin SHUTDOWN ACK şi SHUTDOWN COMPLETION.

Capitolul 4 – Concluzii finale



Aceste teste au demonstrat că pentru un singur stream de date, performanţele SCTP-ului sunt chiar mai scăzute decât în cazul TCP-ului.

Deşi SCTP este un protocol foarte flexibil datorită proprietăţii de multihoming, prezentând astfel un avantaj în faţa TCP-ului, totuşi nu este cu mult mai rapid.

Un avantaj major, care asigură o viteză mai ridicată, este proprietatea de multistreaming.

Deşi este un protocol interesant, cu multe caracteristici deosebite, fiind totuşi nou, şi dezvoltat în timp ce protocolul TCP a fost îmbunătăţit, aceste caracterstici nu se fac simţite în totalitate în aplicaţiile vechi.

Totuşi, diferenţele cu adevărat notabile nu pot fi analizate decât pentru aplicaţiile în timp real, pentru care de altfel acest protocol a fost implementat.






















Anexa A – Tabel de abrevieri



UDP

User Datagram Protocol

TCP

Transmission Control Protocol

SCTP

Stream Control Transmission Protocol

HTTP

Hypertext Transfer Protocol

TSN

Transmission Sequence Number

SSN

stream sequence numbers

SID

Stream ID

CRC

cyclic redundancy check

PPID

Payload process ID

MTU

Maximum Transmission Unit

HOL

Head-of-line

ECN

Explicit Congestion Notification

ECNE

ECN-Echo

ECT

ECN-Capable Trasport

TCP/IP

Transmission Control Protocol/Internet Protocol

RRD

Round Robin Database
























Anexa B – Programe si scripturi folosite



Server.c


#include <arpa/inet.h>

#include <assert.h>

#include <errno.h>

#include <netinet/in.h>

#include <signal.h>

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/wait.h>

#include <unistd.h>

#include <netdb.h>

#include <signal.h>



#include <netinet/sctp.h>


#define ECHO_PORT 2019


#define BUFSIZE 128



#define INSTREAMS 50


#define OUTSTREAMS 50


#include "server.h"


/* HTTP response and header for a successful request. */


static char* ok_response =

"HTTP/1.0 200 OK\n"

"Content-type: text/html\n"

"\n";


/* HTTP response, header, and body indicating that the we didn't

understand the request. */


static char* bad_request_response =

"HTTP/1.0 400 Bad Request\n"

"Content-type: text/html\n"

"\n"

"<html>\n"

" <body>\n"

" <h1>Bad Request</h1>\n"

" <p>This server did not understand your request.</p>\n"

" </body>\n"

"</html>\n";




/* HTTP response, header, and body template indicating that the

requested document was not found. */


static char* not_found_response_template =

"HTTP/1.0 404 Not Found\n"

"Content-type: text/html\n"

"\n"

"<html>\n"

" <body>\n"

" <h1>Not Found</h1>\n"

" <p>The requested URL %s was not found on this server.</p>\n"

" </body>\n"

"</html>\n";


/* HTTP response, header, and body template indicating that the

method was not understood. */


static char* bad_method_response_template =

"HTTP/1.0 501 Method Not Implemented\n"

"Content-type: text/html\n"

"\n"

"<html>\n"

" <body>\n"

" <h1>Method Not Implemented</h1>\n"

" <p>The method %s is not implemented by this server.</p>\n"

" </body>\n"

"</html>\n";







extern struct sctp_sndrcvinfo sri;

extern struct sctp_initmsg initmsg;

extern struct sctp_status status;

struct sctp_event_subscribe evnts;


extern char *addr_ip_list;


int sendmesg(int sock_fd, long int stream, char *msg) {

int sz, msg_flags;

struct sockaddr_in serv_addr;

size_t adrlen;

char readbuf[256];

struct sctp_sndrcvinfo sri;

memset(&sri, 0, sizeof(sri));

memset(readbuf, 0, sizeof(readbuf));

sri.sinfo_stream = stream;

sz = strlen(msg);

sz = sctp_sendmsg (sock_fd, msg, sz, NULL, 0,

0, 0, sri.sinfo_stream, 0, 0);


printf ("sctp_sendmsg:[l:%d, e:%d]\n", sz, errno);



return 0;



}



struct addrinfo *

host_serv(const char *host, const char *serv, int family, int socktype)

{

int n;

struct addrinfo hints, *res;


bzero(&hints, sizeof(struct addrinfo));

hints.ai_flags = AI_ALL; /* always return canonical name */

hints.ai_family = family; /* 0, AF_INET, AF_INET6, etc. */

hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */


if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) {

printf("host_serv error for %s, %s: %s",

(host == NULL) ? "(no hostname)" : host,

(serv == NULL) ? "(no service name)" : serv,

gai_strerror(n));

exit(1);

}


return(res); /* return pointer to first on linked list */

}



int delip(int sock_fd, char *iptext)

{

int err,count;

struct sockaddr* addressArray;

count = sctp_getladdrs(sock_fd, 0, &addressArray);

// printAddressArray(addressArray, count);

// serv_addr.sin_addr.s_addr = ip

err = sctp_bindx(sock_fd, addressArray, 2, SCTP_BINDX_REM_ADDR);

sctp_freepaddrs(addressArray);

count = sctp_getladdrs(sock_fd, 0, &addressArray);

// printAddressArray(addressArray, count);

if (err == 0) {

// printf ("Removed %s:%d as association\n", inet_ntoa(serv_addr.sin_addr), port);

} else {

//printf ("failed to remove association %s:%d [l:%d, e:%d]\n", inet_ntoa(serv

}

if(count > 0) {

sctp_freepaddrs(addressArray); // free allocated memory

}

return 0;

}




int

sctp_bind_arg_list(int sock_fd, char **adr_list, int adr_count)

{



struct addrinfo *addr;

char *bindbuf, *p, portbuf[10];

int addrcnt=0;

int i;

/* Allocate space for bind arguments */

bindbuf = (char *) calloc(adr_count, sizeof(struct sockaddr_storage));

p = bindbuf;

/* Process arguments */

sprintf(portbuf, "%d", ECHO_PORT);

for( i=1; i<adr_count; i++ ) {

addr = host_serv(adr_list[i], portbuf, AF_UNSPEC, SOCK_SEQPACKET);

memcpy(p, addr->ai_addr, addr->ai_addrlen);

freeaddrinfo(addr);

addrcnt++;

p += addr->ai_addrlen;


printf("binding on %s\n",adr_list[i]);

}

/* Call binding function */

sctp_bindx(sock_fd,(struct sockaddr *)bindbuf,addrcnt,SCTP_BINDX_ADD_ADDR);

/* Return success */

free(bindbuf);

return(0);

}


/*

static void sigint_handler (int signo) {


// delip(sock , "10.10.10.13");

printf("blah");

fflush(stdout);

return ;


}

*/


static void clean_up_child_process (int signal_number)

{

int status;

wait (&status);

}


/* Process an HTTP "GET" request for PAGE, and send the results to the

file descriptor CONNECTION_FD. */


static void handle_get (int connection_fd, const char* page,struct sockaddr *client_addr)

{

struct server_module* module = NULL;

int len ;


/* Make sure the requested page begins with a slash and does not

contain any additional slashes -- we don't support any

subdirectories. */

if (*page == '/' && strchr (page + 1, '/') == NULL) {

char module_file_name[64];




/* The page name looks OK. Construct the module name by appending

".so" to the page name. */

snprintf (module_file_name, sizeof (module_file_name),

"%s.so", page + 1);

/* Try to open the module. */

module = module_open (module_file_name);

}


if (module == NULL) {

/* Either the requested page was malformed, or we couldn't open a

module with the indicated name. Either way, return the HTTP

response 404, Not Found. */

char response[1024];


/* Generate the response message. */

snprintf (response, sizeof (response), not_found_response_template, page);

/* Send it to the client. */

len = sizeof(client_addr);

sctp_sendmsg(connection_fd, response, BUFSIZE,(struct sockaddr *)&client_addr,

len,

sri.sinfo_ppid,

sri.sinfo_flags,

sri.sinfo_stream,

0, 0);

} else {

/* The requested module was loaded successfully. */


/* Send the HTTP response indicating success, and the HTTP header

for an HTML page. */

sctp_sendmsg(connection_fd, ok_response, BUFSIZE,(struct sockaddr *)&client_addr,

len,

sri.sinfo_ppid,

sri.sinfo_flags,

sri.sinfo_stream,

0, 0);


/* Invoke the module, which will generate HTML output and send it

to the client file descriptor. */

(*module->generate_function) (connection_fd);

/* We're done with the module. */

module_close (module);

}

}


/* Handle a client connection on the file descriptor CONNECTION_FD. */


static void handle_connection (int connection_fd,struct sockaddr *client_addr)

{

char buffer[256];

ssize_t bytes_read;

int msg_flags ;



int len ;

// signal(SIGINT,sigint_handler);

/* Read some data from the client. */

len = sizeof(client_addr);

bytes_read = sctp_recvmsg(connection_fd, buffer, sizeof(buffer),

(struct sockaddr *)&client_addr, &len, &sri,&msg_flags);

printf("received buffer: %s\n",buffer);

if (bytes_read > 0) {

char method[sizeof (buffer)];

char url[sizeof (buffer)];

char protocol[sizeof (buffer)];


/* Some data was read successfully. NUL-terminate the buffer so

we can use string operations on it. */

buffer[bytes_read] = '\0';

/* The first line the client sends is the HTTP request, which is

composed of a method, the requested page, and the protocol

version. */

sscanf (buffer, "%s %s %s", method, url, protocol);

/* The client may send various header information following the

request. For this HTTP implementation, we don't care about it.

However, we need to read any data the client tries to send. Keep

on reading data until we get to the end of the header, which is

delimited by a blank line. HTTP specifies CR/LF as the line

delimiter. */

printf("method: %s\n",method);

printf("url: %s\n",url);

printf("protocol: %s\n",protocol);

bytes_read = sctp_recvmsg(connection_fd, buffer, sizeof(buffer),

(struct sockaddr *)&client_addr, &len, &sri,&msg_flags);

printf("user agent: %s\n",buffer);

/* Make sure the last read didn't fail. If it did, there's a

problem with the connection, so give up. */

if (bytes_read == -1) {

close (connection_fd);

return;

}

/* Check the protocol field. We understand HTTP versions 1.0 and

1.1. */

if (strcmp (protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) {

/* We don't understand this protocol. Report a bad response. */

printf("we dont understand the protocol\n");



sctp_sendmsg(connection_fd,bad_request_response, strlen (bad_request_response) ,(struct sockaddr *)&client_addr,

len,

sri.sinfo_ppid,

sri.sinfo_flags,

sri.sinfo_stream,

0, 0);

}

else if (strcmp (method, "GET")) {

/* This server only implements the GET method. The client

specified some other method, so report the failure. */

char response[1024];


printf("got method GET\n");

snprintf (response, sizeof (response),bad_method_response_template, method);

sctp_sendmsg(connection_fd, response, strlen (response) ,(struct sockaddr *)&client_addr,

len,

sri.sinfo_ppid,

sri.sinfo_flags,

sri.sinfo_stream,

0, 0);

}

else

/* A valid request. Process it. */

handle_get (connection_fd, url,(struct sockaddr *)&client_addr);

}

else if (bytes_read == 0)

/* The client closed the connection before sending any data.

Nothing to do. */

;

else

/* The call to read failed. */

system_error ("read");

}





void server_run (char* const argv[],int nr_ips)

{

struct sockaddr_in socket_address;

int rval;

struct sigaction sigchld_action;

int server_socket;

int len ;


/* Install a handler for SIGCHLD that cleans up child processes that

have terminated. */

memset (&sigchld_action, 0, sizeof (sigchld_action));

sigchld_action.sa_handler = &clean_up_child_process;



sigaction (SIGCHLD, &sigchld_action, NULL);


/* Create a TCP socket. */

server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);

if (server_socket == -1)

system_error ("socket");


if(sctp_bind_arg_list(server_socket, argv, nr_ips))

perror("Can't bind the address set");


memset(&initmsg, 0, sizeof(initmsg));

initmsg.sinit_max_instreams = INSTREAMS;

initmsg.sinit_num_ostreams = OUTSTREAMS;

printf("Asking for: input streams: %d, output streams: %d\n",

initmsg.sinit_max_instreams,

initmsg.sinit_num_ostreams);



if (setsockopt(server_socket, IPPROTO_SCTP,SCTP_INITMSG, &initmsg, sizeof(initmsg))) {

perror("set sock opt\n");

}

rval = listen (server_socket, 10);

if (rval != 0)

perror ("listen");


/* Loop forever, handling connections. */

while (1) {

struct sockaddr_in remote_address;

socklen_t address_length;

int connection;

pid_t child_pid;


/* Accept a connection. This call blocks until a connection is

ready. */

address_length = sizeof (remote_address);

connection = accept (server_socket, (struct sockaddr *)&remote_address, &address_length);

if (connection == -1) {

/* The call to accept failed. */

if (errno == EINTR)

/* The call was interrupted by a signal. Try again. */

continue;

else



/* Something else went wrong. */

system_error ("accept");

}

memset(&status, 0, sizeof(status));

len = sizeof(status);

if (getsockopt(connection, IPPROTO_SCTP,

SCTP_STATUS, &status, &len) == -1) {

perror("get sock opt");

}

printf("Got: input streams: %d, output streams: %d\n",

status.sstat_instrms,

status.sstat_outstrms);


/* We have a connection. Print a message if we're running in

verbose mode. */

if (verbose) {

socklen_t address_length;


/* Get the remote address of the connection. */

address_length = sizeof (socket_address);

rval = getpeername (connection, (struct sockaddr *)&socket_address, &address_length);

assert (rval == 0);

/* Print a message. */

printf ("connection accepted from %s\n",

inet_ntoa (socket_address.sin_addr));

}


/* Fork a child process to handle the connection. */

child_pid = fork ();

if (child_pid == 0) {

/* This is the child process. It shouldn't use stdin or stdout,

so close them. */

close (STDIN_FILENO);

/* Also this child process shouldn't do anything with the

listening socket. */

close (server_socket);

bzero(&evnts, sizeof(evnts));

evnts.sctp_data_io_event = 1;


if (setsockopt(connection,IPPROTO_SCTP, SCTP_EVENTS,

&evnts, sizeof(evnts)) ) {

perror("set sock opt\n");

}

/* Handle a request from the connection. We have our own copy

of the connected socket descriptor. */

handle_connection (connection,(struct sockaddr *) &remote_address);

/* All done; close the connection socket, and end the child

process. */

close (connection);

exit (0);



}

else if (child_pid > 0) {

/* This is the parent process. The child process handles the

connection, so we don't need our copy of the connected socket

descriptor. Close it. Then continue with the loop and

accept another connection. */

close (connection);

}

else

/* Call to fork failed. */

system_error ("fork");

}

}



Client.c


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <dirent.h>


#include <netinet/sctp.h>


#include "rrd.c"


#define SERV_PORT 2016

#define SERV_MAX_SCTP_STRM 100

#define MAXLINE 800

#define FILE_NAME_MAX 255



#define landing_path "/home/dr/files_received/"



// http request

static char* http_request = "GET /files HTTP/1.0\r\n\r\n" ;

// dummy user_agent

static char* user_agent =

"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.7) Gecko/2009032803 Iceweasel/3.0.6 (Debian-3.0.6-1)";


//int serv_port ;


struct addrinfo *

host_serv(const char *host, const char *serv, int family, int socktype)

{

int n;

struct addrinfo hints, *res;




bzero(&hints, sizeof(struct addrinfo));

hints.ai_flags = AI_ALL; /* always return canonical name */

hints.ai_family = family; /* 0, AF_INET, AF_INET6, etc. */

hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */


if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) {

printf("host_serv error for %s, %s: %s",

(host == NULL) ? "(no hostname)" : host,

(serv == NULL) ? "(no service name)" : serv,

gai_strerror(n));

exit(1);

}


return(res); /* return pointer to first on linked list */

}



void printAddressArray(struct sockaddr* addressArray, int len) {

int i, error;

struct sockaddr *address;

char host[100];

char port[10];

for(i = 0;i < len; i++) {

printf("%d/%d: ",i + 1, len);

memset(host, 0, sizeof(host));

memset(port, 0, sizeof(port));

address = &addressArray[i];

error = getnameinfo(address, sizeof(address),

host, sizeof(host),

port, sizeof(port),

NI_NUMERICHOST);

if (error != 0) {

fprintf(stderr, "getnameinfo error\n");

printf("(unknown address)\n");

} else {

printf("%s:%s\n", host, port);

}

}

}



int listip(int sock_fd) {

struct sockaddr* addressArray;

int count;

count = sctp_getladdrs(sock_fd, 0, &addressArray);

if(count > 0) {

printf("SCTP association local addresses:\n");

printAddressArray(addressArray, count);

sctp_freepaddrs(addressArray); // free allocated memory

}

count = sctp_getpaddrs(sock_fd, 0, &addressArray);

if(count > 0) {

printf("SCTP association remote addresses:\n");

printAddressArray(addressArray, count);

sctp_freepaddrs(addressArray); // free allocated memory



}

return 0;

}




int

sctp_connect_arg_list(int sock_fd, char **adr_list, int adr_count)

{

struct addrinfo *addr;

char *bindbuf, *p, portbuf[10];

int addrcnt=0;

int i,ret;

/* Allocate space for sctp_connectx arguments */

bindbuf = (char *) calloc(adr_count, sizeof(struct sockaddr_storage));

p = bindbuf;

/* Process arguments */

sprintf(portbuf, "%d", SERV_PORT);

for( i=3; i<adr_count; i++ ) {

addr = host_serv(adr_list[i], portbuf, AF_UNSPEC, SOCK_SEQPACKET);

memcpy(p, addr->ai_addr, addr->ai_addrlen);

freeaddrinfo(addr);

addrcnt++;

p += addr->ai_addrlen;

}

/* Call binding function */

ret = sctp_connectx(sock_fd,(struct sockaddr *)bindbuf,addrcnt,NULL);

if (ret < 0 ) {

perror("sctp_connectx");

}


/* Return success */

free(bindbuf);

return(0);

}






int main(int argc, char *argv[]) {

int sockfd;

int len , tlen , ret;

struct sockaddr_in serv_addr;

int port = SERV_PORT;

struct sctp_initmsg initmsg;

struct sctp_status status;

struct sockaddr_in peeraddr;

struct sctp_sndrcvinfo sri;

struct sctp_event_subscribe evnts;

struct timeval tv1 , tv2 ;

char *req , *ptr , *temp_ptr ;

char method[]="GET ";

char path[]="/files ";

char protocol[]="HTTP/1.0" , *proto;

char sendline[MAXLINE], recvline[MAXLINE];



char file_name[FILE_NAME_MAX] , file_size_buf[16];

int out_sz,rd_sz , n , written , useconds ;

int msg_flags;

FILE *destination_file ;

DIR *run_dir ;

int file_size=0 , count=0 , streams;

char *buffer , *sbuf;

socklen_t slen , tolen ;

struct dirent *dirp ;

if (argc < 2 ) {

printf("arguments: <tcp/sctp> <nr_streams> <ip list>\n");

exit(1);

}


if (argv[2]!=NULL){

streams = atoi(argv[2]);

}



if (argv[1]!=NULL){

proto = strdup(argv[1]);

printf("protocol: %s\n",proto);

}

/*

if (argv[3]!=NULL){

serv_port = atoi(argv[3]);

}

*/



/* create endpoint */

if (!strcmp(proto,"sctp")){

sockfd = socket(AF_INET, SOCK_STREAM,

IPPROTO_SCTP

);

} else {

sockfd = socket(AF_INET, SOCK_STREAM,

IPPROTO_TCP

);

}

if (sockfd < 0) {

perror("socket creation");

exit(2);



}

/* connect to server */

serv_addr.sin_family = AF_INET;

serv_addr.sin_addr.s_addr = inet_addr(argv[3]);

serv_addr.sin_port = htons(port);

// serv_addr.sin_port = htonl(serv_port);


memset(&initmsg, 0, sizeof(initmsg));

initmsg.sinit_max_instreams = streams;

initmsg.sinit_num_ostreams = streams;

printf("Asking for: input streams: %d, output streams: %d\n",

initmsg.sinit_max_instreams,

initmsg.sinit_num_ostreams);


if (!strcmp(proto,"sctp")){

if (setsockopt(sockfd, IPPROTO_SCTP,

SCTP_INITMSG, &initmsg, sizeof(initmsg))) {

perror("set sock opt\n");

}


if (sctp_connect_arg_list(sockfd, argv , argc-3 ) < 0) {

perror("connectx");

exit(3);

}

} else {

connect(sockfd,(struct sockaddr *) &serv_addr , sizeof(serv_addr) );

}

run_dir = opendir(".");

dirp = readdir (run_dir);

len = sizeof(status);

memset(&status, 0, len);

if (!strcmp(proto,"sctp" )){

printf("getsockopt\n");

if (getsockopt(sockfd, IPPROTO_SCTP,

SCTP_STATUS, &status, &len) == -1) {

perror("get sock opt");

}

printf("Got: input streams: %d, output streams: %d\n",

status.sstat_instrms,

status.sstat_outstrms);




bzero(&evnts, sizeof(evnts));

evnts.sctp_data_io_event = 1;


if (setsockopt(sockfd,IPPROTO_SCTP, SCTP_EVENTS,

&evnts, sizeof(evnts)) ) {

perror("set sock opt\n");

}

} else {

}

// listip(sockfd);

req=malloc(128);

memset(req,0,128);

ptr = req ;

memcpy(req,method,strlen(method));

req = req + strlen(method) ;

memcpy(req,path,strlen(path));

req= req+strlen(path) ;

memcpy(req,protocol,strlen(protocol));

req= req+strlen(protocol) ;

memcpy(req,"\r\n\r\n",4);

tolen = sizeof(serv_addr);

if (!strcmp(proto,"sctp")) {

printf("sending request\n");

sri.sinfo_stream = 1 ;

out_sz = strlen(http_request);

if (sctp_sendmsg(sockfd, http_request , out_sz, (struct sockaddr *) &serv_addr, tolen,

0, 0,

sri.sinfo_stream,

0, 0) < 0 ) {

perror("sctp_sendmsg");

exit(1);

}

out_sz = strlen(user_agent);

if (sctp_sendmsg(sockfd, user_agent , out_sz, (struct sockaddr *) &serv_addr, tolen,

0, 0,



sri.sinfo_stream,

0, 0) < 0 ) {

perror("sctp_sendmsg");

exit(1);

}

} else {

// write

}


printf("starting file transfer\n");

fflush(stdout);

gettimeofday(&tv1,NULL);

/*

if (!strcmp(proto,"sctp")) {

while(1){

len = sizeof(serv_addr);

rd_sz = sctp_recvmsg(sockfd, recvline, sizeof(recvline),

(struct sockaddr *)&serv_addr, &len,

&sri,&msg_flags);

if (rd_sz < 0 ) {

perror("-") ;

}else {

break ;

}

}

} else {

// read

}

*/

while(1) {

memset(recvline,0,MAXLINE);

memset(file_name,0,FILE_NAME_MAX);

if (!strcmp(proto,"sctp")) {

len = sizeof(serv_addr);

rd_sz = sctp_recvmsg(sockfd, recvline, sizeof(recvline),

(struct sockaddr *)&serv_addr, &len,

&sri,&msg_flags);

if (rd_sz < 1 ) {

break ;



}

} else {

printf("tcp here");

// read

}

printf("receiving file : %s\n",recvline);

fflush(stdout);

if ((recvline[0] == 'f' ) && (recvline[1] == 't' ) ) {

// printf("file transfer header: %s\n",recvline);

ptr = strchr(recvline+2,'|');

len = ptr - recvline - 2 ;

temp_ptr = strchr(ptr+1,'|');

len = temp_ptr - ptr ;

memcpy(file_name,ptr+1,len-1);

temp_ptr = strchr(ptr+3,'|');

len = strlen(recvline);

tlen = temp_ptr - ptr;


memcpy(file_size_buf,temp_ptr+1,len-tlen);


printf("file_name: %s\n",file_name);

printf("file_size: %s\n",file_size_buf);

file_size= atoi(file_size_buf);

}

fflush(stdout);

chdir(landing_path);

if (file_name!=NULL) {

destination_file=fopen(file_name,"w");

} else {

printf("filename ?");

}

if (!strcmp(proto,"sctp")) {

while (

(rd_sz = sctp_recvmsg(sockfd, recvline, sizeof(recvline),

(struct sockaddr *)&serv_addr, &len,

&sri,&msg_flags)) > 0 ) {

if (strstr( recvline , "-EOF-" )){


break ;

}



count = count + rd_sz ;

if ( written = fwrite(recvline , rd_sz , 1 , destination_file) < 0 ) {

perror("fwrite error");

}

// printf("written %d bytes\n",rd_sz);

}

} else {

printf("tcp here\n");

// read


}

if (destination_file !=NULL) {

fclose(destination_file);

} else {

printf("invalid destination file ");

exit(1);

}

memset(file_name,0,FILE_NAME_MAX);

memset(file_size_buf,0,16);



} //while

gettimeofday(&tv2,NULL);

close(sockfd);


useconds = tv2.tv_sec - tv1.tv_sec ;

useconds *= 1000000;

useconds += tv2.tv_usec - tv1.tv_usec ;


// printf("received %4.2lf bytes in %2.d seconds\n",(float)count/1024, seconds );


printf("received %d bytes in %d microseconds\n",count, useconds );


sbuf = malloc(128);

memset(sbuf,0,128);

chdir(getenv("PWD"));

if (count!=0) {

if ((argc < 5) && (streams==2) ) {


sprintf(sbuf,sctp_test_1_int_1_stream_rrd,count,useconds);

printf("%s\n",sbuf);

ret = system(sbuf);

if (ret < 0 ) {

perror("system");

}

exit(0);

} else if ((streams == 2 )) {

sprintf(sbuf,sctp_test_4_int_1_stream_rrd,count,useconds);

printf("%s\n",sbuf);

ret = system(sbuf);


if (ret < 0 ) {

perror("system");

}

exit(0);

}else if ((argc < 3) && (streams==50)) {

sprintf(sbuf,sctp_test_1_int_50_stream_rrd,count,useconds);

printf("%s\n",sbuf);

ret = system(sbuf);

if (ret < 0 ) {

perror("system");

}

exit(0);


} else if ((streams==50)) {

sprintf(sbuf,sctp_test_4_int_50_stream_rrd,count,useconds);

printf("%s\n",sbuf);

ret = system(sbuf);


if (ret < 0 ) {

perror("system");

}

exit(0);

}




}

fflush(stdout);

exit(0);

}



RRD.C


#define path_to_rrd "/home/dr/sctp_proj/rrd"



#define sctp_test_4_int_1_stream_rrd \

"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_4_int_1_stream.rrd \

N:%d:%d"


#define sctp_test_1_int_1_stream_rrd \

"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_1_int_1_stream.rrd \

N:%d:%d"



#define sctp_test_4_int_50_stream_rrd \

"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_4_int_50_stream.rrd \

N:%d:%d"


#define sctp_test_1_int_50_stream_rrd \

"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_1_int_50_stream.rrd \

N:%d:%d"


#define sctp_test_tcp_rrd \

"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_tcp.rrd \

N:%d:%d"



Sctp_test_create_rrd


#!/bin/bash

#


rrdtool create sctp_test_4_int_1_stream.rrd --step 60 \

DS:count_4_int:GAUGE:1800:0:U \

DS:time_4_int:GAUGE:1800:0:U \

RRA:AVERAGE:0.5:1:1440 \

RRA:AVERAGE:0.5:10:1008 \

RRA:AVERAGE:0.5:48:2520 \

RRA:MAX:0.5:1:1440 \

RRA:MAX:0.5:10:1008 \

RRA:MAX:0.5:48:2520


rrdtool create sctp_test_1_int_1_stream.rrd --step 60 \

DS:count_1_int:GAUGE:1800:0:U \

DS:time_1_int:GAUGE:1800:0:U \

RRA:AVERAGE:0.5:1:1440 \

RRA:AVERAGE:0.5:10:1008 \



RRA:AVERAGE:0.5:48:2520 \

RRA:MAX:0.5:1:1440 \

RRA:MAX:0.5:10:1008 \

RRA:MAX:0.5:48:2520



rrdtool create sctp_test_4_int_50_stream.rrd --step 60 \

DS:count_4_int:GAUGE:1800:0:U \

DS:time_4_int:GAUGE:1800:0:U \

RRA:AVERAGE:0.5:1:1440 \

RRA:AVERAGE:0.5:10:1008 \

RRA:AVERAGE:0.5:48:2520 \

RRA:MAX:0.5:1:1440 \

RRA:MAX:0.5:10:1008 \

RRA:MAX:0.5:48:2520


rrdtool create sctp_test_1_int_50_stream.rrd --step 60 \

DS:count_1_int:GAUGE:1800:0:U \

DS:time_1_int:GAUGE:1800:0:U \

RRA:AVERAGE:0.5:1:1440 \

RRA:AVERAGE:0.5:10:1008 \

RRA:AVERAGE:0.5:48:2520 \

RRA:MAX:0.5:1:1440 \

RRA:MAX:0.5:10:1008 \

RRA:MAX:0.5:48:2520


rrdtool create tcp_test.rrd --step 60 \

DS:count_tcp:GAUGE:1800:0:U \

DS:time_tcp:GAUGE:1800:0:U \

RRA:AVERAGE:0.5:1:1440 \

RRA:AVERAGE:0.5:10:1008 \

RRA:AVERAGE:0.5:48:2520 \

RRA:MAX:0.5:1:1440 \

RRA:MAX:0.5:10:1008 \

RRA:MAX:0.5:48:2520



Compile_client


gcc -ggdb -o sctp_client02 sctp_client02.c -lsctp


Makefile


# C source files for the server.

SOURCES = server.c module.c common.c main.c

# Object files corresponding to source files.

OBJECTS = $(SOURCES:.c=.o)

# Server module shared library files.

MODULES = files.so


CLIENT = sctp_client.c

### Rules. ############################################################


.PHONY: all clean


# Default target: build everything.

all: server sctp_client $(MODULES)


# Clean up build products.

clean:

rm -f $(OBJECTS) $(MODULES) server


# The main server program. Link with -Wl,-export-dyanamic so

# dynamically loaded modules can bind symbols in the program. Link in

# libdl, which contains calls for dynamic loading.

server: $(OBJECTS)

$(CC) $(CFLAGS) -ggdb -Wl,-export-dynamic -o $@ $^ -ldl -lsctp


client: $(OBJECTS)

$(CC) $(CFLAGS) -ggdb -Wl,-export-dynamic -o $@ $^ -lsctp



# All object files in the server depend on server.h. But use the

# default rule for building object files from source files.

$(OBJECTS): server.h


# Rule for building module shared libraries from the corresponding

# source files. Compile -fPIC and generate a shared object file.

$(MODULES): \

%.so: %.c server.h

$(CC) $(CFLAGS) -fPIC -shared -o $@ $<

Script.sh


#!/bin/bash

my_pwd="/home/dr/sctp_proj/4clienti/"

#$my_pwd/sctp_client_1_int_1_stream sctp 2 10.10.10.10

#$my_pwd/sctp_client_4_int_1_stream sctp 2 10.10.10.10 10.10.10.11 10.10.10.12 10.10.10.13

$my_pwd/sctp_client_1_int_50_stream sctp 50 10.10.10.10

$my_pwd/sctp_client_4_int_50_stream sctp 50 10.10.10.10 10.10.10.11 10.10.10.11 10.10.10.12 10.10.10.13







Bibliografie



[1] Henrik ¨ Osterdahl – “A comparison of TCP and SCTP performance

using the HTTP protocol”

[2] Matthew Stafford – “Signaling and switching for packet telephony”

[3] SCTP Programmer's Guide: HP-UX 11i v2, HP-UX 11i v3

[4] http://www.linuxjournal.com/article/9749

[5] http://www.ietf.org/rfc/rfc2960.txt

[6] W. Richard Stevens – “UNIX Network Programming: Networking APIs: Sockets and XTI; Volume 1”

[7] Taarik Hassenmahomed – “Stream Control Transmission Protocol”

[8] Linux ManPages

[9] Brian W. Kernighan, Dennis M. Ritchie – “The C Programming Language”

[10] http://www.ietf.org/rfc/rfc0768.txt

[11] http://www.faqs.org/rfcs/rfc793.html



1